| 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
|
|---|
| 33 | if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
|
|---|
| 34 |
|
|---|
| 35 | const C_ZERO = BigInt(0);
|
|---|
| 36 | const C_ONE = BigInt(1);
|
|---|
| 37 | const C_TWO = BigInt(2);
|
|---|
| 38 | const C_THREE = BigInt(3);
|
|---|
| 39 | const C_FIVE = BigInt(5);
|
|---|
| 40 | const C_TEN = BigInt(10);
|
|---|
| 41 | const 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
|
|---|
| 46 | const MAX_CYCLE_LEN = 2000;
|
|---|
| 47 |
|
|---|
| 48 | // Parsed data to avoid calling "new" all the time
|
|---|
| 49 | const P = {
|
|---|
| 50 | "s": C_ONE,
|
|---|
| 51 | "n": C_ZERO,
|
|---|
| 52 | "d": C_ONE
|
|---|
| 53 | };
|
|---|
| 54 |
|
|---|
| 55 | function assign(n, s) {
|
|---|
| 56 |
|
|---|
| 57 | try {
|
|---|
| 58 | n = BigInt(n);
|
|---|
| 59 | } catch (e) {
|
|---|
| 60 | throw InvalidParameter();
|
|---|
| 61 | }
|
|---|
| 62 | return n * s;
|
|---|
| 63 | }
|
|---|
| 64 |
|
|---|
| 65 | function 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
|
|---|
| 70 | function 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 |
|
|---|
| 88 | 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
|
|---|
| 89 | function 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 |
|
|---|
| 114 | const 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 |
|
|---|
| 305 | function 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 |
|
|---|
| 317 | function 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 |
|
|---|
| 347 | function 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 |
|
|---|
| 364 | function 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 | */
|
|---|
| 388 | function 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 |
|
|---|
| 402 | const DivisionByZero = function () { return new Error("Division by Zero"); };
|
|---|
| 403 | const InvalidParameter = function () { return new Error("Invalid argument"); };
|
|---|
| 404 | const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
|
|---|
| 405 |
|
|---|
| 406 | Fraction.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 | };
|
|---|
| 1041 | export {
|
|---|
| 1042 | Fraction as default, Fraction
|
|---|
| 1043 | };
|
|---|