source: node_modules/fraction.js/dist/fraction.mjs@ 2058e5c

Last change on this file since 2058e5c was 2058e5c, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Working / before login

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