fraction.js
Advanced tools
| /** | ||
| * Interface representing a fraction with numerator and denominator. | ||
| */ | ||
| export interface NumeratorDenominator { | ||
| n: number | bigint; | ||
| d: number | bigint; | ||
| } | ||
| /** | ||
| * Type for handling multiple types of input for Fraction operations. | ||
| */ | ||
| export type FractionInput = | ||
| | Fraction | ||
| | number | ||
| | string | ||
| | [number | string, number | string] | ||
| | NumeratorDenominator; | ||
| /** | ||
| * Function signature for Fraction operations like add, sub, mul, etc. | ||
| */ | ||
| export type FractionParam = { | ||
| (fraction: Fraction): Fraction; | ||
| (num: number | bigint | string): Fraction; | ||
| (numerator: number | bigint, denominator: number | bigint): Fraction; | ||
| (numbers: [number | bigint | string, number | bigint | string]): Fraction; | ||
| (fraction: NumeratorDenominator): Fraction; | ||
| (firstValue: FractionInput): Fraction; | ||
| }; | ||
| /** | ||
| * Fraction class representing a rational number with numerator and denominator. | ||
| */ | ||
| class Fraction { | ||
| constructor(fraction: Fraction); | ||
| constructor(num: number | bigint | string); | ||
| constructor(numerator: number | bigint, denominator: number | bigint); | ||
| constructor(numbers: [number | bigint | string, number | bigint | string]); | ||
| constructor(fraction: NumeratorDenominator); | ||
| constructor(firstValue: FractionInput, secondValue?: number); | ||
| s: bigint; | ||
| n: bigint; | ||
| d: bigint; | ||
| abs(): Fraction; | ||
| neg(): Fraction; | ||
| add: FractionParam; | ||
| sub: FractionParam; | ||
| mul: FractionParam; | ||
| div: FractionParam; | ||
| pow: FractionParam; | ||
| log: FractionParam; | ||
| gcd: FractionParam; | ||
| lcm: FractionParam; | ||
| mod(n?: number | bigint | string | Fraction): Fraction; | ||
| ceil(places?: number): Fraction; | ||
| floor(places?: number): Fraction; | ||
| round(places?: number): Fraction; | ||
| roundTo: FractionParam; | ||
| inverse(): Fraction; | ||
| simplify(eps?: number): Fraction; | ||
| equals(n: number | bigint | string | Fraction): boolean; | ||
| lt(n: number | bigint | string | Fraction): boolean; | ||
| lte(n: number | bigint | string | Fraction): boolean; | ||
| gt(n: number | bigint | string | Fraction): boolean; | ||
| gte(n: number | bigint | string | Fraction): boolean; | ||
| compare(n: number | bigint | string | Fraction): number; | ||
| divisible(n: number | bigint | string | Fraction): boolean; | ||
| valueOf(): number; | ||
| toString(decimalPlaces?: number): string; | ||
| toLatex(showMixed?: boolean): string; | ||
| toFraction(showMixed?: boolean): string; | ||
| toContinued(): bigint[]; | ||
| clone(): Fraction; | ||
| } | ||
| export { Fraction as default, Fraction }; |
+60
-55
@@ -38,4 +38,6 @@ 'use strict'; | ||
| const C_TWO = BigInt(2); | ||
| const C_THREE = BigInt(3); | ||
| const C_FIVE = BigInt(5); | ||
| const C_TEN = BigInt(10); | ||
| const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); | ||
@@ -64,3 +66,3 @@ // Maximum search depth for cyclic rational numbers. 2000 should be more than enough. | ||
| function trunc(x) { | ||
| function ifloor(x) { | ||
| return typeof x === 'bigint' ? x : Math.floor(x); | ||
@@ -88,25 +90,25 @@ } | ||
| function factorize(num) { | ||
| 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 | ||
| function factorize(n) { | ||
| const factors = {}; | ||
| const factors = Object.create(null); | ||
| if (n <= C_ONE) { | ||
| factors[n] = C_ONE; | ||
| return factors; | ||
| } | ||
| let n = num; | ||
| let i = C_TWO; | ||
| let s = C_FIVE - C_ONE; | ||
| const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; }; | ||
| while (s <= n) { | ||
| while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; } | ||
| while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; } | ||
| while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; } | ||
| while (n % i === C_ZERO) { | ||
| n /= i; | ||
| factors[i] = (factors[i] || C_ZERO) + C_ONE; | ||
| } | ||
| s += C_ONE + C_TWO * i++; | ||
| // 30-wheel trial division: test only residues coprime to 2*3*5 | ||
| // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ... | ||
| for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) { | ||
| while (n % p === C_ZERO) { add(p); n /= p; } | ||
| p += FACTORSTEPS[si]; | ||
| si = (si + 1) & 7; // fast modulo 8 | ||
| } | ||
| if (n !== num) { | ||
| if (n > 1) | ||
| factors[n] = (factors[n] || C_ZERO) + C_ONE; | ||
| } else { | ||
| factors[num] = (factors[num] || C_ZERO) + C_ONE; | ||
| } | ||
| if (n > C_ONE) add(n); | ||
| return factors; | ||
@@ -403,5 +405,5 @@ } | ||
| var DivisionByZero = function () { return new Error("Division by Zero"); }; | ||
| var InvalidParameter = function () { return new Error("Invalid argument"); }; | ||
| var NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; | ||
| const DivisionByZero = function () { return new Error("Division by Zero"); }; | ||
| const InvalidParameter = function () { return new Error("Invalid argument"); }; | ||
| const NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; | ||
@@ -650,3 +652,3 @@ Fraction.prototype = { | ||
| const allPrimes = {}; | ||
| const allPrimes = Object.create(null); | ||
@@ -794,3 +796,3 @@ const baseFactors = factorize(P['n']); | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) + | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + | ||
| (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO), | ||
@@ -809,3 +811,3 @@ places); | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) - | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) - | ||
| (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO), | ||
@@ -827,15 +829,15 @@ places); | ||
| s >= 0: | ||
| round(n / d) = trunc(n / d) + (n % d) / d >= 0.5 ? 1 : 0 | ||
| = trunc(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0 | ||
| = ifloor(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| s < 0: | ||
| round(n / d) =-trunc(n / d) - (n % d) / d > 0.5 ? 1 : 0 | ||
| =-trunc(n / d) - 2(n % d) > d ? 1 : 0 | ||
| round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0 | ||
| =-ifloor(n / d) - 2(n % d) > d ? 1 : 0 | ||
| =>: | ||
| round(s * n / d) = s * trunc(n / d) + s * (C + 2(n % d) > d ? 1 : 0) | ||
| round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0) | ||
| where C = s >= 0 ? 1 : 0, to fix the >= for the positve case. | ||
| */ | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) + | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + | ||
| this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO), | ||
@@ -865,4 +867,4 @@ places); | ||
| // round(n / d) = trunc(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| let k = trunc(n / d); | ||
| // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| let k = ifloor(n / d); | ||
| if (r + r >= d) { | ||
@@ -882,3 +884,4 @@ k++; | ||
| parse(a, b); | ||
| return !(!(P["n"] * this["d"]) || ((this["n"] * P["d"]) % (P["n"] * this["d"]))); | ||
| if (P['n'] === C_ZERO) return false; | ||
| return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO; | ||
| }, | ||
@@ -892,4 +895,5 @@ | ||
| 'valueOf': function () { | ||
| // Best we can do so far | ||
| return Number(this["s"] * this["n"]) / Number(this["d"]); | ||
| //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) { | ||
| return Number(this['s'] * this['n']) / Number(this['d']); | ||
| //} | ||
| }, | ||
@@ -902,3 +906,3 @@ | ||
| **/ | ||
| 'toString': function (dec) { | ||
| 'toString': function (dec = 15) { | ||
@@ -908,4 +912,2 @@ let N = this["n"]; | ||
| dec = dec || 15; // 15 = decimal places when no repetition | ||
| let cycLen = cycleLen(N, D); // Cycle length | ||
@@ -917,3 +919,3 @@ let cycOff = cycleStart(N, D, cycLen); // Cycle start | ||
| // Append integer part | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
@@ -929,3 +931,3 @@ N %= D; | ||
| for (let i = cycOff; i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -936,3 +938,3 @@ N *= C_TEN; | ||
| for (let i = cycLen; i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -944,3 +946,3 @@ N *= C_TEN; | ||
| for (let i = dec; N && i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -958,3 +960,3 @@ N *= C_TEN; | ||
| **/ | ||
| 'toFraction': function (showMixed) { | ||
| 'toFraction': function (showMixed = false) { | ||
@@ -968,3 +970,3 @@ let n = this["n"]; | ||
| } else { | ||
| let whole = trunc(n / d); | ||
| const whole = ifloor(n / d); | ||
| if (showMixed && whole > C_ZERO) { | ||
@@ -988,3 +990,3 @@ str += whole; | ||
| **/ | ||
| 'toLatex': function (showMixed) { | ||
| 'toLatex': function (showMixed = false) { | ||
@@ -998,3 +1000,3 @@ let n = this["n"]; | ||
| } else { | ||
| let whole = trunc(n / d); | ||
| const whole = ifloor(n / d); | ||
| if (showMixed && whole > C_ZERO) { | ||
@@ -1023,18 +1025,21 @@ str += whole; | ||
| let b = this['d']; | ||
| let res = []; | ||
| const res = []; | ||
| do { | ||
| res.push(trunc(a / b)); | ||
| let t = a % b; | ||
| while (b) { | ||
| res.push(ifloor(a / b)); | ||
| const t = a % b; | ||
| a = b; | ||
| b = t; | ||
| } while (a !== C_ONE); | ||
| } | ||
| return res; | ||
| }, | ||
| "simplify": function (eps) { | ||
| "simplify": function (eps = 1e-3) { | ||
| const ieps = BigInt(1 / (eps || 0.001) | 0); | ||
| // Continued fractions give best approximations for a max denominator, | ||
| // generally outperforming mediants in denominator–accuracy trade-offs. | ||
| // Semiconvergents can further reduce the denominator within tolerance. | ||
| const ieps = BigInt(Math.ceil(1 / eps)); | ||
| const thisABS = this['abs'](); | ||
@@ -1041,0 +1046,0 @@ const cont = thisABS['toContinued'](); |
+15
-15
| /* | ||
| Fraction.js v5.2.2 3/30/2025 | ||
| Fraction.js v5.3.0 8/16/2025 | ||
| https://raw.org/article/rational-numbers-in-javascript/ | ||
@@ -8,15 +8,15 @@ | ||
| */ | ||
| 'use strict';(function(E){function C(){return Error("Parameters must be integer")}function w(){return Error("Invalid argument")}function A(){return Error("Division by Zero")}function p(a,b){var d=g,c=h;let f=h;if(void 0!==a&&null!==a)if(void 0!==b){if("bigint"===typeof a)d=a;else{if(isNaN(a))throw w();if(0!==a%1)throw C();d=BigInt(a)}if("bigint"===typeof b)c=b;else{if(isNaN(b))throw w();if(0!==b%1)throw C();c=BigInt(b)}f=d*c}else if("object"===typeof a){if("d"in a&&"n"in a)d=BigInt(a.n),c=BigInt(a.d), | ||
| "s"in a&&(d*=BigInt(a.s));else if(0 in a)d=BigInt(a[0]),1 in a&&(c=BigInt(a[1]));else if("bigint"===typeof a)d=a;else throw w();f=d*c}else if("number"===typeof a){if(isNaN(a))throw w();0>a&&(f=-h,a=-a);if(0===a%1)d=BigInt(a);else{b=1;var k=0,l=1,m=1;let q=1;1<=a&&(b=10**Math.floor(1+Math.log10(a)),a/=b);for(;1E7>=l&&1E7>=q;)if(c=(k+m)/(l+q),a===c){1E7>=l+q?(d=k+m,c=l+q):q>l?(d=m,c=q):(d=k,c=l);break}else a>c?(k+=m,l+=q):(m+=k,q+=l),1E7<l?(d=m,c=q):(d=k,c=l);d=BigInt(d)*BigInt(b);c=BigInt(c)}}else if("string"=== | ||
| typeof a){c=0;k=b=d=g;l=m=h;a=a.replace(/_/g,"").match(/\d+|./g);if(null===a)throw w();"-"===a[c]?(f=-h,c++):"+"===a[c]&&c++;if(a.length===c+1)b=v(a[c++],f);else if("."===a[c+1]||"."===a[c]){"."!==a[c]&&(d=v(a[c++],f));c++;if(c+1===a.length||"("===a[c+1]&&")"===a[c+3]||"'"===a[c+1]&&"'"===a[c+3])b=v(a[c],f),m=r**BigInt(a[c].length),c++;if("("===a[c]&&")"===a[c+2]||"'"===a[c]&&"'"===a[c+2])k=v(a[c+1],f),l=r**BigInt(a[c+1].length)-h,c+=3}else"/"===a[c+1]||":"===a[c+1]?(b=v(a[c],f),m=v(a[c+2],h),c+= | ||
| 3):"/"===a[c+3]&&" "===a[c+1]&&(d=v(a[c],f),b=v(a[c+2],f),m=v(a[c+4],h),c+=5);if(a.length<=c)c=m*l,f=d=k+c*d+l*b;else throw w();}else if("bigint"===typeof a)f=d=a,c=h;else throw w();if(c===g)throw A();e.s=f<g?-h:h;e.n=d<g?-d:d;e.d=c<g?-c:c}function v(a,b){try{a=BigInt(a)}catch(d){throw w();}return a*b}function t(a){return"bigint"===typeof a?a:Math.floor(a)}function n(a,b){if(b===g)throw A();const d=Object.create(u.prototype);d.s=a<g?-h:h;a=a<g?-a:a;const c=x(a,b);d.n=a/c;d.d=b/c;return d}function y(a){const b= | ||
| {};let d=a,c=z,f=B-h;for(;f<=d;){for(;d%c===g;)d/=c,b[c]=(b[c]||g)+h;f+=h+z*c++}d!==a?1<d&&(b[d]=(b[d]||g)+h):b[a]=(b[a]||g)+h;return b}function x(a,b){if(!a)return b;if(!b)return a;for(;;){a%=b;if(!a)return b;b%=a;if(!b)return a}}function u(a,b){p(a,b);if(this instanceof u)a=x(e.d,e.n),this.s=e.s,this.n=e.n/a,this.d=e.d/a;else return n(e.s*e.n,e.d)}"undefined"===typeof BigInt&&(BigInt=function(a){if(isNaN(a))throw Error("");return a});const g=BigInt(0),h=BigInt(1),z=BigInt(2),B=BigInt(5),r=BigInt(10), | ||
| e={s:h,n:g,d:h};u.prototype={s:h,n:g,d:h,abs:function(){return n(this.n,this.d)},neg:function(){return n(-this.s*this.n,this.d)},add:function(a,b){p(a,b);return n(this.s*this.n*e.d+e.s*this.d*e.n,this.d*e.d)},sub:function(a,b){p(a,b);return n(this.s*this.n*e.d-e.s*this.d*e.n,this.d*e.d)},mul:function(a,b){p(a,b);return n(this.s*e.s*this.n*e.n,this.d*e.d)},div:function(a,b){p(a,b);return n(this.s*e.s*this.n*e.d,this.d*e.n)},clone:function(){return n(this.s*this.n,this.d)},mod:function(a,b){if(void 0=== | ||
| a)return n(this.s*this.n%this.d,h);p(a,b);if(g===e.n*this.d)throw A();return n(this.s*e.d*this.n%(e.n*this.d),e.d*this.d)},gcd:function(a,b){p(a,b);return n(x(e.n,this.n)*x(e.d,this.d),e.d*this.d)},lcm:function(a,b){p(a,b);return e.n===g&&this.n===g?n(g,h):n(e.n*this.n,x(e.n,this.n)*x(e.d,this.d))},inverse:function(){return n(this.s*this.d,this.n)},pow:function(a,b){p(a,b);if(e.d===h)return e.s<g?n((this.s*this.d)**e.n,this.n**e.n):n((this.s*this.n)**e.n,this.d**e.n);if(this.s<g)return null;a=y(this.n); | ||
| b=y(this.d);let d=h,c=h;for(let f in a)if("1"!==f){if("0"===f){d=g;break}a[f]*=e.n;if(a[f]%e.d===g)a[f]/=e.d;else return null;d*=BigInt(f)**a[f]}for(let f in b)if("1"!==f){b[f]*=e.n;if(b[f]%e.d===g)b[f]/=e.d;else return null;c*=BigInt(f)**b[f]}return e.s<g?n(c,d):n(d,c)},log:function(a,b){p(a,b);if(this.s<=g||e.s<=g)return null;var d={};a=y(e.n);const c=y(e.d);b=y(this.n);const f=y(this.d);for(var k in c)a[k]=(a[k]||g)-c[k];for(var l in f)b[l]=(b[l]||g)-f[l];for(var m in a)"1"!==m&&(d[m]=!0);for(var q in b)"1"!== | ||
| q&&(d[q]=!0);l=k=null;for(const D in d)if(m=a[D]||g,d=b[D]||g,m===g){if(d!==g)return null}else if(q=x(d,m),d/=q,m/=q,null===k&&null===l)k=d,l=m;else if(d*l!==k*m)return null;return null!==k&&null!==l?n(k,l):null},equals:function(a,b){p(a,b);return this.s*this.n*e.d===e.s*e.n*this.d},lt:function(a,b){p(a,b);return this.s*this.n*e.d<e.s*e.n*this.d},lte:function(a,b){p(a,b);return this.s*this.n*e.d<=e.s*e.n*this.d},gt:function(a,b){p(a,b);return this.s*this.n*e.d>e.s*e.n*this.d},gte:function(a,b){p(a, | ||
| b);return this.s*this.n*e.d>=e.s*e.n*this.d},compare:function(a,b){p(a,b);a=this.s*this.n*e.d-e.s*e.n*this.d;return(g<a)-(a<g)},ceil:function(a){a=r**BigInt(a||0);return n(t(this.s*a*this.n/this.d)+(a*this.n%this.d>g&&this.s>=g?h:g),a)},floor:function(a){a=r**BigInt(a||0);return n(t(this.s*a*this.n/this.d)-(a*this.n%this.d>g&&this.s<g?h:g),a)},round:function(a){a=r**BigInt(a||0);return n(t(this.s*a*this.n/this.d)+this.s*((this.s>=g?h:g)+a*this.n%this.d*z>this.d?h:g),a)},roundTo:function(a,b){p(a, | ||
| b);var d=this.n*e.d;a=this.d*e.n;b=d%a;d=t(d/a);b+b>=a&&d++;return n(this.s*d*e.n,e.d)},divisible:function(a,b){p(a,b);return!(!(e.n*this.d)||this.n*e.d%(e.n*this.d))},valueOf:function(){return Number(this.s*this.n)/Number(this.d)},toString:function(a){let b=this.n,d=this.d;a=a||15;var c;a:{for(c=d;c%z===g;c/=z);for(;c%B===g;c/=B);if(c===h)c=g;else{for(var f=r%c,k=1;f!==h;k++)if(f=f*r%c,2E3<k){c=g;break a}c=BigInt(k)}}a:{f=h;k=r;var l=c;let m=h;for(;l>g;k=k*k%d,l>>=h)l&h&&(m=m*k%d);k=m;for(l=0;300> | ||
| l;l++){if(f===k){f=BigInt(l);break a}f=f*r%d;k=k*r%d}f=0}k=f;f=this.s<g?"-":"";f+=t(b/d);(b=b%d*r)&&(f+=".");if(c){for(a=k;a--;)f+=t(b/d),b%=d,b*=r;f+="(";for(a=c;a--;)f+=t(b/d),b%=d,b*=r;f+=")"}else for(;b&&a--;)f+=t(b/d),b%=d,b*=r;return f},toFraction:function(a){let b=this.n,d=this.d,c=this.s<g?"-":"";if(d===h)c+=b;else{let f=t(b/d);a&&f>g&&(c+=f,c+=" ",b%=d);c=c+b+"/"+d}return c},toLatex:function(a){let b=this.n,d=this.d,c=this.s<g?"-":"";if(d===h)c+=b;else{let f=t(b/d);a&&f>g&&(c+=f,b%=d);c= | ||
| c+"\\frac{"+b+"}{"+d;c+="}"}return c},toContinued:function(){let a=this.n,b=this.d,d=[];do{d.push(t(a/b));let c=a%b;a=b;b=c}while(a!==h);return d},simplify:function(a){a=BigInt(1/(a||.001)|0);const b=this.abs(),d=b.toContinued();for(let f=1;f<d.length;f++){let k=n(d[f-1],h);for(var c=f-2;0<=c;c--)k=k.inverse().add(d[c]);c=k.sub(b);if(c.n*a<c.d)return k.mul(this.s)}return this}};"function"===typeof define&&define.amd?define([],function(){return u}):"object"===typeof exports?(Object.defineProperty(u, | ||
| "__esModule",{value:!0}),u["default"]=u,u.Fraction=u,module.exports=u):E.Fraction=u})(this); | ||
| 'use strict';(function(F){function D(){return Error("Parameters must be integer")}function x(){return Error("Invalid argument")}function C(){return Error("Division by Zero")}function q(a,b){var d=g,c=h;let f=h;if(void 0!==a&&null!==a)if(void 0!==b){if("bigint"===typeof a)d=a;else{if(isNaN(a))throw x();if(0!==a%1)throw D();d=BigInt(a)}if("bigint"===typeof b)c=b;else{if(isNaN(b))throw x();if(0!==b%1)throw D();c=BigInt(b)}f=d*c}else if("object"===typeof a){if("d"in a&&"n"in a)d=BigInt(a.n),c=BigInt(a.d), | ||
| "s"in a&&(d*=BigInt(a.s));else if(0 in a)d=BigInt(a[0]),1 in a&&(c=BigInt(a[1]));else if("bigint"===typeof a)d=a;else throw x();f=d*c}else if("number"===typeof a){if(isNaN(a))throw x();0>a&&(f=-h,a=-a);if(0===a%1)d=BigInt(a);else{b=1;var k=0,l=1,m=1;let r=1;1<=a&&(b=10**Math.floor(1+Math.log10(a)),a/=b);for(;1E7>=l&&1E7>=r;)if(c=(k+m)/(l+r),a===c){1E7>=l+r?(d=k+m,c=l+r):r>l?(d=m,c=r):(d=k,c=l);break}else a>c?(k+=m,l+=r):(m+=k,r+=l),1E7<l?(d=m,c=r):(d=k,c=l);d=BigInt(d)*BigInt(b);c=BigInt(c)}}else if("string"=== | ||
| typeof a){c=0;k=b=d=g;l=m=h;a=a.replace(/_/g,"").match(/\d+|./g);if(null===a)throw x();"-"===a[c]?(f=-h,c++):"+"===a[c]&&c++;if(a.length===c+1)b=w(a[c++],f);else if("."===a[c+1]||"."===a[c]){"."!==a[c]&&(d=w(a[c++],f));c++;if(c+1===a.length||"("===a[c+1]&&")"===a[c+3]||"'"===a[c+1]&&"'"===a[c+3])b=w(a[c],f),m=t**BigInt(a[c].length),c++;if("("===a[c]&&")"===a[c+2]||"'"===a[c]&&"'"===a[c+2])k=w(a[c+1],f),l=t**BigInt(a[c+1].length)-h,c+=3}else"/"===a[c+1]||":"===a[c+1]?(b=w(a[c],f),m=w(a[c+2],h),c+= | ||
| 3):"/"===a[c+3]&&" "===a[c+1]&&(d=w(a[c],f),b=w(a[c+2],f),m=w(a[c+4],h),c+=5);if(a.length<=c)c=m*l,f=d=k+c*d+l*b;else throw x();}else if("bigint"===typeof a)f=d=a,c=h;else throw x();if(c===g)throw C();e.s=f<g?-h:h;e.n=d<g?-d:d;e.d=c<g?-c:c}function w(a,b){try{a=BigInt(a)}catch(d){throw x();}return a*b}function u(a){return"bigint"===typeof a?a:Math.floor(a)}function n(a,b){if(b===g)throw C();const d=Object.create(v.prototype);d.s=a<g?-h:h;a=a<g?-a:a;const c=y(a,b);d.n=a/c;d.d=b/c;return d}function A(a){const b= | ||
| Object.create(null);if(a<=h)return b[a]=h,b;for(;a%p===g;)b[p]=(b[p]||g)+h,a/=p;for(;a%B===g;)b[B]=(b[B]||g)+h,a/=B;for(;a%z===g;)b[z]=(b[z]||g)+h,a/=z;for(let d=0,c=p+z;c*c<=a;){for(;a%c===g;)b[c]=(b[c]||g)+h,a/=c;c+=G[d];d=d+1&7}a>h&&(b[a]=(b[a]||g)+h);return b}function y(a,b){if(!a)return b;if(!b)return a;for(;;){a%=b;if(!a)return b;b%=a;if(!b)return a}}function v(a,b){q(a,b);if(this instanceof v)a=y(e.d,e.n),this.s=e.s,this.n=e.n/a,this.d=e.d/a;else return n(e.s*e.n,e.d)}"undefined"===typeof BigInt&& | ||
| (BigInt=function(a){if(isNaN(a))throw Error("");return a});const g=BigInt(0),h=BigInt(1),p=BigInt(2),B=BigInt(3),z=BigInt(5),t=BigInt(10),e={s:h,n:g,d:h},G=[p*p,p,p*p,p,p*p,p*B,p,p*B];v.prototype={s:h,n:g,d:h,abs:function(){return n(this.n,this.d)},neg:function(){return n(-this.s*this.n,this.d)},add:function(a,b){q(a,b);return n(this.s*this.n*e.d+e.s*this.d*e.n,this.d*e.d)},sub:function(a,b){q(a,b);return n(this.s*this.n*e.d-e.s*this.d*e.n,this.d*e.d)},mul:function(a,b){q(a,b);return n(this.s*e.s* | ||
| this.n*e.n,this.d*e.d)},div:function(a,b){q(a,b);return n(this.s*e.s*this.n*e.d,this.d*e.n)},clone:function(){return n(this.s*this.n,this.d)},mod:function(a,b){if(void 0===a)return n(this.s*this.n%this.d,h);q(a,b);if(g===e.n*this.d)throw C();return n(this.s*e.d*this.n%(e.n*this.d),e.d*this.d)},gcd:function(a,b){q(a,b);return n(y(e.n,this.n)*y(e.d,this.d),e.d*this.d)},lcm:function(a,b){q(a,b);return e.n===g&&this.n===g?n(g,h):n(e.n*this.n,y(e.n,this.n)*y(e.d,this.d))},inverse:function(){return n(this.s* | ||
| this.d,this.n)},pow:function(a,b){q(a,b);if(e.d===h)return e.s<g?n((this.s*this.d)**e.n,this.n**e.n):n((this.s*this.n)**e.n,this.d**e.n);if(this.s<g)return null;a=A(this.n);b=A(this.d);let d=h,c=h;for(let f in a)if("1"!==f){if("0"===f){d=g;break}a[f]*=e.n;if(a[f]%e.d===g)a[f]/=e.d;else return null;d*=BigInt(f)**a[f]}for(let f in b)if("1"!==f){b[f]*=e.n;if(b[f]%e.d===g)b[f]/=e.d;else return null;c*=BigInt(f)**b[f]}return e.s<g?n(c,d):n(d,c)},log:function(a,b){q(a,b);if(this.s<=g||e.s<=g)return null; | ||
| var d=Object.create(null);a=A(e.n);const c=A(e.d);b=A(this.n);const f=A(this.d);for(var k in c)a[k]=(a[k]||g)-c[k];for(var l in f)b[l]=(b[l]||g)-f[l];for(var m in a)"1"!==m&&(d[m]=!0);for(var r in b)"1"!==r&&(d[r]=!0);l=k=null;for(const E in d)if(m=a[E]||g,d=b[E]||g,m===g){if(d!==g)return null}else if(r=y(d,m),d/=r,m/=r,null===k&&null===l)k=d,l=m;else if(d*l!==k*m)return null;return null!==k&&null!==l?n(k,l):null},equals:function(a,b){q(a,b);return this.s*this.n*e.d===e.s*e.n*this.d},lt:function(a, | ||
| b){q(a,b);return this.s*this.n*e.d<e.s*e.n*this.d},lte:function(a,b){q(a,b);return this.s*this.n*e.d<=e.s*e.n*this.d},gt:function(a,b){q(a,b);return this.s*this.n*e.d>e.s*e.n*this.d},gte:function(a,b){q(a,b);return this.s*this.n*e.d>=e.s*e.n*this.d},compare:function(a,b){q(a,b);a=this.s*this.n*e.d-e.s*e.n*this.d;return(g<a)-(a<g)},ceil:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/this.d)+(a*this.n%this.d>g&&this.s>=g?h:g),a)},floor:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/ | ||
| this.d)-(a*this.n%this.d>g&&this.s<g?h:g),a)},round:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/this.d)+this.s*((this.s>=g?h:g)+a*this.n%this.d*p>this.d?h:g),a)},roundTo:function(a,b){q(a,b);var d=this.n*e.d;a=this.d*e.n;b=d%a;d=u(d/a);b+b>=a&&d++;return n(this.s*d*e.n,e.d)},divisible:function(a,b){q(a,b);return e.n===g?!1:this.n*e.d%(e.n*this.d)===g},valueOf:function(){return Number(this.s*this.n)/Number(this.d)},toString:function(a=15){let b=this.n,d=this.d;var c;a:{for(c=d;c%p===g;c/= | ||
| p);for(;c%z===g;c/=z);if(c===h)c=g;else{for(var f=t%c,k=1;f!==h;k++)if(f=f*t%c,2E3<k){c=g;break a}c=BigInt(k)}}a:{f=h;k=t;var l=c;let m=h;for(;l>g;k=k*k%d,l>>=h)l&h&&(m=m*k%d);k=m;for(l=0;300>l;l++){if(f===k){f=BigInt(l);break a}f=f*t%d;k=k*t%d}f=0}k=f;f=this.s<g?"-":"";f+=u(b/d);(b=b%d*t)&&(f+=".");if(c){for(a=k;a--;)f+=u(b/d),b%=d,b*=t;f+="(";for(a=c;a--;)f+=u(b/d),b%=d,b*=t;f+=")"}else for(;b&&a--;)f+=u(b/d),b%=d,b*=t;return f},toFraction:function(a=!1){let b=this.n,d=this.d,c=this.s<g?"-":""; | ||
| if(d===h)c+=b;else{const f=u(b/d);a&&f>g&&(c+=f,c+=" ",b%=d);c=c+b+"/"+d}return c},toLatex:function(a=!1){let b=this.n,d=this.d,c=this.s<g?"-":"";if(d===h)c+=b;else{const f=u(b/d);a&&f>g&&(c+=f,b%=d);c=c+"\\frac{"+b+"}{"+d;c+="}"}return c},toContinued:function(){let a=this.n,b=this.d;const d=[];for(;b;){d.push(u(a/b));const c=a%b;a=b;b=c}return d},simplify:function(a=.001){a=BigInt(Math.ceil(1/a));const b=this.abs(),d=b.toContinued();for(let f=1;f<d.length;f++){let k=n(d[f-1],h);for(var c=f-2;0<= | ||
| c;c--)k=k.inverse().add(d[c]);c=k.sub(b);if(c.n*a<c.d)return k.mul(this.s)}return this}};"function"===typeof define&&define.amd?define([],function(){return v}):"object"===typeof exports?(Object.defineProperty(v,"__esModule",{value:!0}),v["default"]=v,v.Fraction=v,module.exports=v):F.Fraction=v})(this); |
+60
-55
@@ -38,4 +38,6 @@ 'use strict'; | ||
| const C_TWO = BigInt(2); | ||
| const C_THREE = BigInt(3); | ||
| const C_FIVE = BigInt(5); | ||
| const C_TEN = BigInt(10); | ||
| const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); | ||
@@ -64,3 +66,3 @@ // Maximum search depth for cyclic rational numbers. 2000 should be more than enough. | ||
| function trunc(x) { | ||
| function ifloor(x) { | ||
| return typeof x === 'bigint' ? x : Math.floor(x); | ||
@@ -88,25 +90,25 @@ } | ||
| function factorize(num) { | ||
| 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 | ||
| function factorize(n) { | ||
| const factors = {}; | ||
| const factors = Object.create(null); | ||
| if (n <= C_ONE) { | ||
| factors[n] = C_ONE; | ||
| return factors; | ||
| } | ||
| let n = num; | ||
| let i = C_TWO; | ||
| let s = C_FIVE - C_ONE; | ||
| const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; }; | ||
| while (s <= n) { | ||
| while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; } | ||
| while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; } | ||
| while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; } | ||
| while (n % i === C_ZERO) { | ||
| n /= i; | ||
| factors[i] = (factors[i] || C_ZERO) + C_ONE; | ||
| } | ||
| s += C_ONE + C_TWO * i++; | ||
| // 30-wheel trial division: test only residues coprime to 2*3*5 | ||
| // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ... | ||
| for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) { | ||
| while (n % p === C_ZERO) { add(p); n /= p; } | ||
| p += FACTORSTEPS[si]; | ||
| si = (si + 1) & 7; // fast modulo 8 | ||
| } | ||
| if (n !== num) { | ||
| if (n > 1) | ||
| factors[n] = (factors[n] || C_ZERO) + C_ONE; | ||
| } else { | ||
| factors[num] = (factors[num] || C_ZERO) + C_ONE; | ||
| } | ||
| if (n > C_ONE) add(n); | ||
| return factors; | ||
@@ -403,5 +405,5 @@ } | ||
| var DivisionByZero = function () { return new Error("Division by Zero"); }; | ||
| var InvalidParameter = function () { return new Error("Invalid argument"); }; | ||
| var NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; | ||
| const DivisionByZero = function () { return new Error("Division by Zero"); }; | ||
| const InvalidParameter = function () { return new Error("Invalid argument"); }; | ||
| const NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; | ||
@@ -650,3 +652,3 @@ Fraction.prototype = { | ||
| const allPrimes = {}; | ||
| const allPrimes = Object.create(null); | ||
@@ -794,3 +796,3 @@ const baseFactors = factorize(P['n']); | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) + | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + | ||
| (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO), | ||
@@ -809,3 +811,3 @@ places); | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) - | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) - | ||
| (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO), | ||
@@ -827,15 +829,15 @@ places); | ||
| s >= 0: | ||
| round(n / d) = trunc(n / d) + (n % d) / d >= 0.5 ? 1 : 0 | ||
| = trunc(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0 | ||
| = ifloor(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| s < 0: | ||
| round(n / d) =-trunc(n / d) - (n % d) / d > 0.5 ? 1 : 0 | ||
| =-trunc(n / d) - 2(n % d) > d ? 1 : 0 | ||
| round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0 | ||
| =-ifloor(n / d) - 2(n % d) > d ? 1 : 0 | ||
| =>: | ||
| round(s * n / d) = s * trunc(n / d) + s * (C + 2(n % d) > d ? 1 : 0) | ||
| round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0) | ||
| where C = s >= 0 ? 1 : 0, to fix the >= for the positve case. | ||
| */ | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) + | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + | ||
| this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO), | ||
@@ -865,4 +867,4 @@ places); | ||
| // round(n / d) = trunc(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| let k = trunc(n / d); | ||
| // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| let k = ifloor(n / d); | ||
| if (r + r >= d) { | ||
@@ -882,3 +884,4 @@ k++; | ||
| parse(a, b); | ||
| return !(!(P["n"] * this["d"]) || ((this["n"] * P["d"]) % (P["n"] * this["d"]))); | ||
| if (P['n'] === C_ZERO) return false; | ||
| return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO; | ||
| }, | ||
@@ -892,4 +895,5 @@ | ||
| 'valueOf': function () { | ||
| // Best we can do so far | ||
| return Number(this["s"] * this["n"]) / Number(this["d"]); | ||
| //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) { | ||
| return Number(this['s'] * this['n']) / Number(this['d']); | ||
| //} | ||
| }, | ||
@@ -902,3 +906,3 @@ | ||
| **/ | ||
| 'toString': function (dec) { | ||
| 'toString': function (dec = 15) { | ||
@@ -908,4 +912,2 @@ let N = this["n"]; | ||
| dec = dec || 15; // 15 = decimal places when no repetition | ||
| let cycLen = cycleLen(N, D); // Cycle length | ||
@@ -917,3 +919,3 @@ let cycOff = cycleStart(N, D, cycLen); // Cycle start | ||
| // Append integer part | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
@@ -929,3 +931,3 @@ N %= D; | ||
| for (let i = cycOff; i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -936,3 +938,3 @@ N *= C_TEN; | ||
| for (let i = cycLen; i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -944,3 +946,3 @@ N *= C_TEN; | ||
| for (let i = dec; N && i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -958,3 +960,3 @@ N *= C_TEN; | ||
| **/ | ||
| 'toFraction': function (showMixed) { | ||
| 'toFraction': function (showMixed = false) { | ||
@@ -968,3 +970,3 @@ let n = this["n"]; | ||
| } else { | ||
| let whole = trunc(n / d); | ||
| const whole = ifloor(n / d); | ||
| if (showMixed && whole > C_ZERO) { | ||
@@ -988,3 +990,3 @@ str += whole; | ||
| **/ | ||
| 'toLatex': function (showMixed) { | ||
| 'toLatex': function (showMixed = false) { | ||
@@ -998,3 +1000,3 @@ let n = this["n"]; | ||
| } else { | ||
| let whole = trunc(n / d); | ||
| const whole = ifloor(n / d); | ||
| if (showMixed && whole > C_ZERO) { | ||
@@ -1023,18 +1025,21 @@ str += whole; | ||
| let b = this['d']; | ||
| let res = []; | ||
| const res = []; | ||
| do { | ||
| res.push(trunc(a / b)); | ||
| let t = a % b; | ||
| while (b) { | ||
| res.push(ifloor(a / b)); | ||
| const t = a % b; | ||
| a = b; | ||
| b = t; | ||
| } while (a !== C_ONE); | ||
| } | ||
| return res; | ||
| }, | ||
| "simplify": function (eps) { | ||
| "simplify": function (eps = 1e-3) { | ||
| const ieps = BigInt(1 / (eps || 0.001) | 0); | ||
| // Continued fractions give best approximations for a max denominator, | ||
| // generally outperforming mediants in denominator–accuracy trade-offs. | ||
| // Semiconvergents can further reduce the denominator within tolerance. | ||
| const ieps = BigInt(Math.ceil(1 / eps)); | ||
| const thisABS = this['abs'](); | ||
@@ -1041,0 +1046,0 @@ const cont = thisABS['toContinued'](); |
@@ -11,3 +11,3 @@ /* | ||
| // NOTE: This is a nice example, but a stable version of this is served with Polynomial.js: | ||
| // https://github.com/infusion/Polynomial.js | ||
| // https://github.com/rawify/Polynomial.js | ||
@@ -14,0 +14,0 @@ function integrate(poly) { |
+41
-117
@@ -1,40 +0,3 @@ | ||
| declare module 'Fraction'; | ||
| /** | ||
| * Interface representing a fraction with numerator and denominator. | ||
| */ | ||
| export interface NumeratorDenominator { | ||
| n: number | bigint; | ||
| d: number | bigint; | ||
| } | ||
| /** | ||
| * Type for handling multiple types of input for Fraction operations. | ||
| * Allows passing fractions, numbers, strings, or objects containing | ||
| * a numerator and denominator. | ||
| */ | ||
| type FractionInput = Fraction | number | string | [number | string, number | string] | NumeratorDenominator; | ||
| /** | ||
| * Function signature for Fraction operations like add, sub, mul, etc. | ||
| * Accepts various types of inputs and returns a Fraction. | ||
| */ | ||
| type FractionParam = { | ||
| (fraction: Fraction): Fraction; | ||
| (num: number | bigint | string): Fraction; | ||
| (numerator: number | bigint, denominator: number | bigint): Fraction; | ||
| (numbers: [number | bigint | string, number | bigint | string]): Fraction; | ||
| (fraction: NumeratorDenominator): Fraction; | ||
| (firstValue: FractionInput): Fraction; | ||
| }; | ||
| /** | ||
| * Fraction class representing a rational number with numerator and denominator. | ||
| * Supports arithmetic, comparison, and formatting operations. | ||
| */ | ||
| export class Fraction { | ||
| /** | ||
| * Constructs a new Fraction instance. | ||
| * Can accept another Fraction, a number, a string, a tuple, or an object with numerator and denominator. | ||
| */ | ||
| declare class Fraction { | ||
| constructor(fraction: Fraction); | ||
@@ -44,118 +7,79 @@ constructor(num: number | bigint | string); | ||
| constructor(numbers: [number | bigint | string, number | bigint | string]); | ||
| constructor(fraction: NumeratorDenominator); | ||
| constructor(firstValue: FractionInput, secondValue?: number); | ||
| constructor(fraction: Fraction.NumeratorDenominator); | ||
| constructor(firstValue: Fraction.FractionInput, secondValue?: number); | ||
| /** The sign of the fraction (-1 or 1) */ | ||
| s: bigint; | ||
| /** The numerator of the fraction */ | ||
| n: bigint; | ||
| /** The denominator of the fraction */ | ||
| d: bigint; | ||
| /** Returns the absolute value of the fraction */ | ||
| abs(): Fraction; | ||
| /** Negates the fraction */ | ||
| neg(): Fraction; | ||
| /** Adds a fraction or number */ | ||
| add: FractionParam; | ||
| add: Fraction.FractionParam; | ||
| sub: Fraction.FractionParam; | ||
| mul: Fraction.FractionParam; | ||
| div: Fraction.FractionParam; | ||
| pow: Fraction.FractionParam; | ||
| log: Fraction.FractionParam; | ||
| gcd: Fraction.FractionParam; | ||
| lcm: Fraction.FractionParam; | ||
| /** Subtracts a fraction or number */ | ||
| sub: FractionParam; | ||
| /** Multiplies by a fraction or number */ | ||
| mul: FractionParam; | ||
| /** Divides by a fraction or number */ | ||
| div: FractionParam; | ||
| /** Raises the fraction to a given power */ | ||
| pow: FractionParam; | ||
| /** Calculates the logarithm of a fraction to a given rational base */ | ||
| log: FractionParam; | ||
| /** Finds the greatest common divisor (gcd) with another fraction or number */ | ||
| gcd: FractionParam; | ||
| /** Finds the least common multiple (lcm) with another fraction or number */ | ||
| lcm: FractionParam; | ||
| /** | ||
| * Returns the modulo of the fraction with respect to another fraction or number. | ||
| * If no argument is passed, it returns the fraction modulo 1. | ||
| */ | ||
| mod(n?: number | bigint | string | Fraction): Fraction; | ||
| /** Rounds the fraction up to the nearest integer, optionally specifying the number of decimal places */ | ||
| ceil(places?: number): Fraction; | ||
| /** Rounds the fraction down to the nearest integer, optionally specifying the number of decimal places */ | ||
| floor(places?: number): Fraction; | ||
| /** Rounds the fraction to the nearest integer, optionally specifying the number of decimal places */ | ||
| round(places?: number): Fraction; | ||
| roundTo: Fraction.FractionParam; | ||
| /** Rounds the fraction to the nearest multiple of another fraction or number */ | ||
| roundTo: FractionParam; | ||
| /** Returns the inverse of the fraction (numerator and denominator swapped) */ | ||
| inverse(): Fraction; | ||
| /** | ||
| * Simplifies the fraction to its simplest form. | ||
| * Optionally accepts an epsilon value for controlling precision in approximation. | ||
| */ | ||
| simplify(eps?: number): Fraction; | ||
| /** Checks if two fractions or numbers are equal */ | ||
| equals(n: number | bigint | string | Fraction): boolean; | ||
| /** Check if this rational number is less than another */ | ||
| lt(n: number | bigint | string | Fraction): boolean; | ||
| /** Check if this rational number is less than or equal another */ | ||
| lte(n: number | bigint | string | Fraction): boolean; | ||
| /** Check if this rational number is greater than another */ | ||
| gt(n: number | bigint | string | Fraction): boolean; | ||
| /** Check if this rational number is greater than or equal another */ | ||
| gte(n: number | bigint | string | Fraction): boolean; | ||
| /** Compares two fractions or numbers. Returns -1, 0, or 1 based on the comparison. */ | ||
| compare(n: number | bigint | string | Fraction): number; | ||
| /** Checks if the fraction is divisible by another fraction or number */ | ||
| divisible(n: number | bigint | string | Fraction): boolean; | ||
| /** Returns the decimal representation of the fraction */ | ||
| valueOf(): number; | ||
| toString(decimalPlaces?: number): string; | ||
| toLatex(showMixed?: boolean): string; | ||
| toFraction(showMixed?: boolean): string; | ||
| toContinued(): bigint[]; | ||
| clone(): Fraction; | ||
| } | ||
| declare namespace Fraction { | ||
| /** | ||
| * Returns a string representation of the fraction. | ||
| * Optionally specifies the number of decimal places. | ||
| * Interface representing a fraction with numerator and denominator. | ||
| */ | ||
| toString(decimalPlaces?: number): string; | ||
| interface NumeratorDenominator { | ||
| n: number | bigint; | ||
| d: number | bigint; | ||
| } | ||
| /** | ||
| * Returns a LaTeX string representing the fraction. | ||
| * Optionally excludes the whole part in the LaTeX string. | ||
| * Type for handling multiple types of input for Fraction operations. | ||
| */ | ||
| toLatex(showMixed?: boolean): string; | ||
| type FractionInput = | ||
| | Fraction | ||
| | number | ||
| | string | ||
| | [number | string, number | string] | ||
| | NumeratorDenominator; | ||
| /** | ||
| * Returns a string representing the fraction in fraction format. | ||
| * Optionally excludes the whole part of the fraction. | ||
| * Function signature for Fraction operations like add, sub, mul, etc. | ||
| */ | ||
| toFraction(showMixed?: boolean): string; | ||
| /** Returns an array representing the continued fraction form of the fraction */ | ||
| toContinued(): bigint[]; | ||
| /** Returns a clone of the current fraction */ | ||
| clone(): Fraction; | ||
| type FractionParam = { | ||
| (fraction: Fraction): Fraction; | ||
| (num: number | bigint | string): Fraction; | ||
| (numerator: number | bigint, denominator: number | bigint): Fraction; | ||
| (numbers: [number | bigint | string, number | bigint | string]): Fraction; | ||
| (fraction: NumeratorDenominator): Fraction; | ||
| (firstValue: FractionInput): Fraction; | ||
| }; | ||
| } | ||
| export default Fraction; | ||
| export = Fraction; |
+30
-9
| { | ||
| "name": "fraction.js", | ||
| "title": "Fraction.js", | ||
| "version": "5.2.2", | ||
| "version": "5.3.0", | ||
| "description": "The RAW rational numbers library", | ||
| "homepage": "https://raw.org/article/rational-numbers-in-javascript/", | ||
| "bugs": "https://github.com/rawify/Fraction.js/issues", | ||
| "description": "A rational numbers library", | ||
| "keywords": [ | ||
@@ -29,16 +29,37 @@ "math", | ||
| "module": "./dist/fraction.mjs", | ||
| "types": "./fraction.d.ts", | ||
| "browser": "./dist/fraction.min.js", | ||
| "unpkg": "./dist/fraction.min.js", | ||
| "readmeFilename": "README.md", | ||
| "types": "./fraction.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./fraction.d.ts", | ||
| "require": "./dist/fraction.js", | ||
| "import": "./dist/fraction.mjs" | ||
| "import": { | ||
| "types": "./fraction.d.mts", | ||
| "default": "./dist/fraction.mjs" | ||
| }, | ||
| "require": { | ||
| "types": "./fraction.d.ts", | ||
| "default": "./dist/fraction.js" | ||
| }, | ||
| "browser": { | ||
| "types": "./fraction.d.ts", | ||
| "default": "./dist/fraction.min.js" | ||
| }, | ||
| "default": { | ||
| "types": "./fraction.d.ts", | ||
| "default": "./dist/fraction.js" | ||
| } | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "typesVersions": { | ||
| "<4.7": { | ||
| "*": [ | ||
| "fraction.d.ts" | ||
| ] | ||
| } | ||
| }, | ||
| "sideEffects": false, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git@github.com:rawify/Fraction.js.git" | ||
| "url": "git+ssh://git@github.com/rawify/Fraction.js.git" | ||
| }, | ||
@@ -56,3 +77,3 @@ "funding": { | ||
| "engines": { | ||
| "node": ">= 12" | ||
| "node": "*" | ||
| }, | ||
@@ -59,0 +80,0 @@ "directories": { |
+29
-16
@@ -12,10 +12,4 @@ # Fraction.js - ℚ in JavaScript | ||
| For applications requiring higher precision or where working with fractions is preferable, consider incorporating *Fraction.js* into your project. Integration is straightforward: | ||
| For applications requiring higher precision or where working with fractions is preferable, consider incorporating *Fraction.js* into your project. | ||
| ```javascript | ||
| import Fraction from 'fraction.js'; | ||
| // Alternatively | ||
| var Fraction = require('fraction.js'); | ||
| ``` | ||
| The library effectively addresses precision issues, as demonstrated below: | ||
@@ -27,3 +21,3 @@ | ||
| *Fraction.js* uses a `BigInt` representation for both the numerator and denominator, ensuring minimal performance overhead while maximizing accuracy. Its design is optimized for precision, making it an ideal choice as a foundational library for other math tools, such as [Polynomial.js](https://github.com/infusion/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs). | ||
| *Fraction.js* uses a `BigInt` representation for both the numerator and denominator, ensuring minimal performance overhead while maximizing accuracy. Its design is optimized for precision, making it an ideal choice as a foundational library for other math tools, such as [Polynomial.js](https://github.com/rawify/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs). | ||
@@ -462,3 +456,3 @@ ## Convert Decimal to Fraction | ||
| Installing fraction.js is as easy as cloning this repo or use the following command: | ||
| You can install `Fraction.js` via npm: | ||
@@ -469,19 +463,38 @@ ```bash | ||
| ## Using Fraction.js with the browser | ||
| Or with yarn: | ||
| ```bash | ||
| yarn add fraction.js | ||
| ``` | ||
| Alternatively, download or clone the repository: | ||
| ```bash | ||
| git clone https://github.com/rawify/Fraction.js | ||
| ``` | ||
| ## Usage | ||
| Include the `fraction.min.js` file in your project: | ||
| ```html | ||
| <script src="fraction.min.js"></script> | ||
| <script src="path/to/fraction.min.js"></script> | ||
| <script> | ||
| console.log(Fraction("123/456")); | ||
| var x = new Fraction("13/4"); | ||
| </script> | ||
| ``` | ||
| ## Using Fraction.js with TypeScript | ||
| Or in a Node.js project: | ||
| ```js | ||
| import Fraction from "fraction.js"; | ||
| console.log(Fraction("123/456")); | ||
| ```javascript | ||
| const Fraction = require('fraction.js'); | ||
| ``` | ||
| or | ||
| ```javascript | ||
| import Fraction from 'fraction.js'; | ||
| ``` | ||
| ## Coding Style | ||
@@ -488,0 +501,0 @@ |
+61
-56
| /** | ||
| * @license Fraction.js v5.2.2 3/30/2025 | ||
| * @license Fraction.js v5.3.0 8/16/2025 | ||
| * https://raw.org/article/rational-numbers-in-javascript/ | ||
@@ -44,4 +44,6 @@ * | ||
| const C_TWO = BigInt(2); | ||
| const C_THREE = BigInt(3); | ||
| const C_FIVE = BigInt(5); | ||
| const C_TEN = BigInt(10); | ||
| const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); | ||
@@ -70,3 +72,3 @@ // Maximum search depth for cyclic rational numbers. 2000 should be more than enough. | ||
| function trunc(x) { | ||
| function ifloor(x) { | ||
| return typeof x === 'bigint' ? x : Math.floor(x); | ||
@@ -94,25 +96,25 @@ } | ||
| function factorize(num) { | ||
| 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 | ||
| function factorize(n) { | ||
| const factors = {}; | ||
| const factors = Object.create(null); | ||
| if (n <= C_ONE) { | ||
| factors[n] = C_ONE; | ||
| return factors; | ||
| } | ||
| let n = num; | ||
| let i = C_TWO; | ||
| let s = C_FIVE - C_ONE; | ||
| const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; }; | ||
| while (s <= n) { | ||
| while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; } | ||
| while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; } | ||
| while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; } | ||
| while (n % i === C_ZERO) { | ||
| n /= i; | ||
| factors[i] = (factors[i] || C_ZERO) + C_ONE; | ||
| } | ||
| s += C_ONE + C_TWO * i++; | ||
| // 30-wheel trial division: test only residues coprime to 2*3*5 | ||
| // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ... | ||
| for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) { | ||
| while (n % p === C_ZERO) { add(p); n /= p; } | ||
| p += FACTORSTEPS[si]; | ||
| si = (si + 1) & 7; // fast modulo 8 | ||
| } | ||
| if (n !== num) { | ||
| if (n > 1) | ||
| factors[n] = (factors[n] || C_ZERO) + C_ONE; | ||
| } else { | ||
| factors[num] = (factors[num] || C_ZERO) + C_ONE; | ||
| } | ||
| if (n > C_ONE) add(n); | ||
| return factors; | ||
@@ -409,5 +411,5 @@ } | ||
| var DivisionByZero = function () { return new Error("Division by Zero"); }; | ||
| var InvalidParameter = function () { return new Error("Invalid argument"); }; | ||
| var NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; | ||
| const DivisionByZero = function () { return new Error("Division by Zero"); }; | ||
| const InvalidParameter = function () { return new Error("Invalid argument"); }; | ||
| const NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; | ||
@@ -656,3 +658,3 @@ Fraction.prototype = { | ||
| const allPrimes = {}; | ||
| const allPrimes = Object.create(null); | ||
@@ -800,3 +802,3 @@ const baseFactors = factorize(P['n']); | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) + | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + | ||
| (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO), | ||
@@ -815,3 +817,3 @@ places); | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) - | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) - | ||
| (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO), | ||
@@ -833,15 +835,15 @@ places); | ||
| s >= 0: | ||
| round(n / d) = trunc(n / d) + (n % d) / d >= 0.5 ? 1 : 0 | ||
| = trunc(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0 | ||
| = ifloor(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| s < 0: | ||
| round(n / d) =-trunc(n / d) - (n % d) / d > 0.5 ? 1 : 0 | ||
| =-trunc(n / d) - 2(n % d) > d ? 1 : 0 | ||
| round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0 | ||
| =-ifloor(n / d) - 2(n % d) > d ? 1 : 0 | ||
| =>: | ||
| round(s * n / d) = s * trunc(n / d) + s * (C + 2(n % d) > d ? 1 : 0) | ||
| round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0) | ||
| where C = s >= 0 ? 1 : 0, to fix the >= for the positve case. | ||
| */ | ||
| return newFraction(trunc(this["s"] * places * this["n"] / this["d"]) + | ||
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + | ||
| this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO), | ||
@@ -871,4 +873,4 @@ places); | ||
| // round(n / d) = trunc(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| let k = trunc(n / d); | ||
| // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0 | ||
| let k = ifloor(n / d); | ||
| if (r + r >= d) { | ||
@@ -888,3 +890,4 @@ k++; | ||
| parse(a, b); | ||
| return !(!(P["n"] * this["d"]) || ((this["n"] * P["d"]) % (P["n"] * this["d"]))); | ||
| if (P['n'] === C_ZERO) return false; | ||
| return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO; | ||
| }, | ||
@@ -898,4 +901,5 @@ | ||
| 'valueOf': function () { | ||
| // Best we can do so far | ||
| return Number(this["s"] * this["n"]) / Number(this["d"]); | ||
| //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) { | ||
| return Number(this['s'] * this['n']) / Number(this['d']); | ||
| //} | ||
| }, | ||
@@ -908,3 +912,3 @@ | ||
| **/ | ||
| 'toString': function (dec) { | ||
| 'toString': function (dec = 15) { | ||
@@ -914,4 +918,2 @@ let N = this["n"]; | ||
| dec = dec || 15; // 15 = decimal places when no repetition | ||
| let cycLen = cycleLen(N, D); // Cycle length | ||
@@ -923,3 +925,3 @@ let cycOff = cycleStart(N, D, cycLen); // Cycle start | ||
| // Append integer part | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
@@ -935,3 +937,3 @@ N %= D; | ||
| for (let i = cycOff; i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -942,3 +944,3 @@ N *= C_TEN; | ||
| for (let i = cycLen; i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -950,3 +952,3 @@ N *= C_TEN; | ||
| for (let i = dec; N && i--;) { | ||
| str += trunc(N / D); | ||
| str += ifloor(N / D); | ||
| N %= D; | ||
@@ -964,3 +966,3 @@ N *= C_TEN; | ||
| **/ | ||
| 'toFraction': function (showMixed) { | ||
| 'toFraction': function (showMixed = false) { | ||
@@ -974,3 +976,3 @@ let n = this["n"]; | ||
| } else { | ||
| let whole = trunc(n / d); | ||
| const whole = ifloor(n / d); | ||
| if (showMixed && whole > C_ZERO) { | ||
@@ -994,3 +996,3 @@ str += whole; | ||
| **/ | ||
| 'toLatex': function (showMixed) { | ||
| 'toLatex': function (showMixed = false) { | ||
@@ -1004,3 +1006,3 @@ let n = this["n"]; | ||
| } else { | ||
| let whole = trunc(n / d); | ||
| const whole = ifloor(n / d); | ||
| if (showMixed && whole > C_ZERO) { | ||
@@ -1029,18 +1031,21 @@ str += whole; | ||
| let b = this['d']; | ||
| let res = []; | ||
| const res = []; | ||
| do { | ||
| res.push(trunc(a / b)); | ||
| let t = a % b; | ||
| while (b) { | ||
| res.push(ifloor(a / b)); | ||
| const t = a % b; | ||
| a = b; | ||
| b = t; | ||
| } while (a !== C_ONE); | ||
| } | ||
| return res; | ||
| }, | ||
| "simplify": function (eps) { | ||
| "simplify": function (eps = 1e-3) { | ||
| const ieps = BigInt(1 / (eps || 0.001) | 0); | ||
| // Continued fractions give best approximations for a max denominator, | ||
| // generally outperforming mediants in denominator–accuracy trade-offs. | ||
| // Semiconvergents can further reduce the denominator within tolerance. | ||
| const ieps = BigInt(Math.ceil(1 / eps)); | ||
| const thisABS = this['abs'](); | ||
@@ -1047,0 +1052,0 @@ const cont = thisABS['toContinued'](); |
@@ -1136,2 +1136,20 @@ const Fraction = require('fraction.js'); | ||
| }, { | ||
| label: "(-8/27)^(1/3)", | ||
| set: [-8, 27], | ||
| fn: "pow", | ||
| param: [1, 3], | ||
| expect: "null" | ||
| }, { | ||
| label: "(-8/27)^(2/3)", | ||
| set: [-8, 27], | ||
| fn: "pow", | ||
| param: [2, 3], | ||
| expect: "null" | ||
| }, { | ||
| label: "(-32/243)^(5/3)", | ||
| set: [-32, 243], | ||
| fn: "pow", | ||
| param: [5, 3], | ||
| expect: "null" | ||
| }, { | ||
| label: "sqrt(0)", | ||
@@ -1138,0 +1156,0 @@ set: 0, |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
148856
2.08%21
5%521
2.56%4726
-0.25%