fraction.mjs 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. 'use strict';
  2. /**
  3. *
  4. * This class offers the possibility to calculate fractions.
  5. * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
  6. *
  7. * Array/Object form
  8. * [ 0 => <numerator>, 1 => <denominator> ]
  9. * { n => <numerator>, d => <denominator> }
  10. *
  11. * Integer form
  12. * - Single integer value as BigInt or Number
  13. *
  14. * Double form
  15. * - Single double value as Number
  16. *
  17. * String form
  18. * 123.456 - a simple double
  19. * 123/456 - a string fraction
  20. * 123.'456' - a double with repeating decimal places
  21. * 123.(456) - synonym
  22. * 123.45'6' - a double with repeating last place
  23. * 123.45(6) - synonym
  24. *
  25. * Example:
  26. * let f = new Fraction("9.4'31'");
  27. * f.mul([-4, 3]).div(4.9);
  28. *
  29. */
  30. // Set Identity function to downgrade BigInt to Number if needed
  31. if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
  32. const C_ZERO = BigInt(0);
  33. const C_ONE = BigInt(1);
  34. const C_TWO = BigInt(2);
  35. const C_THREE = BigInt(3);
  36. const C_FIVE = BigInt(5);
  37. const C_TEN = BigInt(10);
  38. const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
  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. const MAX_CYCLE_LEN = 2000;
  43. // Parsed data to avoid calling "new" all the time
  44. const P = {
  45. "s": C_ONE,
  46. "n": C_ZERO,
  47. "d": C_ONE
  48. };
  49. function assign(n, s) {
  50. try {
  51. n = BigInt(n);
  52. } catch (e) {
  53. throw InvalidParameter();
  54. }
  55. return n * s;
  56. }
  57. function ifloor(x) {
  58. return typeof x === 'bigint' ? x : Math.floor(x);
  59. }
  60. // Creates a new Fraction internally without the need of the bulky constructor
  61. function newFraction(n, d) {
  62. if (d === C_ZERO) {
  63. throw DivisionByZero();
  64. }
  65. const f = Object.create(Fraction.prototype);
  66. f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
  67. n = n < C_ZERO ? -n : n;
  68. const a = gcd(n, d);
  69. f["n"] = n / a;
  70. f["d"] = d / a;
  71. return f;
  72. }
  73. const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
  74. function factorize(n) {
  75. const factors = Object.create(null);
  76. if (n <= C_ONE) {
  77. factors[n] = C_ONE;
  78. return factors;
  79. }
  80. const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
  81. while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
  82. while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
  83. while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
  84. // 30-wheel trial division: test only residues coprime to 2*3*5
  85. // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
  86. for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
  87. while (n % p === C_ZERO) { add(p); n /= p; }
  88. p += FACTORSTEPS[si];
  89. si = (si + 1) & 7; // fast modulo 8
  90. }
  91. if (n > C_ONE) add(n);
  92. return factors;
  93. }
  94. const parse = function (p1, p2) {
  95. let n = C_ZERO, d = C_ONE, s = C_ONE;
  96. if (p1 === undefined || p1 === null) { // No argument
  97. /* void */
  98. } else if (p2 !== undefined) { // Two arguments
  99. if (typeof p1 === "bigint") {
  100. n = p1;
  101. } else if (isNaN(p1)) {
  102. throw InvalidParameter();
  103. } else if (p1 % 1 !== 0) {
  104. throw NonIntegerParameter();
  105. } else {
  106. n = BigInt(p1);
  107. }
  108. if (typeof p2 === "bigint") {
  109. d = p2;
  110. } else if (isNaN(p2)) {
  111. throw InvalidParameter();
  112. } else if (p2 % 1 !== 0) {
  113. throw NonIntegerParameter();
  114. } else {
  115. d = BigInt(p2);
  116. }
  117. s = n * d;
  118. } else if (typeof p1 === "object") {
  119. if ("d" in p1 && "n" in p1) {
  120. n = BigInt(p1["n"]);
  121. d = BigInt(p1["d"]);
  122. if ("s" in p1)
  123. n *= BigInt(p1["s"]);
  124. } else if (0 in p1) {
  125. n = BigInt(p1[0]);
  126. if (1 in p1)
  127. d = BigInt(p1[1]);
  128. } else if (typeof p1 === "bigint") {
  129. n = p1;
  130. } else {
  131. throw InvalidParameter();
  132. }
  133. s = n * d;
  134. } else if (typeof p1 === "number") {
  135. if (isNaN(p1)) {
  136. throw InvalidParameter();
  137. }
  138. if (p1 < 0) {
  139. s = -C_ONE;
  140. p1 = -p1;
  141. }
  142. if (p1 % 1 === 0) {
  143. n = BigInt(p1);
  144. } else {
  145. let z = 1;
  146. let A = 0, B = 1;
  147. let C = 1, D = 1;
  148. let N = 10000000;
  149. if (p1 >= 1) {
  150. z = 10 ** Math.floor(1 + Math.log10(p1));
  151. p1 /= z;
  152. }
  153. // Using Farey Sequences
  154. while (B <= N && D <= N) {
  155. let M = (A + C) / (B + D);
  156. if (p1 === M) {
  157. if (B + D <= N) {
  158. n = A + C;
  159. d = B + D;
  160. } else if (D > B) {
  161. n = C;
  162. d = D;
  163. } else {
  164. n = A;
  165. d = B;
  166. }
  167. break;
  168. } else {
  169. if (p1 > M) {
  170. A += C;
  171. B += D;
  172. } else {
  173. C += A;
  174. D += B;
  175. }
  176. if (B > N) {
  177. n = C;
  178. d = D;
  179. } else {
  180. n = A;
  181. d = B;
  182. }
  183. }
  184. }
  185. n = BigInt(n) * BigInt(z);
  186. d = BigInt(d);
  187. }
  188. } else if (typeof p1 === "string") {
  189. let ndx = 0;
  190. let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
  191. let match = p1.replace(/_/g, '').match(/\d+|./g);
  192. if (match === null)
  193. throw InvalidParameter();
  194. if (match[ndx] === '-') {// Check for minus sign at the beginning
  195. s = -C_ONE;
  196. ndx++;
  197. } else if (match[ndx] === '+') {// Check for plus sign at the beginning
  198. ndx++;
  199. }
  200. if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
  201. w = assign(match[ndx++], s);
  202. } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
  203. if (match[ndx] !== '.') { // Handle 0.5 and .5
  204. v = assign(match[ndx++], s);
  205. }
  206. ndx++;
  207. // Check for decimal places
  208. if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
  209. w = assign(match[ndx], s);
  210. y = C_TEN ** BigInt(match[ndx].length);
  211. ndx++;
  212. }
  213. // Check for repeating places
  214. if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
  215. x = assign(match[ndx + 1], s);
  216. z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
  217. ndx += 3;
  218. }
  219. } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
  220. w = assign(match[ndx], s);
  221. y = assign(match[ndx + 2], C_ONE);
  222. ndx += 3;
  223. } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
  224. v = assign(match[ndx], s);
  225. w = assign(match[ndx + 2], s);
  226. y = assign(match[ndx + 4], C_ONE);
  227. ndx += 5;
  228. }
  229. if (match.length <= ndx) { // Check for more tokens on the stack
  230. d = y * z;
  231. s = /* void */
  232. n = x + d * v + z * w;
  233. } else {
  234. throw InvalidParameter();
  235. }
  236. } else if (typeof p1 === "bigint") {
  237. n = p1;
  238. s = p1;
  239. d = C_ONE;
  240. } else {
  241. throw InvalidParameter();
  242. }
  243. if (d === C_ZERO) {
  244. throw DivisionByZero();
  245. }
  246. P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
  247. P["n"] = n < C_ZERO ? -n : n;
  248. P["d"] = d < C_ZERO ? -d : d;
  249. };
  250. function modpow(b, e, m) {
  251. let r = C_ONE;
  252. for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
  253. if (e & C_ONE) {
  254. r = (r * b) % m;
  255. }
  256. }
  257. return r;
  258. }
  259. function cycleLen(n, d) {
  260. for (; d % C_TWO === C_ZERO;
  261. d /= C_TWO) {
  262. }
  263. for (; d % C_FIVE === C_ZERO;
  264. d /= C_FIVE) {
  265. }
  266. if (d === C_ONE) // Catch non-cyclic numbers
  267. return C_ZERO;
  268. // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
  269. // 10^(d-1) % d == 1
  270. // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
  271. // as we want to translate the numbers to strings.
  272. let rem = C_TEN % d;
  273. let t = 1;
  274. for (; rem !== C_ONE; t++) {
  275. rem = rem * C_TEN % d;
  276. if (t > MAX_CYCLE_LEN)
  277. return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
  278. }
  279. return BigInt(t);
  280. }
  281. function cycleStart(n, d, len) {
  282. let rem1 = C_ONE;
  283. let rem2 = modpow(C_TEN, len, d);
  284. for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
  285. // Solve 10^s == 10^(s+t) (mod d)
  286. if (rem1 === rem2)
  287. return BigInt(t);
  288. rem1 = rem1 * C_TEN % d;
  289. rem2 = rem2 * C_TEN % d;
  290. }
  291. return 0;
  292. }
  293. function gcd(a, b) {
  294. if (!a)
  295. return b;
  296. if (!b)
  297. return a;
  298. while (1) {
  299. a %= b;
  300. if (!a)
  301. return b;
  302. b %= a;
  303. if (!b)
  304. return a;
  305. }
  306. }
  307. /**
  308. * Module constructor
  309. *
  310. * @constructor
  311. * @param {number|Fraction=} a
  312. * @param {number=} b
  313. */
  314. function Fraction(a, b) {
  315. parse(a, b);
  316. if (this instanceof Fraction) {
  317. a = gcd(P["d"], P["n"]); // Abuse a
  318. this["s"] = P["s"];
  319. this["n"] = P["n"] / a;
  320. this["d"] = P["d"] / a;
  321. } else {
  322. return newFraction(P['s'] * P['n'], P['d']);
  323. }
  324. }
  325. const DivisionByZero = function () { return new Error("Division by Zero"); };
  326. const InvalidParameter = function () { return new Error("Invalid argument"); };
  327. const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
  328. Fraction.prototype = {
  329. "s": C_ONE,
  330. "n": C_ZERO,
  331. "d": C_ONE,
  332. /**
  333. * Calculates the absolute value
  334. *
  335. * Ex: new Fraction(-4).abs() => 4
  336. **/
  337. "abs": function () {
  338. return newFraction(this["n"], this["d"]);
  339. },
  340. /**
  341. * Inverts the sign of the current fraction
  342. *
  343. * Ex: new Fraction(-4).neg() => 4
  344. **/
  345. "neg": function () {
  346. return newFraction(-this["s"] * this["n"], this["d"]);
  347. },
  348. /**
  349. * Adds two rational numbers
  350. *
  351. * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
  352. **/
  353. "add": function (a, b) {
  354. parse(a, b);
  355. return newFraction(
  356. this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
  357. this["d"] * P["d"]
  358. );
  359. },
  360. /**
  361. * Subtracts two rational numbers
  362. *
  363. * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
  364. **/
  365. "sub": function (a, b) {
  366. parse(a, b);
  367. return newFraction(
  368. this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
  369. this["d"] * P["d"]
  370. );
  371. },
  372. /**
  373. * Multiplies two rational numbers
  374. *
  375. * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
  376. **/
  377. "mul": function (a, b) {
  378. parse(a, b);
  379. return newFraction(
  380. this["s"] * P["s"] * this["n"] * P["n"],
  381. this["d"] * P["d"]
  382. );
  383. },
  384. /**
  385. * Divides two rational numbers
  386. *
  387. * Ex: new Fraction("-17.(345)").inverse().div(3)
  388. **/
  389. "div": function (a, b) {
  390. parse(a, b);
  391. return newFraction(
  392. this["s"] * P["s"] * this["n"] * P["d"],
  393. this["d"] * P["n"]
  394. );
  395. },
  396. /**
  397. * Clones the actual object
  398. *
  399. * Ex: new Fraction("-17.(345)").clone()
  400. **/
  401. "clone": function () {
  402. return newFraction(this['s'] * this['n'], this['d']);
  403. },
  404. /**
  405. * Calculates the modulo of two rational numbers - a more precise fmod
  406. *
  407. * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
  408. * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
  409. **/
  410. "mod": function (a, b) {
  411. if (a === undefined) {
  412. return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
  413. }
  414. parse(a, b);
  415. if (C_ZERO === P["n"] * this["d"]) {
  416. throw DivisionByZero();
  417. }
  418. /**
  419. * I derived the rational modulo similar to the modulo for integers
  420. *
  421. * https://raw.org/book/analysis/rational-numbers/
  422. *
  423. * n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
  424. * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
  425. * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
  426. * = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
  427. * = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
  428. */
  429. return newFraction(
  430. this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
  431. P["d"] * this["d"]);
  432. },
  433. /**
  434. * Calculates the fractional gcd of two rational numbers
  435. *
  436. * Ex: new Fraction(5,8).gcd(3,7) => 1/56
  437. */
  438. "gcd": function (a, b) {
  439. parse(a, b);
  440. // https://raw.org/book/analysis/rational-numbers/
  441. // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
  442. return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
  443. },
  444. /**
  445. * Calculates the fractional lcm of two rational numbers
  446. *
  447. * Ex: new Fraction(5,8).lcm(3,7) => 15
  448. */
  449. "lcm": function (a, b) {
  450. parse(a, b);
  451. // https://raw.org/book/analysis/rational-numbers/
  452. // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
  453. if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
  454. return newFraction(C_ZERO, C_ONE);
  455. }
  456. return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
  457. },
  458. /**
  459. * Gets the inverse of the fraction, means numerator and denominator are exchanged
  460. *
  461. * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
  462. **/
  463. "inverse": function () {
  464. return newFraction(this["s"] * this["d"], this["n"]);
  465. },
  466. /**
  467. * Calculates the fraction to some integer exponent
  468. *
  469. * Ex: new Fraction(-1,2).pow(-3) => -8
  470. */
  471. "pow": function (a, b) {
  472. parse(a, b);
  473. // Trivial case when exp is an integer
  474. if (P['d'] === C_ONE) {
  475. if (P['s'] < C_ZERO) {
  476. return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
  477. } else {
  478. return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
  479. }
  480. }
  481. // Negative roots become complex
  482. // (-a/b)^(c/d) = x
  483. // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
  484. // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
  485. // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x # DeMoivre's formula
  486. // From which follows that only for c=0 the root is non-complex
  487. if (this['s'] < C_ZERO) return null;
  488. // Now prime factor n and d
  489. let N = factorize(this['n']);
  490. let D = factorize(this['d']);
  491. // Exponentiate and take root for n and d individually
  492. let n = C_ONE;
  493. let d = C_ONE;
  494. for (let k in N) {
  495. if (k === '1') continue;
  496. if (k === '0') {
  497. n = C_ZERO;
  498. break;
  499. }
  500. N[k] *= P['n'];
  501. if (N[k] % P['d'] === C_ZERO) {
  502. N[k] /= P['d'];
  503. } else return null;
  504. n *= BigInt(k) ** N[k];
  505. }
  506. for (let k in D) {
  507. if (k === '1') continue;
  508. D[k] *= P['n'];
  509. if (D[k] % P['d'] === C_ZERO) {
  510. D[k] /= P['d'];
  511. } else return null;
  512. d *= BigInt(k) ** D[k];
  513. }
  514. if (P['s'] < C_ZERO) {
  515. return newFraction(d, n);
  516. }
  517. return newFraction(n, d);
  518. },
  519. /**
  520. * Calculates the logarithm of a fraction to a given rational base
  521. *
  522. * Ex: new Fraction(27, 8).log(9, 4) => 3/2
  523. */
  524. "log": function (a, b) {
  525. parse(a, b);
  526. if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
  527. const allPrimes = Object.create(null);
  528. const baseFactors = factorize(P['n']);
  529. const T1 = factorize(P['d']);
  530. const numberFactors = factorize(this['n']);
  531. const T2 = factorize(this['d']);
  532. for (const prime in T1) {
  533. baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
  534. }
  535. for (const prime in T2) {
  536. numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
  537. }
  538. for (const prime in baseFactors) {
  539. if (prime === '1') continue;
  540. allPrimes[prime] = true;
  541. }
  542. for (const prime in numberFactors) {
  543. if (prime === '1') continue;
  544. allPrimes[prime] = true;
  545. }
  546. let retN = null;
  547. let retD = null;
  548. // Iterate over all unique primes to determine if a consistent ratio exists
  549. for (const prime in allPrimes) {
  550. const baseExponent = baseFactors[prime] || C_ZERO;
  551. const numberExponent = numberFactors[prime] || C_ZERO;
  552. if (baseExponent === C_ZERO) {
  553. if (numberExponent !== C_ZERO) {
  554. return null; // Logarithm cannot be expressed as a rational number
  555. }
  556. continue; // Skip this prime since both exponents are zero
  557. }
  558. // Calculate the ratio of exponents for this prime
  559. let curN = numberExponent;
  560. let curD = baseExponent;
  561. // Simplify the current ratio
  562. const gcdValue = gcd(curN, curD);
  563. curN /= gcdValue;
  564. curD /= gcdValue;
  565. // Check if this is the first ratio; otherwise, ensure ratios are consistent
  566. if (retN === null && retD === null) {
  567. retN = curN;
  568. retD = curD;
  569. } else if (curN * retD !== retN * curD) {
  570. return null; // Ratios do not match, logarithm cannot be rational
  571. }
  572. }
  573. return retN !== null && retD !== null
  574. ? newFraction(retN, retD)
  575. : null;
  576. },
  577. /**
  578. * Check if two rational numbers are the same
  579. *
  580. * Ex: new Fraction(19.6).equals([98, 5]);
  581. **/
  582. "equals": function (a, b) {
  583. parse(a, b);
  584. return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
  585. },
  586. /**
  587. * Check if this rational number is less than another
  588. *
  589. * Ex: new Fraction(19.6).lt([98, 5]);
  590. **/
  591. "lt": function (a, b) {
  592. parse(a, b);
  593. return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
  594. },
  595. /**
  596. * Check if this rational number is less than or equal another
  597. *
  598. * Ex: new Fraction(19.6).lt([98, 5]);
  599. **/
  600. "lte": function (a, b) {
  601. parse(a, b);
  602. return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
  603. },
  604. /**
  605. * Check if this rational number is greater than another
  606. *
  607. * Ex: new Fraction(19.6).lt([98, 5]);
  608. **/
  609. "gt": function (a, b) {
  610. parse(a, b);
  611. return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
  612. },
  613. /**
  614. * Check if this rational number is greater than or equal another
  615. *
  616. * Ex: new Fraction(19.6).lt([98, 5]);
  617. **/
  618. "gte": function (a, b) {
  619. parse(a, b);
  620. return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
  621. },
  622. /**
  623. * Compare two rational numbers
  624. * < 0 iff this < that
  625. * > 0 iff this > that
  626. * = 0 iff this = that
  627. *
  628. * Ex: new Fraction(19.6).compare([98, 5]);
  629. **/
  630. "compare": function (a, b) {
  631. parse(a, b);
  632. let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
  633. return (C_ZERO < t) - (t < C_ZERO);
  634. },
  635. /**
  636. * Calculates the ceil of a rational number
  637. *
  638. * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
  639. **/
  640. "ceil": function (places) {
  641. places = C_TEN ** BigInt(places || 0);
  642. return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
  643. (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
  644. places);
  645. },
  646. /**
  647. * Calculates the floor of a rational number
  648. *
  649. * Ex: new Fraction('4.(3)').floor() => (4 / 1)
  650. **/
  651. "floor": function (places) {
  652. places = C_TEN ** BigInt(places || 0);
  653. return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
  654. (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
  655. places);
  656. },
  657. /**
  658. * Rounds a rational numbers
  659. *
  660. * Ex: new Fraction('4.(3)').round() => (4 / 1)
  661. **/
  662. "round": function (places) {
  663. places = C_TEN ** BigInt(places || 0);
  664. /* Derivation:
  665. s >= 0:
  666. round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
  667. = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
  668. s < 0:
  669. round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
  670. =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
  671. =>:
  672. round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
  673. where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
  674. */
  675. return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
  676. this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
  677. places);
  678. },
  679. /**
  680. * Rounds a rational number to a multiple of another rational number
  681. *
  682. * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
  683. **/
  684. "roundTo": function (a, b) {
  685. /*
  686. k * x/y ≤ a/b < (k+1) * x/y
  687. ⇔ k ≤ a/b / (x/y) < (k+1)
  688. ⇔ k = floor(a/b * y/x)
  689. ⇔ k = floor((a * y) / (b * x))
  690. */
  691. parse(a, b);
  692. const n = this['n'] * P['d'];
  693. const d = this['d'] * P['n'];
  694. const r = n % d;
  695. // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
  696. let k = ifloor(n / d);
  697. if (r + r >= d) {
  698. k++;
  699. }
  700. return newFraction(this['s'] * k * P['n'], P['d']);
  701. },
  702. /**
  703. * Check if two rational numbers are divisible
  704. *
  705. * Ex: new Fraction(19.6).divisible(1.5);
  706. */
  707. "divisible": function (a, b) {
  708. parse(a, b);
  709. if (P['n'] === C_ZERO) return false;
  710. return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
  711. },
  712. /**
  713. * Returns a decimal representation of the fraction
  714. *
  715. * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
  716. **/
  717. 'valueOf': function () {
  718. //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
  719. return Number(this['s'] * this['n']) / Number(this['d']);
  720. //}
  721. },
  722. /**
  723. * Creates a string representation of a fraction with all digits
  724. *
  725. * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
  726. **/
  727. 'toString': function (dec = 15) {
  728. let N = this["n"];
  729. let D = this["d"];
  730. let cycLen = cycleLen(N, D); // Cycle length
  731. let cycOff = cycleStart(N, D, cycLen); // Cycle start
  732. let str = this['s'] < C_ZERO ? "-" : "";
  733. // Append integer part
  734. str += ifloor(N / D);
  735. N %= D;
  736. N *= C_TEN;
  737. if (N)
  738. str += ".";
  739. if (cycLen) {
  740. for (let i = cycOff; i--;) {
  741. str += ifloor(N / D);
  742. N %= D;
  743. N *= C_TEN;
  744. }
  745. str += "(";
  746. for (let i = cycLen; i--;) {
  747. str += ifloor(N / D);
  748. N %= D;
  749. N *= C_TEN;
  750. }
  751. str += ")";
  752. } else {
  753. for (let i = dec; N && i--;) {
  754. str += ifloor(N / D);
  755. N %= D;
  756. N *= C_TEN;
  757. }
  758. }
  759. return str;
  760. },
  761. /**
  762. * Returns a string-fraction representation of a Fraction object
  763. *
  764. * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
  765. **/
  766. 'toFraction': function (showMixed = false) {
  767. let n = this["n"];
  768. let d = this["d"];
  769. let str = this['s'] < C_ZERO ? "-" : "";
  770. if (d === C_ONE) {
  771. str += n;
  772. } else {
  773. const whole = ifloor(n / d);
  774. if (showMixed && whole > C_ZERO) {
  775. str += whole;
  776. str += " ";
  777. n %= d;
  778. }
  779. str += n;
  780. str += '/';
  781. str += d;
  782. }
  783. return str;
  784. },
  785. /**
  786. * Returns a latex representation of a Fraction object
  787. *
  788. * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
  789. **/
  790. 'toLatex': function (showMixed = false) {
  791. let n = this["n"];
  792. let d = this["d"];
  793. let str = this['s'] < C_ZERO ? "-" : "";
  794. if (d === C_ONE) {
  795. str += n;
  796. } else {
  797. const whole = ifloor(n / d);
  798. if (showMixed && whole > C_ZERO) {
  799. str += whole;
  800. n %= d;
  801. }
  802. str += "\\frac{";
  803. str += n;
  804. str += '}{';
  805. str += d;
  806. str += '}';
  807. }
  808. return str;
  809. },
  810. /**
  811. * Returns an array of continued fraction elements
  812. *
  813. * Ex: new Fraction("7/8").toContinued() => [0,1,7]
  814. */
  815. 'toContinued': function () {
  816. let a = this['n'];
  817. let b = this['d'];
  818. const res = [];
  819. while (b) {
  820. res.push(ifloor(a / b));
  821. const t = a % b;
  822. a = b;
  823. b = t;
  824. }
  825. return res;
  826. },
  827. "simplify": function (eps = 1e-3) {
  828. // Continued fractions give best approximations for a max denominator,
  829. // generally outperforming mediants in denominator–accuracy trade-offs.
  830. // Semiconvergents can further reduce the denominator within tolerance.
  831. const ieps = BigInt(Math.ceil(1 / eps));
  832. const thisABS = this['abs']();
  833. const cont = thisABS['toContinued']();
  834. for (let i = 1; i < cont.length; i++) {
  835. let s = newFraction(cont[i - 1], C_ONE);
  836. for (let k = i - 2; k >= 0; k--) {
  837. s = s['inverse']()['add'](cont[k]);
  838. }
  839. let t = s['sub'](thisABS);
  840. if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
  841. return s['mul'](this['s']);
  842. }
  843. }
  844. return this;
  845. }
  846. };
  847. export {
  848. Fraction as default, Fraction
  849. };