@rgsoft/math
Version:
Yet another JS math library
985 lines (972 loc) • 21.8 kB
JavaScript
// src/bases.ts
var alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
function toBase(n, b) {
if (!Number.isInteger(n) || n < 0) {
throw new Error("The number must be positive integer");
}
if (!Number.isInteger(b) || b <= 0) {
throw new Error("The base must be a positive integer");
}
if (b < 2) {
throw new Error("The min base is 2");
}
if (b > alphabet.length) {
throw new Error(`The max base allowed is ${alphabet.length}`);
}
let e = 1;
const digits = [];
while (n / (e * b) >= 1) {
e *= b;
}
do {
digits.push(alphabet[Math.floor(n / e)]);
n = n % e;
e /= b;
} while (e >= 1);
return digits.join("");
}
function fromBase(d, b) {
if (!Number.isInteger(b) || b <= 0) {
throw new Error("The base must be a positive integer");
}
if (b < 2) {
throw new Error("The min base is 2");
}
if (b > alphabet.length) {
throw new Error(`The max base allowed is ${alphabet.length}`);
}
const digits = d.split("").reverse();
return digits.reduce((prev, curr, e) => {
const n = alphabet.indexOf(curr);
if (n < 0) {
throw new Error(`Digit ${curr} not found`);
}
if (n >= b) {
throw new Error(`Invalid digit ${curr} for base ${b}`);
}
return prev + n * b ** e;
}, 0);
}
// src/combination.ts
function factorial(n, m = NaN) {
if (!Number.isInteger(n) || n < 0) {
throw new Error("The number must be a positive integer");
}
if (isNaN(m)) {
m = 1;
} else {
if (!Number.isInteger(m) || m < 0) {
throw new Error("The lower limit must be a positive integer");
}
if (n < m) {
throw new Error("The lower limit cannot be higher than the number");
}
}
if (n === 0) {
return 1;
}
let f = 1;
while (n >= m) {
f *= n;
n--;
}
return f;
}
function combination(n, k) {
if (!Number.isInteger(n) || n < 0) {
throw new Error("The number of elements must be a positive integer");
}
if (!Number.isInteger(k) || k < 0) {
throw new Error("The group quantity must be a positive integer");
}
if (k > n) {
throw new Error("The group quantity cannot be greater than the total elements");
}
if (k === 0 || k === n) {
return 1;
}
return factorial(n, Math.max(k + 1, n - k + 1)) / factorial(Math.min(k, n - k));
}
function permutation(n, k) {
if (!Number.isInteger(n) || n < 0) {
throw new Error("The number of elements must be a positive integer");
}
if (!Number.isInteger(k) || k < 0) {
throw new Error("The group quantity must be a positive integer");
}
if (k > n) {
throw new Error("The group quantity cannot be greater than the total elements");
}
if (k === 0) {
return 1;
}
return factorial(n, n - k + 1);
}
// src/complex.ts
var Complex = class _Complex {
constructor(a, b) {
this.a = a;
this.b = b;
return this;
}
/**
*
* @param { number | Complex} n
*/
add(n) {
let { a, b } = this;
if (typeof n === "number") {
a += n;
} else if (n instanceof _Complex) {
a += n.a;
b += n.b;
}
return new _Complex(a, b);
}
/**
*
* @param { number | Complex} n
*/
sub(n) {
let { a, b } = this;
if (typeof n === "number") {
a -= n;
} else if (n instanceof _Complex) {
a -= n.a;
b -= n.b;
}
return new _Complex(a, b);
}
/**
*
* @param { number | Complex} n
*/
mult(n) {
let { a, b } = this;
if (typeof n === "number") {
a *= n;
b *= n;
} else if (n instanceof _Complex) {
a = this.a * n.a - this.b * n.b;
b = this.a * n.b + this.b * n.a;
}
return new _Complex(a, b);
}
/**
*
* @param { number | Complex} n
*/
div(n) {
let { a, b } = this;
if (typeof n === "number") {
a /= n;
b /= n;
} else if (n instanceof _Complex) {
const d = n.a * n.a + n.b * n.b;
a = (this.a * n.a + this.b * n.b) / d;
b = (this.b * n.a - this.a * n.b) / d;
}
return new _Complex(a, b);
}
sqrt() {
let { a, b } = this;
const m = Math.sqrt(this.mag);
const phi = Math.atan2(this.b, this.a) * 0.5;
a = m * Math.cos(phi);
b = m * Math.sin(phi);
return new _Complex(a, b);
}
conjugate() {
return new _Complex(this.a, -1 * this.b);
}
toString() {
let str = "";
if (this.a !== 0) {
str += `${this.a}`;
}
if (this.b > 0) {
if (this.b === 1) {
str += ` + i`;
} else {
str += ` + ${this.b}i`;
}
} else if (this.b < 0) {
if (this.b === -1) {
str += ` - i`;
} else {
str += ` - ${Math.abs(this.b)}i`;
}
}
return str.trim();
}
/**
* @returns { number }
*/
get mag() {
if (this._mag === void 0) {
this._mag = Math.sqrt(this.a * this.a + this.b * this.b);
}
return this._mag;
}
};
// src/vector.ts
var Vector = class _Vector {
/**
*
* @param { number } x
* @param { number } y
*/
constructor(x, y) {
this._x = x;
this._y = y;
this.resetValues();
}
resetValues() {
this._mag = void 0;
this._angle = void 0;
}
/**
* @returns { number }
*/
get mag() {
if (this._mag === void 0) {
this._mag = Math.sqrt(this._x * this._x + this._y * this._y);
}
return this._mag;
}
/**
* @param { number } value
*/
set mag(value) {
if (value < 0) {
throw new Error("New magnitude must be positive");
}
this.normalize();
this.mult(value);
this._mag = value;
}
/**
* @returns { number }
*/
get angle() {
if (this._angle === void 0) {
if (this.mag > 0) {
this._angle = Math.atan2(this._y, this._x);
} else {
this._angle = 0;
}
}
return this._angle;
}
/**
* @returns { number }
*/
get x() {
return this._x;
}
/**
* @param { number } value
*/
set x(value) {
this._x = value;
this.resetValues();
}
/**
* @returns { number }
*/
get y() {
return this._y;
}
/**
* @param { number } value
*/
set y(value) {
this._y = value;
this.resetValues();
}
/**
*
* @returns { Vector }
*/
normalize() {
if (this.mag === 0) {
return this;
}
this._x /= this.mag;
this._y /= this.mag;
this.resetValues();
return this;
}
/**
*
* @param { number} num
* @returns { Vector }
*/
mult(num) {
this._x *= num;
this._y *= num;
this.resetValues();
return this;
}
/**
*
* @param { number} num
* @returns { Vector }
*/
div(num) {
this._x /= num;
this._y /= num;
this.resetValues();
return this;
}
/**
* @param {Vector} v
* @returns {number}
*/
dot(v) {
return this._x * v._x + this._y * v._y;
}
/**
*
* @param { Vector } vector
* @returns { Vector }
*/
add(vector) {
this._x += vector._x;
this._y += vector._y;
this.resetValues();
return this;
}
/**
*
* @param { Vector } vector
* @returns { Vector }
*/
sub(vector) {
this._x -= vector._x;
this._y -= vector._y;
this.resetValues();
return this;
}
/**
*
* @param { Vector } vector
* @returns { number }
*/
dist(vector) {
const dx = this._x - vector._x;
const dy = this._y - vector._y;
return Math.sqrt(dx * dx + dy * dy);
}
/**
*
* @param { Vector } vector
* @returns { boolean }
*/
equals(vector) {
return vector.x === this._x && vector.y === this.y;
}
/**
*
* @param { Vector } vector
* @returns { number }
*/
angleTo(vector) {
const cp = this.dot(vector);
if (this.mag === 0 || vector.mag === 0) {
return NaN;
}
return Math.acos(cp / (this.mag * vector.mag));
}
/**
* Calculates the projection on another vector
* @param { Vector } vector
* @returns { Vector }
*/
projection(vector) {
const cp = this.dot(vector);
const sqrtMag = vector.dot(vector);
const proj = vector.copy();
proj.mult(cp / sqrtMag);
return proj;
}
/**
* @returns { Vector }
*/
copy() {
return new _Vector(this._x, this._y);
}
transpose() {
const aux = this._y;
this._y = this._x;
this._x = aux;
this.resetValues();
}
/**
*
* @param { number } mag
*/
limit(mag) {
if (this.mag <= mag) {
return;
}
this.mag = mag;
}
/**
*
* @param { number } angle
* @returns { Vector }
*/
static fromAngle(angle) {
const instance = new _Vector(Math.cos(angle), Math.sin(angle));
instance._angle = angle;
return instance;
}
/**
*
* @param { Vector } v
* @param { Vector } w
* @returns { Vector }
*/
static add(v, w) {
const instance = v.copy();
return instance.add(w);
}
/**
*
* @param { Vector } v
* @param { Vector } w
* @returns { Vector }
*/
static sub(v, w) {
const instance = v.copy();
return instance.sub(w);
}
/**
*
* @param { Vector } v
* @param { number } n
* @returns { Vector }
*/
static mult(v, n) {
const instance = v.copy();
return instance.mult(n);
}
/**
*
* @param { Vector } v
* @param { number } n
* @returns { Vector }
*/
static div(v, n) {
const instance = v.copy();
return instance.div(n);
}
};
// src/line.ts
var Line = class _Line {
constructor(m, a) {
this.m = m;
this.a = a;
}
static fromPoints(p, q) {
const m = p.x === q.x ? NaN : (p.y - q.y) / (p.x - q.x);
const a = isNaN(m) ? p.x : -1 * m * p.x + p.y;
return new _Line(m, a);
}
static mediatrix(p, q) {
const a = q.copy();
a.sub(p);
a.mult(0.5);
const b = Vector.fromAngle(a.angle + Math.PI * 0.5);
a.add(p);
b.add(a);
return _Line.fromPoints(a, b);
}
/**
* Calculates a line intersection point with another
* @param { Line } line
* @returns { Vector }
*/
intersectionPoint(line) {
let x, y;
if (this.m === line.m) {
throw new Error("The slopes are equal");
}
if (isNaN(this.m)) {
x = this.a;
y = line.m * x + line.a;
} else if (isNaN(line.m)) {
x = line.a;
y = this.m * x + this.a;
} else {
x = (line.a - this.a) / (this.m - line.m);
y = this.m * x + this.a;
}
return new Vector(x, y);
}
};
// src/number.ts
function mod(n, m) {
if (!Number.isInteger(n)) {
throw new Error("The number must be an integer");
}
if (!Number.isInteger(m) || m <= 0) {
throw new Error("Modulo must be a positive integer");
}
return (n % m + m) % m;
}
function gcd(a, b) {
if (!Number.isInteger(a) || a <= 0) {
throw new Error("Both numbers must be positive integers");
}
if (!Number.isInteger(b) || b <= 0) {
throw new Error("Both numbers must be positive integers");
}
if (a === b) {
return a;
}
let d;
do {
d = Math.abs(a - b);
a = Math.min(a, b);
b = d;
} while (d !== a && d > 0);
return d;
}
function lcm(a, b) {
if (!Number.isInteger(a) || a <= 0) {
throw new Error("Both numbers must be positive integers");
}
if (!Number.isInteger(b) || b <= 0) {
throw new Error("Both numbers must be positive integers");
}
const d = gcd(a, b);
return a * b / d;
}
function prime(n) {
if (!Number.isInteger(n) || n <= 0) {
throw new Error("The number must be positive integer");
}
const l = Math.sqrt(n);
for (let i = 2; i <= l; i++) {
if (n % i === 0) {
return false;
}
}
return true;
}
function factors(n) {
if (!Number.isInteger(n) || n <= 0) {
throw new Error("The number must be positive integer");
}
if (n === 1) {
return [[1, 1]];
}
const factors2 = [];
let i = 2;
let factor = [i, 0];
while (n > 1) {
if (n % i === 0) {
n /= i;
factor[1]++;
} else {
i++;
if (factor[1] > 0) {
factors2.push(factor);
}
factor = [i, 0];
}
}
if (factor[1] > 0) {
factors2.push(factor);
}
return factors2;
}
function totient(m) {
if (!Number.isInteger(m) || m <= 0) {
throw new Error("The number must be positive integer");
}
const factorization = factors(m);
let a = 1;
let b = 1;
factorization.forEach((f) => {
a *= f[0];
b *= f[0] - 1;
});
return b * m / a;
}
function collatz(n, limit = 1e4) {
if (!Number.isInteger(n) || n <= 0) {
throw new Error("The number must be positive integer");
}
const seq = [n];
for (let i = 1; i < limit; i++) {
if (n === 1) {
break;
}
if (n % 2 === 0) {
n = n * 0.5;
} else {
n = 3 * n + 1;
}
seq.push(n);
}
return seq;
}
function digitalRoots(n) {
if (!Number.isInteger(n) || n <= 0) {
throw new Error("The number must be positive integer");
}
if (n < 10) {
return n;
}
do {
let chars = n.toString().split("");
n = chars.reduce((prev, c) => prev += Number(c), 0);
} while (n >= 10);
return n;
}
// src/pdf/erf.ts
function erf(z) {
const t = 1 / (1 + 0.5 * Math.abs(z));
const tau = t * Math.exp(
-z * z - 1.26551223 + 1.00002368 * t + 0.37409196 * t ** 2 + 0.09678418 * t ** 3 - 0.18628806 * t ** 4 + 0.27886807 * t ** 5 - 1.13520398 * t ** 6 + 1.48851587 * t ** 7 - 0.82215223 * t ** 8 + 0.17087277 * t ** 9
);
return z >= 0 ? 1 - tau : tau - 1;
}
// src/pdf/exponential.ts
var Exponential = class {
constructor(mean) {
this.mean = mean;
this.m = 1 / mean;
}
getAccumulated(x) {
if (x < 0) {
return 0;
}
return 1 - Math.exp(-this.m * x);
}
getValue() {
const p = Math.random();
if (p === 1) {
return Infinity;
}
return -1 * Math.log(1 - p) * this.mean;
}
getMean() {
return this.mean;
}
};
// src/pdf/gaussian.ts
var Gaussian = class {
constructor(m = 0, v = 1) {
this.m = m;
this.v = v;
if (v <= 0) {
throw new Error("Variance must be positive");
}
this.stdDev = Math.sqrt(v);
}
getAccumulated(x) {
const z = (x - this.m) / Math.sqrt(2 * this.v);
return 0.5 * (1 + erf(z));
}
inverseCFD(p) {
if (p < 0 || p > 1) {
throw new Error("Invalid value for p");
}
if (p === 0) return -Infinity;
if (p === 1) return Infinity;
const a = [-39.6968302866538, 220.946098424521, -275.928510446969, 138.357751867269, -30.6647980661472, 2.50662827745924];
const b = [-54.4760987982241, 161.585836858041, -155.698979859887, 66.8013118877197, -13.2806815528857];
const c = [-0.00778489400243029, -0.322396458041136, -2.40075827716184, -2.54973253934373, 4.37466414146497, 2.93816398269878];
const d = [0.00778469570904146, 0.32246712907004, 2.445134137143, 3.75440866190742];
let q, r;
if (p < 0.02425) {
q = Math.sqrt(-2 * Math.log(p));
return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
} else if (p > 1 - 0.02425) {
q = Math.sqrt(-2 * Math.log(1 - p));
return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
} else {
q = p - 0.5;
r = q * q;
return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
}
}
getMean() {
return this.m;
}
getValue() {
const p = Math.random();
const z = this.inverseCFD(p);
return this.m + z * this.stdDev;
}
};
// src/pdf/uniform.ts
var Uniform = class {
constructor(min, max) {
this.min = min;
this.max = max;
if (min > max) {
throw new Error("Min cannot be greater than max");
}
}
getAccumulated(x) {
if (x < this.min) {
return 0;
}
if (x > this.max) {
return 1;
}
return (x - this.min) / (this.max - this.min);
}
getMean() {
return (this.max + this.min) * 0.5;
}
getValue() {
return Math.random() * (this.max - this.min) + this.min;
}
};
// src/pmf/binomial.ts
var Binomial = class {
constructor(n, p) {
this.n = n;
this.p = p;
if (!Number.isInteger(n) || n < 1) {
throw new Error("The number of experiments must be a positive integer");
}
if (!(p >= 0 && p <= 1)) {
throw new Error("The success probability must be between 0 and 1");
}
this.probabilities = Array(n + 1).fill(0).map((_, i) => combination(n, i) * p ** i * (1 - p) ** (n - i));
let acc = 0;
this.accumulative = this.probabilities.map((p2) => {
acc += p2;
return acc;
});
}
getAccumulated(x) {
x = Math.floor(x);
if (isNaN(x)) {
throw new Error("Number expected");
}
if (x < 0) {
return 0;
}
if (x > this.n) {
return 1;
}
return this.accumulative[x];
}
getValue() {
const rnd = Math.random();
return this.accumulative.findIndex((acc) => rnd < acc);
}
probability(x) {
if (!Number.isInteger(x) || x < 0) {
throw new Error("The value must be a positive integer");
}
if (x > this.n) {
throw new Error("The value cannot ve larger than the experiments number");
}
return this.probabilities[x];
}
};
// src/pmf/negative-binomial.ts
var NegativeBinomial = class {
constructor(r, p) {
this.r = r;
this.p = p;
this.accumulative = [];
if (!Number.isInteger(r) || r < 1) {
throw new Error("The number of experiments must be a positive integer");
}
if (!(p >= 0 && p <= 1)) {
throw new Error("The success probability must be between 0 and 1");
}
}
getAccumulated(x) {
x = Math.floor(x);
if (isNaN(x)) {
throw new Error("Number expected");
}
if (x < 0) {
return 0;
}
if (this.accumulative.length > x) {
return this.accumulative[x];
}
let i = this.accumulative.length;
let acc = i > 0 ? this.accumulative[i - 1] : 0;
do {
acc += this.probability(i);
this.accumulative.push(acc);
i++;
} while (i <= x);
return this.accumulative[x];
}
getValue() {
const rnd = Math.random();
let i = 0;
while (i < this.accumulative.length) {
if (rnd < this.accumulative[i]) {
return i;
}
i++;
}
let acc = i > 0 ? this.accumulative[i - 1] : 0;
do {
acc += this.probability(i);
this.accumulative.push(acc);
i++;
} while (rnd > acc);
return i - 1;
}
probability(x) {
if (!Number.isInteger(x) || x < 0) {
throw new Error("The value must be a positive integer");
}
return combination(this.r + x - 1, x) * this.p ** this.r * (1 - this.p) ** x;
}
};
// src/pmf/poisson.ts
var Poisson = class {
constructor(l) {
this.l = l;
this.accumulative = [];
if (!Number.isInteger(l) || l < 0) {
throw new Error("The number of experiments must be a positive integer");
}
}
getAccumulated(x) {
x = Math.floor(x);
if (isNaN(x)) {
throw new Error("Number expected");
}
if (x < 0) {
return 0;
}
if (this.accumulative.length > x) {
return this.accumulative[x];
}
let i = this.accumulative.length;
let acc = i > 0 ? this.accumulative[i - 1] : 0;
do {
acc += this.probability(i);
this.accumulative.push(acc);
i++;
} while (i <= x);
return this.accumulative[x];
}
getValue() {
const rnd = Math.random();
let i = 0;
while (i < this.accumulative.length) {
if (rnd < this.accumulative[i]) {
return i;
}
i++;
}
let acc = i > 0 ? this.accumulative[i - 1] : 0;
do {
acc += this.probability(i);
this.accumulative.push(acc);
i++;
} while (rnd > acc);
return i - 1;
}
probability(x) {
if (!Number.isInteger(x) || x < 0) {
throw new Error("The value must be a positive integer");
}
return this.l ** x * Math.exp(-this.l) / factorial(x);
}
};
// src/segment.ts
var Segment = class {
constructor(p, q) {
this.p = p;
this.q = q;
}
/**
*
* @param { Vector } r
* @returns { boolean }
*/
isOnSegment(r) {
return (this.q.x - this.p.x) * (r.y - this.p.y) === (this.q.y - this.p.y) * (r.x - this.p.x);
}
/**
* To find orientation of ordered triplet (p, q, r).
* The function returns following values
* 0 when p, q and r are collinear; -1 when clockwise, 1 counterclockwise
*
* @param { Vector } p
* @param { Vector } q
* @param { Vector } r
* @returns { number }
*/
getOrientation(p, q, r) {
let val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
if (val == 0) return 0;
return val > 0 ? -1 : 1;
}
/**
*
* Returns true if intersects with this segment
*
* @param { Segment } segment
* @returns { boolean }
*/
intersects(segment) {
let o1 = this.getOrientation(this.p, this.q, segment.p);
let o2 = this.getOrientation(this.p, this.q, segment.q);
let o3 = this.getOrientation(segment.p, segment.q, this.p);
let o4 = this.getOrientation(segment.p, segment.q, this.q);
if (o1 != o2 && o3 != o4) {
return true;
}
if (o1 == 0 && this.isOnSegment(segment.p)) {
return true;
}
if (o2 == 0 && this.isOnSegment(segment.q)) {
return true;
}
if (o3 == 0 && segment.isOnSegment(this.p)) {
return true;
}
if (o4 == 0 && segment.isOnSegment(this.q)) {
return true;
}
return false;
}
};
export {
Binomial,
Complex,
Exponential,
Gaussian,
Line,
NegativeBinomial,
Poisson,
Segment,
Uniform,
Vector,
collatz,
combination,
digitalRoots,
erf,
factorial,
factors,
fromBase,
gcd,
lcm,
mod,
permutation,
prime,
toBase,
totient
};
//# sourceMappingURL=index.mjs.map