@cortex-js/compute-engine
Version:
Symbolic computing and numeric evaluations for JavaScript and Node.js
1,427 lines (1,421 loc) • 439 kB
JavaScript
/** Interval 0.58.0 */
(function(global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Interval = {}));})(this, (function (exports) { 'use strict';
var Interval = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
// src/interval.ts
var interval_exports = {};
__export(interval_exports, {
IntervalArithmetic: () => IntervalArithmetic,
_mul: () => _mul,
abs: () => abs,
acos: () => acos,
acosh: () => acosh,
acot: () => acot,
acoth: () => acoth,
acsc: () => acsc,
acsch: () => acsch,
add: () => add,
and: () => and,
asec: () => asec,
asech: () => asech,
asin: () => asin,
asinh: () => asinh,
atan: () => atan,
atan2: () => atan2,
atanh: () => atanh,
binomial: () => binomial,
ceil: () => ceil,
chop: () => chop2,
clamp: () => clamp,
containsExtremum: () => containsExtremum,
containsZero: () => containsZero,
cos: () => cos,
cosh: () => cosh2,
cot: () => cot,
coth: () => coth,
csc: () => csc,
csch: () => csch,
div: () => div,
equal: () => equal,
erf: () => erf2,
erfc: () => erfc2,
exp: () => exp,
exp2: () => exp2,
factorial: () => factorial3,
factorial2: () => factorial22,
floor: () => floor,
fract: () => fract,
fresnelC: () => fresnelC2,
fresnelS: () => fresnelS2,
gamma: () => gamma2,
gammaln: () => gammaln2,
gcd: () => gcd3,
getValue: () => getValue,
greater: () => greater,
greaterEqual: () => greaterEqual,
heaviside: () => heaviside,
hypot: () => hypot2,
isNegative: () => isNegative,
isNonNegative: () => isNonNegative,
isNonPositive: () => isNonPositive,
isPoint: () => isPoint,
isPositive: () => isPositive,
lcm: () => lcm3,
less: () => less,
lessEqual: () => lessEqual,
ln: () => ln,
log10: () => log10,
log2: () => log2,
max: () => max,
mergeDomainClip: () => mergeDomainClip,
midpoint: () => midpoint,
min: () => min,
mod: () => mod,
mul: () => mul,
negate: () => negate,
not: () => not,
notEqual: () => notEqual,
ok: () => ok,
or: () => or,
piecewise: () => piecewise,
point: () => point,
pow: () => pow,
powInterval: () => powInterval,
remainder: () => remainder,
round: () => round,
sec: () => sec,
sech: () => sech,
sign: () => sign,
sin: () => sin,
sinc: () => sinc,
sinh: () => sinh2,
sqrt: () => sqrt,
square: () => square,
sub: () => sub,
tan: () => tan,
tanh: () => tanh,
trunc: () => trunc,
unionResults: () => unionResults,
unwrap: () => unwrap,
unwrapOrPropagate: () => unwrapOrPropagate,
version: () => version,
width: () => width
});
// src/compute-engine/interval/util.ts
function ok(value) {
return { kind: "interval", value };
}
function point(n) {
return { lo: n, hi: n };
}
function containsExtremum(x, extremum, period) {
const n = Math.ceil((x.lo - extremum) / period);
const candidate = extremum + n * period;
const EPS = 1e-15;
return candidate >= x.lo - EPS && candidate <= x.hi + EPS;
}
function unionResults(a, b) {
if (a.kind === "empty") return b;
if (b.kind === "empty") return a;
if (a.kind === "singular" || b.kind === "singular") {
return { kind: "singular" };
}
if (a.kind === "entire" || b.kind === "entire") {
return { kind: "entire" };
}
const aVal = a.value;
const bVal = b.value;
const aDomainClip = a.kind === "partial" ? a.domainClipped : null;
const bDomainClip = b.kind === "partial" ? b.domainClipped : null;
const value = {
lo: Math.min(aVal.lo, bVal.lo),
hi: Math.max(aVal.hi, bVal.hi)
};
if (aDomainClip || bDomainClip) {
const domainClipped = mergeDomainClip(aDomainClip, bDomainClip);
return { kind: "partial", value, domainClipped };
}
return { kind: "interval", value };
}
function mergeDomainClip(a, b) {
if (a === "both" || b === "both") return "both";
if (a === null) return b;
if (b === null) return a;
if (a === b) return a;
return "both";
}
function isPoint(x) {
return x.lo === x.hi;
}
function containsZero(x) {
return x.lo <= 0 && x.hi >= 0;
}
function isPositive(x) {
return x.lo > 0;
}
function isNegative(x) {
return x.hi < 0;
}
function isNonNegative(x) {
return x.lo >= 0;
}
function isNonPositive(x) {
return x.hi <= 0;
}
function width(x) {
return x.hi - x.lo;
}
function midpoint(x) {
return (x.lo + x.hi) / 2;
}
function getValue(result) {
if (result.kind === "interval" || result.kind === "partial") {
return result.value;
}
return void 0;
}
function unwrap(input) {
if ("kind" in input) {
if (input.kind === "interval" || input.kind === "partial") {
return input.value;
}
return void 0;
}
return input;
}
function unwrapOrPropagate(...inputs) {
const result = [];
for (const input of inputs) {
if ("kind" in input) {
if (input.kind === "empty") return { kind: "empty" };
if (input.kind === "entire") return { kind: "entire" };
if (input.kind === "singular") return input;
result.push(input.value);
} else {
result.push(input);
}
}
return result;
}
// src/compute-engine/interval/arithmetic.ts
function add(a, b) {
const unwrapped = unwrapOrPropagate(a, b);
if (!Array.isArray(unwrapped)) return unwrapped;
const [aVal, bVal] = unwrapped;
return ok({ lo: aVal.lo + bVal.lo, hi: aVal.hi + bVal.hi });
}
function sub(a, b) {
const unwrapped = unwrapOrPropagate(a, b);
if (!Array.isArray(unwrapped)) return unwrapped;
const [aVal, bVal] = unwrapped;
return ok({ lo: aVal.lo - bVal.hi, hi: aVal.hi - bVal.lo });
}
function negate(x) {
const unwrapped = unwrapOrPropagate(x);
if (!Array.isArray(unwrapped)) return unwrapped;
const [xVal] = unwrapped;
return ok({ lo: -xVal.hi, hi: -xVal.lo });
}
function _mul(a, b) {
const products = [a.lo * b.lo, a.lo * b.hi, a.hi * b.lo, a.hi * b.hi];
return { lo: Math.min(...products), hi: Math.max(...products) };
}
function mul(a, b) {
const unwrapped = unwrapOrPropagate(a, b);
if (!Array.isArray(unwrapped)) return unwrapped;
const [aVal, bVal] = unwrapped;
return ok(_mul(aVal, bVal));
}
function div(a, b) {
const unwrapped = unwrapOrPropagate(a, b);
if (!Array.isArray(unwrapped)) return unwrapped;
const [aVal, bVal] = unwrapped;
return _div(aVal, bVal);
}
function _div(a, b) {
if (b.lo > 0 || b.hi < 0) {
return ok(_mul(a, { lo: 1 / b.hi, hi: 1 / b.lo }));
}
if (b.lo < 0 && b.hi > 0) {
return { kind: "singular" };
}
if (b.lo === 0 && b.hi > 0) {
if (a.lo >= 0) {
return {
kind: "partial",
value: { lo: a.lo / b.hi, hi: Infinity },
domainClipped: "hi"
};
} else if (a.hi <= 0) {
return {
kind: "partial",
value: { lo: -Infinity, hi: a.hi / b.hi },
domainClipped: "lo"
};
} else {
return { kind: "entire" };
}
}
if (b.hi === 0 && b.lo < 0) {
if (a.lo >= 0) {
return {
kind: "partial",
value: { lo: -Infinity, hi: a.lo / b.lo },
domainClipped: "lo"
};
} else if (a.hi <= 0) {
return {
kind: "partial",
value: { lo: a.hi / b.lo, hi: Infinity },
domainClipped: "hi"
};
} else {
return { kind: "entire" };
}
}
return { kind: "empty" };
}
// src/big-decimal/utils.ts
var _pow10Cache = /* @__PURE__ */ new Map();
function pow10(n) {
if (n <= 100) {
let v = _pow10Cache.get(n);
if (v === void 0) {
v = 10n ** BigInt(n);
_pow10Cache.set(n, v);
}
return v;
}
return 10n ** BigInt(n);
}
function fpmul(a, b, scale) {
return a * b / scale;
}
function fpdiv(a, b, scale) {
return a * scale / b;
}
function fpsqrt(a, scale) {
if (a === 0n) return 0n;
if (a < 0n) throw new RangeError("fpsqrt: negative input");
let x;
const aNum = Number(a);
const scaleNum = Number(scale);
if (Number.isFinite(aNum) && Number.isFinite(scaleNum) && aNum > 0 && scaleNum > 0) {
const approx = Math.sqrt(aNum / scaleNum) * scaleNum;
if (Number.isFinite(approx) && approx > 0) {
x = BigInt(Math.floor(approx));
if (x === 0n) x = 1n;
} else {
x = digitBasedSeed(a, scale);
}
} else {
x = digitBasedSeed(a, scale);
}
const as = a * scale;
let prev;
do {
prev = x;
x = (x + as / x) / 2n;
} while (bigintAbs(x - prev) > 1n);
const next = (x + as / x) / 2n;
const diffX = bigintAbs(x * x - as);
const diffNext = bigintAbs(next * next - as);
return diffNext < diffX ? next : x;
}
function digitBasedSeed(a, scale) {
const LEAD = 15;
const digA = bigintDigits(a);
const shiftA = Math.max(0, digA - LEAD);
const leadA = Number(shiftA > 0 ? a / pow10(shiftA) : a);
const digS = bigintDigits(scale);
const shiftS = Math.max(0, digS - LEAD);
const leadS = Number(shiftS > 0 ? scale / pow10(shiftS) : scale);
const totalShift = shiftA + shiftS;
const halfShift = Math.floor(totalShift / 2);
let floatSeed = Math.sqrt(leadA * leadS);
if (totalShift % 2 !== 0) floatSeed *= 3.1622776601683795;
const seed = BigInt(Math.round(floatSeed)) * pow10(halfShift);
return seed > 0n ? seed : 1n;
}
function bigintAbs(n) {
return n < 0n ? -n : n;
}
function bigintDigits(n) {
if (n === 0n) return 1;
if (n < 0n) n = -n;
if (n < 0x20000000000000n) return Math.floor(Math.log10(Number(n))) + 1;
let bits = 0;
let tmp = n;
let high = 1;
while (tmp >> BigInt(high) > 0n) high *= 2;
for (let shift = high >> 1; shift >= 1; shift >>= 1) {
if (tmp >> BigInt(shift) > 0n) {
bits += shift;
tmp >>= BigInt(shift);
}
}
bits += 1;
const approx = Math.ceil(bits * 0.30102999566398);
if (n < pow10(approx - 1)) return approx - 1;
if (n >= pow10(approx)) return approx + 1;
return approx;
}
function fpexp(x, scale) {
if (x === 0n) return scale;
let k = 0;
let r = x;
const half = scale / 2n;
while (bigintAbs(r) > half) {
r = r / 2n;
k++;
}
let sum = scale;
let term = r;
sum += term;
for (let n = 2; ; n++) {
term = term * r / (BigInt(n) * scale);
if (bigintAbs(term) === 0n) break;
sum += term;
}
for (let i = 0; i < k; i++) {
sum = sum * sum / scale;
}
return sum;
}
function fpln(x, scale) {
if (x === scale) return 0n;
const xNum = Number(x);
const scaleNum = Number(scale);
let y;
let target = x;
let k = 0;
if (Number.isFinite(xNum) && Number.isFinite(scaleNum) && xNum > 0 && scaleNum > 0) {
const ratio = xNum / scaleNum;
if (Number.isFinite(ratio) && ratio > 0) {
const approx = Math.log(ratio);
if (Number.isFinite(approx)) {
y = BigInt(Math.round(approx * scaleNum));
} else {
y = estimateLnSeed(x, scale);
}
} else {
y = estimateLnSeed(x, scale);
}
} else {
target = x;
const twoScale = 2n * scale;
const halfScale = scale / 2n;
while (target > twoScale || target < halfScale) {
target = fpsqrt(target, scale);
k++;
}
y = estimateLnSeed(target, scale);
}
let prevAbsDelta = 0n;
for (let i = 0; i < 100; i++) {
const ey = fpexp(y, scale);
if (ey === 0n) {
y = y / 2n;
continue;
}
const yn = y + target * scale / ey - scale;
const absDelta = bigintAbs(yn - y);
if (absDelta <= 1n) break;
if (absDelta < 100000n && prevAbsDelta > 0n && prevAbsDelta < 100000n && absDelta * 4n >= prevAbsDelta)
break;
prevAbsDelta = absDelta;
y = yn;
}
for (let i = 0; i < k; i++) {
y = 2n * y;
}
return y;
}
function estimateLnSeed(x, scale) {
const xDigits = bigintDigits(x);
const scaleDigits = bigintDigits(scale);
const digitDiff = BigInt(xDigits - scaleDigits);
return digitDiff * 2302585n * scale / 1000000n;
}
var PI_DIGITS = "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745530506820349625245174939965143142980919065925093722169646151570985838741059788595977297549893016175392846813826868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388439045124413654976278079771569143599770012961608944169486855584840635";
var _fppiCache = null;
function fppi(scale) {
if (_fppiCache !== null && _fppiCache.scale === scale)
return _fppiCache.value;
const p = bigintDigits(scale) - 1;
const neededDigits = p + 10;
const digits = PI_DIGITS.slice(0, neededDigits + 1);
const piInt = BigInt(digits);
const fracDigits = digits.length - 1;
const value = piInt * scale / pow10(fracDigits);
_fppiCache = { scale, value };
return value;
}
function fpsincos(x, scale) {
if (x === 0n) return [0n, scale];
const pi = fppi(scale);
const twoPi = 2n * pi;
const halfPi = pi / 2n;
let r;
const absX = bigintAbs(x);
if (absX > scale * (1n << 30n)) {
const extraDigits = bigintDigits(absX) - bigintDigits(scale) + 20;
const extScale = scale * pow10(extraDigits);
const extX = x * pow10(extraDigits);
const extPi = fppi(extScale);
const extTwoPi = 2n * extPi;
let extR = extX % extTwoPi;
if (extR < 0n) extR += extTwoPi;
r = extR / pow10(extraDigits);
} else {
r = x % twoPi;
}
if (r < 0n) r += twoPi;
let sinSign = 1n;
let cosSign = 1n;
if (r > 3n * halfPi) {
r = twoPi - r;
sinSign = -1n;
} else if (r > pi) {
r = r - pi;
sinSign = -1n;
cosSign = -1n;
} else if (r > halfPi) {
r = pi - r;
cosSign = -1n;
}
const p = bigintDigits(scale) - 1;
const targetK = Math.min(18, Math.max(2, Math.ceil(0.87 * Math.sqrt(p))));
let k = 0;
const threshold = scale >> BigInt(targetK);
while (r > threshold) {
r = r / 2n;
k++;
}
let sinVal = r;
let cosVal = scale;
let sinTerm = r;
let cosTerm = scale;
const r2 = r * r;
const scale2 = scale * scale;
for (let n = 2; ; n += 2) {
cosTerm = cosTerm * r2 / (BigInt(n) * BigInt(n - 1) * scale2);
if (cosTerm === 0n) {
sinTerm = sinTerm * r2 / (BigInt(n + 1) * BigInt(n) * scale2);
if (sinTerm !== 0n) {
if (n % 4 === 2) {
cosVal -= cosTerm;
sinVal -= sinTerm;
} else {
cosVal += cosTerm;
sinVal += sinTerm;
}
}
break;
}
sinTerm = sinTerm * r2 / (BigInt(n + 1) * BigInt(n) * scale2);
if (n % 4 === 2) {
cosVal -= cosTerm;
sinVal -= sinTerm;
} else {
cosVal += cosTerm;
sinVal += sinTerm;
}
if (sinTerm === 0n) break;
}
for (let i = 0; i < k; i++) {
const newSin = 2n * sinVal * cosVal / scale;
const newCos = 2n * cosVal * cosVal / scale - scale;
sinVal = newSin;
cosVal = newCos;
}
return [sinSign * sinVal, cosSign * cosVal];
}
function fpatan(x, scale) {
if (x === 0n) return 0n;
if (x < 0n) return -fpatan(-x, scale);
const pi = fppi(scale);
const halfPi = pi / 2n;
if (x > scale) {
const reciprocal = scale * scale / x;
return halfPi - fpatan(reciprocal, scale);
}
const threshold = 4n * scale / 10n;
let halvings = 0;
let r = x;
while (r > threshold) {
const r22 = r * r;
const val = (scale * scale + r22) / scale;
const sqrtVal = fpsqrt(val, scale);
r = r * scale / (scale + sqrtVal);
halvings++;
}
let sum = r;
let term = r;
const r2 = r * r;
const scale2 = scale * scale;
for (let n = 3; ; n += 2) {
term = term * r2 / scale2;
if (term === 0n) break;
if (n % 4 === 3) {
sum -= term / BigInt(n);
} else {
sum += term / BigInt(n);
}
}
for (let i = 0; i < halvings; i++) {
sum = 2n * sum;
}
return sum;
}
// src/big-decimal/big-decimal.ts
var NAN_CMP = NaN;
var BigDecimal = class _BigDecimal {
/** Working precision (significant digits) for inexact operations like division. */
static precision = 50;
// ---------- Static constants ----------
static ZERO = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: 0n,
exponent: 0
})
);
static ONE = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: 1n,
exponent: 0
})
);
static TWO = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: 2n,
exponent: 0
})
);
static NEGATIVE_ONE = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: -1n,
exponent: 0
})
);
static HALF = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: 5n,
exponent: -1
})
);
static NAN = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: 0n,
exponent: NaN
})
);
static POSITIVE_INFINITY = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: 1n,
exponent: Infinity
})
);
static NEGATIVE_INFINITY = Object.freeze(
Object.assign(Object.create(_BigDecimal.prototype), {
significand: -1n,
exponent: Infinity
})
);
/** Full-precision PI (1100+ digits) */
static _piFullPrecision = null;
/** PI rounded to working precision */
static _piCache = null;
static _piCachePrecision = 0;
/** PI to current working precision. */
static get PI() {
if (_BigDecimal._piFullPrecision === null) {
_BigDecimal._piFullPrecision = new _BigDecimal(
PI_DIGITS[0] + "." + PI_DIGITS.slice(1)
);
}
const prec = _BigDecimal.precision;
if (_BigDecimal._piCache === null || _BigDecimal._piCachePrecision !== prec) {
_BigDecimal._piCache = _BigDecimal._piFullPrecision.toPrecision(prec + 4);
_BigDecimal._piCachePrecision = prec;
}
return _BigDecimal._piCache;
}
significand;
exponent;
constructor(value) {
if (value instanceof _BigDecimal) {
this.significand = value.significand;
this.exponent = value.exponent;
return;
}
if (typeof value === "bigint") {
[this.significand, this.exponent] = normalize(value, 0);
return;
}
if (typeof value === "number") {
[this.significand, this.exponent] = fromNumber(value);
return;
}
[this.significand, this.exponent] = fromString(value);
}
// ---------- State checks ----------
/** True when this value represents NaN. */
isNaN() {
return Number.isNaN(this.exponent);
}
/** True when significand is 0 and the value is not NaN. */
isZero() {
return this.exponent === 0 && this.significand === 0n;
}
/** True when the exponent is finite (not NaN, not +/-Infinity). */
isFinite() {
return Number.isFinite(this.exponent);
}
/** True when the value represents a mathematical integer (exponent >= 0) and is finite. */
isInteger() {
return this.isFinite() && this.exponent >= 0;
}
/** True when significand > 0 (including positive infinity). */
isPositive() {
return this.significand > 0n;
}
/** True when significand < 0 (including negative infinity). */
isNegative() {
return this.significand < 0n;
}
// ---------- Comparison methods ----------
/**
* Compare this value with another.
* Returns -1 if this < other, 0 if equal, 1 if this > other, NaN if either is NaN.
*/
cmp(other) {
if (typeof other === "number") {
if (Number.isNaN(other)) return NAN_CMP;
const thisExp2 = this.exponent;
if (Number.isNaN(thisExp2)) return NAN_CMP;
if (other === 0) {
if (this.significand === 0n) return 0;
return this.significand > 0n ? 1 : -1;
}
if (!Number.isFinite(thisExp2)) {
if (other === Infinity) return this.significand > 0n ? 0 : -1;
if (other === -Infinity) return this.significand < 0n ? 0 : 1;
return this.significand > 0n ? 1 : -1;
}
if (this.significand === 0n) return other > 0 ? -1 : 1;
if (other === Infinity) return -1;
if (other === -Infinity) return 1;
if (this.significand > 0n !== other > 0)
return this.significand > 0n ? 1 : -1;
if (Number.isInteger(other) && thisExp2 >= 0 && thisExp2 <= 15) {
const thisVal = this.significand * pow10(thisExp2);
const otherVal = BigInt(other);
if (thisVal < otherVal) return -1;
if (thisVal > otherVal) return 1;
return 0;
}
other = new _BigDecimal(other);
}
const thisExp = this.exponent;
const otherExp = other.exponent;
const thisSig = this.significand;
const otherSig = other.significand;
if (thisExp !== thisExp || otherExp !== otherExp) return NAN_CMP;
if (!Number.isFinite(thisExp) || !Number.isFinite(otherExp)) {
if (!Number.isFinite(thisExp) && !Number.isFinite(otherExp)) {
if (thisSig === otherSig) return 0;
return thisSig > otherSig ? 1 : -1;
}
if (!Number.isFinite(thisExp)) return thisSig > 0n ? 1 : -1;
return otherSig > 0n ? -1 : 1;
}
if (thisSig === 0n) {
if (otherSig === 0n) return 0;
return otherSig > 0n ? -1 : 1;
}
if (otherSig === 0n) return thisSig > 0n ? 1 : -1;
if (thisSig > 0n && otherSig < 0n) return 1;
if (thisSig < 0n && otherSig > 0n) return -1;
if (thisExp === otherExp) {
if (thisSig < otherSig) return -1;
if (thisSig > otherSig) return 1;
return 0;
}
const thisDigits = bigintDigits(thisSig);
const otherDigits = bigintDigits(otherSig);
const thisMag = thisDigits + thisExp;
const otherMag = otherDigits + otherExp;
if (thisMag !== otherMag) {
const sign2 = thisSig > 0n ? 1 : -1;
return thisMag > otherMag ? sign2 : -sign2;
}
let aSig = thisSig;
let bSig = otherSig;
const diff = Math.abs(thisExp - otherExp);
if (diff > 1e3) {
const aDigits = thisDigits;
const bDigits = otherDigits;
const target = Math.max(aDigits, bDigits) + 1;
if (aDigits < target) aSig = aSig * pow10(target - aDigits);
if (bDigits < target) bSig = bSig * pow10(target - bDigits);
} else if (thisExp < otherExp) {
bSig = bSig * pow10(diff);
} else {
aSig = aSig * pow10(diff);
}
if (aSig < bSig) return -1;
if (aSig > bSig) return 1;
return 0;
}
/**
* Returns true if this value equals other.
* NaN === NaN → false (standard NaN semantics).
*/
eq(other) {
if (typeof other === "number") {
if (other === 0) return this.significand === 0n && this.exponent === 0;
if (other === 1) return this.significand === 1n && this.exponent === 0;
if (other === -1) return this.significand === -1n && this.exponent === 0;
if (Number.isInteger(other) && Number.isFinite(this.exponent) && this.exponent >= 0 && this.exponent <= 15) {
return this.significand * pow10(this.exponent) === BigInt(other);
}
return this.cmp(other) === 0;
}
return this.significand === other.significand && this.exponent === other.exponent;
}
/** Returns true if this value is strictly less than other. */
lt(other) {
return this.cmp(other) === -1;
}
/** Returns true if this value is less than or equal to other. */
lte(other) {
const c = this.cmp(other);
return c === -1 || c === 0;
}
/** Returns true if this value is strictly greater than other. */
gt(other) {
return this.cmp(other) === 1;
}
/** Returns true if this value is greater than or equal to other. */
gte(other) {
const c = this.cmp(other);
return c === 1 || c === 0;
}
// ---------- Arithmetic methods ----------
/**
* Add this value to another.
* Aligns exponents, adds significands. The result is exact.
*/
add(other) {
if (typeof other === "number") other = new _BigDecimal(other);
const thisExp = this.exponent;
const otherExp = other.exponent;
if (Number.isFinite(thisExp) && Number.isFinite(otherExp)) {
if (thisExp === otherExp)
return fromRaw(this.significand + other.significand, thisExp);
const diff = thisExp - otherExp;
if (diff > 0)
return fromRaw(
this.significand * pow10(diff) + other.significand,
otherExp
);
return fromRaw(
this.significand + other.significand * pow10(-diff),
thisExp
);
}
if (thisExp !== thisExp || otherExp !== otherExp) return _BigDecimal.NAN;
const thisInf = !Number.isFinite(thisExp);
const otherInf = !Number.isFinite(otherExp);
if (thisInf && otherInf) {
if (this.significand !== other.significand) return _BigDecimal.NAN;
return this.significand > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
}
if (thisInf)
return this.significand > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
return other.significand > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
}
/**
* Subtract other from this.
* Aligns exponents, subtracts significands. The result is exact.
*/
sub(other) {
if (typeof other === "number") other = new _BigDecimal(other);
const thisExp = this.exponent;
const otherExp = other.exponent;
if (Number.isFinite(thisExp) && Number.isFinite(otherExp)) {
if (thisExp === otherExp)
return fromRaw(this.significand - other.significand, thisExp);
const diff = thisExp - otherExp;
if (diff > 0)
return fromRaw(
this.significand * pow10(diff) - other.significand,
otherExp
);
return fromRaw(
this.significand - other.significand * pow10(-diff),
thisExp
);
}
if (thisExp !== thisExp || otherExp !== otherExp) return _BigDecimal.NAN;
const thisInf = !Number.isFinite(thisExp);
const otherInf = !Number.isFinite(otherExp);
if (thisInf && otherInf) {
if (this.significand === other.significand) return _BigDecimal.NAN;
return this.significand > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
}
if (thisInf)
return this.significand > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
return other.significand > 0n ? _BigDecimal.NEGATIVE_INFINITY : _BigDecimal.POSITIVE_INFINITY;
}
/**
* Multiply this value by another.
* Multiplies significands, adds exponents. The result is exact.
*/
mul(other) {
if (typeof other === "number") other = new _BigDecimal(other);
const thisExp = this.exponent;
const otherExp = other.exponent;
if (Number.isFinite(thisExp) && Number.isFinite(otherExp))
return fromRaw(this.significand * other.significand, thisExp + otherExp);
if (thisExp !== thisExp || otherExp !== otherExp) return _BigDecimal.NAN;
if (this.significand === 0n || other.significand === 0n)
return _BigDecimal.NAN;
const signA = this.significand > 0n ? 1n : -1n;
const signB = other.significand > 0n ? 1n : -1n;
return signA * signB > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
}
/**
* Negate this value. Zero.neg() → Zero.
*/
neg() {
const sig = this.significand;
if (sig === 0n) return this;
if (Number.isFinite(this.exponent)) return fromRaw(-sig, this.exponent);
return sig > 0n ? _BigDecimal.NEGATIVE_INFINITY : _BigDecimal.POSITIVE_INFINITY;
}
/**
* Absolute value. If already non-negative, returns this.
*/
abs() {
if (this.significand >= 0n) return this;
if (!Number.isFinite(this.exponent)) return _BigDecimal.POSITIVE_INFINITY;
return fromRaw(-this.significand, this.exponent);
}
/**
* Round toward -Infinity.
* `3.7` → `3`, `-3.7` → `-4`.
* For integers returns this. NaN/Infinity → return this.
*/
floor() {
const exp3 = this.exponent;
if (exp3 >= 0) return this;
if (Number.isFinite(exp3)) {
const t = this.trunc();
if (this.significand < 0n) return t.sub(fromRaw(1n, 0));
return t;
}
return this;
}
/**
* Round toward +Infinity.
* `3.2` → `4`, `-3.2` → `-3`.
* For integers returns this. NaN/Infinity → return this.
*/
ceil() {
const exp3 = this.exponent;
if (exp3 >= 0) return this;
if (Number.isFinite(exp3)) {
const t = this.trunc();
if (this.significand > 0n) return t.add(fromRaw(1n, 0));
return t;
}
return this;
}
/**
* Round half away from zero (standard math rounding).
* `3.5` → `4`, `-3.5` → `-4`, `3.4` → `3`, `3.6` → `4`.
* For integers returns this. NaN/Infinity → return this.
*/
round() {
const exp3 = this.exponent;
if (exp3 >= 0) return this;
if (Number.isFinite(exp3)) {
const half = fromRaw(5n, -1);
if (this.significand > 0n) return this.add(half).trunc();
return this.sub(half).trunc();
}
return this;
}
/**
* Truncate toward zero, removing the fractional part.
* For integers (exponent >= 0) returns this.
* NaN → NaN, +/-Infinity → +/-Infinity.
*/
trunc() {
const exp3 = this.exponent;
if (exp3 >= 0) return this;
if (Number.isFinite(exp3)) {
const truncSig = this.significand / pow10(-exp3);
if (truncSig === 0n) return fromRaw(0n, 0);
return fromRaw(truncSig, 0);
}
return this;
}
/**
* Divide this value by another.
* Uses `BigDecimal.precision` to determine significant digits for inexact results.
*
* Special cases:
* - NaN / x → NaN, x / NaN → NaN
* - nonzero / 0 → +/-Infinity (matching Decimal.js behavior)
* - 0 / 0 → NaN
* - Inf / finite → Inf (correct sign)
* - finite / Inf → 0
* - Inf / Inf → NaN
*/
div(other) {
if (typeof other === "number") other = new _BigDecimal(other);
const thisExp = this.exponent;
const otherExp = other.exponent;
const thisSig = this.significand;
const otherSig = other.significand;
if (Number.isFinite(thisExp) && Number.isFinite(otherExp)) {
if (otherSig === 0n) {
if (thisSig === 0n) return _BigDecimal.NAN;
return thisSig > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
}
if (thisSig === 0n) return fromRaw(0n, 0);
const prec = _BigDecimal.precision;
const guard = 10;
const absDividend = thisSig < 0n ? -thisSig : thisSig;
const absDivisor = otherSig < 0n ? -otherSig : otherSig;
const dividendDigits = bigintDigits(absDividend);
const divisorDigits = bigintDigits(absDivisor);
const totalScale = prec + guard + Math.max(0, divisorDigits - dividendDigits);
const scale = pow10(totalScale);
const quotient = thisSig * scale / otherSig;
const resultExp = thisExp - otherExp - totalScale;
return fromRaw(quotient, resultExp).toPrecision(prec);
}
if (thisExp !== thisExp || otherExp !== otherExp) return _BigDecimal.NAN;
const thisInf = !Number.isFinite(thisExp);
const otherInf = !Number.isFinite(otherExp);
if (thisInf && otherInf) return _BigDecimal.NAN;
if (thisInf) {
const signA = thisSig > 0n ? 1n : -1n;
const signB = otherSig > 0n ? 1n : otherSig < 0n ? -1n : 1n;
return signA * signB > 0n ? _BigDecimal.POSITIVE_INFINITY : _BigDecimal.NEGATIVE_INFINITY;
}
return fromRaw(0n, 0);
}
/**
* Multiplicative inverse: 1 / this.
* Uses `BigDecimal.precision` for the division.
*/
inv() {
return fromRaw(1n, 0).div(this);
}
/**
* Modulo (remainder after truncating division).
* Defined as: this - trunc(this / other) * other
*
* The sign of the result matches the sign of the dividend (this),
* consistent with JavaScript's % operator and Decimal.js.
*/
mod(other) {
if (typeof other === "number") other = new _BigDecimal(other);
const thisExp = this.exponent;
const otherExp = other.exponent;
if (Number.isFinite(thisExp) && Number.isFinite(otherExp)) {
if (other.significand === 0n) return _BigDecimal.NAN;
if (this.significand === 0n) return fromRaw(0n, 0);
return this.sub(this.div(other).trunc().mul(other)).toPrecision(
_BigDecimal.precision
);
}
if (thisExp !== thisExp || otherExp !== otherExp) return _BigDecimal.NAN;
if (!Number.isFinite(thisExp)) return _BigDecimal.NAN;
return new _BigDecimal(this);
}
/**
* Raise to a power.
*
* - Integer exponent: exact result via repeated squaring
* - Zero exponent: 1 (for any non-NaN base)
* - Negative integer exponent: pow(abs(n)).inv() (uses precision)
* - Non-integer exponent on positive base: exp(n * ln(this))
* - Non-integer exponent on negative base: NaN (real-valued result doesn't exist)
*
* Special cases:
* - NaN base or exponent → NaN
* - Infinite exponent → NaN
* - 0^0 → 1 (mathematical convention)
* - 0^positive → 0
* - 0^negative → Infinity
*/
pow(n) {
if (typeof n === "number") n = new _BigDecimal(n);
if (this.isNaN() || n.isNaN()) return _BigDecimal.NAN;
if (!n.isFinite()) return _BigDecimal.NAN;
if (n.isInteger()) {
const expValue = n.toBigInt();
if (expValue === 0n) return fromRaw(1n, 0);
if (!this.isFinite()) {
if (expValue > 0n) {
if (this.significand < 0n && expValue % 2n !== 0n)
return _BigDecimal.NEGATIVE_INFINITY;
return _BigDecimal.POSITIVE_INFINITY;
}
return fromRaw(0n, 0);
}
if (this.isZero()) {
if (expValue > 0n) return fromRaw(0n, 0);
return _BigDecimal.POSITIVE_INFINITY;
}
if (expValue < 0n) {
return this.pow(n.neg()).inv();
}
const absSig = this.significand < 0n ? -this.significand : this.significand;
const thisLog10 = bigintDigits(absSig) + this.exponent;
const resultLog10 = Number(expValue) * thisLog10;
if (resultLog10 > 9e15) {
return this.significand < 0n && expValue % 2n !== 0n ? _BigDecimal.NEGATIVE_INFINITY : _BigDecimal.POSITIVE_INFINITY;
}
if (resultLog10 < -9e15) {
return fromRaw(0n, 0);
}
const prec = _BigDecimal.precision;
let result = fromRaw(1n, 0);
let base = this;
let exp3 = expValue;
while (exp3 > 0n) {
if (exp3 & 1n) {
result = result.mul(base).toPrecision(prec);
}
exp3 >>= 1n;
if (exp3 > 0n) {
base = base.mul(base).toPrecision(prec);
}
}
return result;
}
if (!this.isFinite()) {
if (this.significand < 0n) return _BigDecimal.NAN;
if (n.significand > 0n) return _BigDecimal.POSITIVE_INFINITY;
return _BigDecimal.ZERO;
}
if (this.isZero()) {
if (n.significand > 0n) return _BigDecimal.ZERO;
return _BigDecimal.POSITIVE_INFINITY;
}
if (this.significand < 0n) return _BigDecimal.NAN;
return n.mul(this.ln()).exp();
}
// ---------- Conversion methods ----------
/** Convert to a JavaScript number. May lose precision for large values. */
toNumber() {
if (!Number.isFinite(this.exponent)) {
if (this.exponent !== this.exponent) return NaN;
return this.significand > 0n ? Infinity : -Infinity;
}
if (this.significand === 0n) return 0;
if (this.exponent === 0) return Number(this.significand);
return Number(this.toString());
}
/**
* Reconstruct a decimal string from significand and exponent.
*
* For normal-range exponents, produces a clean decimal string.
* For very large (> 20) or very small (< -6) adjusted exponents,
* uses scientific notation like `'1.5e+25'`.
*/
toString() {
if (!Number.isFinite(this.exponent)) {
if (this.exponent !== this.exponent) return "NaN";
return this.significand > 0n ? "Infinity" : "-Infinity";
}
if (this.significand === 0n) return "0";
const negative = this.significand < 0n;
const absStr = (negative ? -this.significand : this.significand).toString();
const numDigits = absStr.length;
const adjustedExp = numDigits + this.exponent - 1;
const sign2 = negative ? "-" : "";
if (adjustedExp > 20 || adjustedExp < -6) {
const sciStr = numDigits === 1 ? absStr : absStr[0] + "." + absStr.slice(1);
const expSign = adjustedExp >= 0 ? "+" : "";
return `${sign2}${sciStr}e${expSign}${adjustedExp}`;
}
if (this.exponent >= 0) {
return sign2 + absStr + "0".repeat(this.exponent);
}
const decimalDigits = -this.exponent;
if (decimalDigits < numDigits) {
const intPart = absStr.slice(0, numDigits - decimalDigits);
const fracPart = absStr.slice(numDigits - decimalDigits);
return `${sign2}${intPart}.${fracPart}`;
}
const leadingZeros = decimalDigits - numDigits;
return `${sign2}0.${"0".repeat(leadingZeros)}${absStr}`;
}
/**
* Format with a fixed number of decimal places.
*
* Rounds to the specified number of digits after the decimal point
* using round-half-to-even (banker's rounding) for the tie-breaking case.
* If `digits` is undefined, behaves like toFixed(0).
*/
toFixed(digits) {
const d = digits ?? 0;
if (!Number.isFinite(this.exponent)) {
if (this.exponent !== this.exponent) return "NaN";
return this.significand > 0n ? "Infinity" : "-Infinity";
}
const negative = this.significand < 0n;
const absSig = negative ? -this.significand : this.significand;
const shift = this.exponent + d;
let rounded;
if (shift >= 0) {
rounded = absSig * pow10(shift);
} else {
const divisor = pow10(-shift);
const quotient = absSig / divisor;
const remainder2 = absSig % divisor;
const half = divisor / 2n;
if (remainder2 > half) {
rounded = quotient + 1n;
} else if (remainder2 < half) {
rounded = quotient;
} else {
if (divisor % 2n !== 0n) {
rounded = quotient;
} else if (quotient % 2n === 0n) {
rounded = quotient;
} else {
rounded = quotient + 1n;
}
}
}
const sign2 = negative && rounded !== 0n ? "-" : "";
const roundedStr = rounded.toString();
if (d === 0) {
return `${sign2}${roundedStr}`;
}
if (roundedStr.length <= d) {
const padded = roundedStr.padStart(d, "0");
return `${sign2}0.${padded}`;
}
const intPart = roundedStr.slice(0, roundedStr.length - d);
const fracPart = roundedStr.slice(roundedStr.length - d);
return `${sign2}${intPart}.${fracPart}`;
}
/**
* Round to n significant digits, returning a new BigDecimal.
* Uses round-half-to-even for tie-breaking.
* If the value already has n or fewer significant digits, returns this.
*/
toPrecision(n) {
if (this.significand === 0n || !Number.isFinite(this.exponent)) return this;
const absSig = this.significand < 0n ? -this.significand : this.significand;
const digits = bigintDigits(absSig);
if (digits <= n) return this;
const shift = digits - n;
const divisor = pow10(shift);
let rounded = absSig / divisor;
const remainder2 = absSig % divisor;
const half = divisor / 2n;
if (remainder2 > half || remainder2 === half && rounded % 2n !== 0n) {
rounded += 1n;
}
const sig = this.significand < 0n ? -rounded : rounded;
return fromRaw(sig, this.exponent + shift);
}
/**
* Truncate fractional part and return a bigint.
* Throws if the value is NaN or Infinity.
*/
toBigInt() {
if (!Number.isFinite(this.exponent)) {
if (this.exponent !== this.exponent)
throw new RangeError("Cannot convert NaN to BigInt");
throw new RangeError("Cannot convert Infinity to BigInt");
}
if (this.exponent >= 0) {
return this.significand * pow10(this.exponent);
}
const divisor = pow10(-this.exponent);
return this.significand / divisor;
}
};
function fromRaw(sig, exp3) {
const [normSig, normExp] = normalize(sig, exp3);
const bd = Object.create(BigDecimal.prototype);
bd.significand = normSig;
bd.exponent = normExp;
return bd;
}
var _1e9 = 1000000000n;
var _1e3 = 1000n;
function normalize(sig, exp3) {
if (sig === 0n) return [0n, 0];
while (sig % _1e9 === 0n) {
sig /= _1e9;
exp3 += 9;
}
while (sig % _1e3 === 0n) {
sig /= _1e3;
exp3 += 3;
}
while (sig % 10n === 0n) {
sig /= 10n;
exp3 += 1;
}
return [sig, exp3];
}
function fromNumber(value) {
if (Number.isNaN(value)) return [0n, NaN];
if (value === Infinity) return [1n, Infinity];
if (value === -Infinity) return [-1n, Infinity];
if (Number.isInteger(value)) return normalize(BigInt(value), 0);
return fromString(value.toString());
}
function fromString(s) {
s = s.trim();
if (s === "" || s === "NaN") return [0n, NaN];
if (s === "Infinity" || s === "+Infinity") return [1n, Infinity];
if (s === "-Infinity") return [-1n, Infinity];
let mantissa;
let explicitExp = 0;
const eIdx = s.search(/[eE]/);
if (eIdx !== -1) {
mantissa = s.slice(0, eIdx);
explicitExp = Number(s.slice(eIdx + 1));
if (!Number.isFinite(explicitExp)) return [0n, NaN];
} else {
mantissa = s;
}
let negative = false;
if (mantissa.startsWith("-")) {
negative = true;
mantissa = mantissa.slice(1);
} else if (mantissa.startsWith("+")) {
mantissa = mantissa.slice(1);
}
const dotIdx = mantissa.indexOf(".");
let intPart;
let fracPart;
if (dotIdx === -1) {
intPart = mantissa;
fracPart = "";
} else {
intPart = mantissa.slice(0, dotIdx);
fracPart = mantissa.slice(dotIdx + 1);
}
intPart = intPart.replace(/^0+/, "") || "0";
const digits = intPart + fracPart;
if (digits.length === 0 || !/^\d+$/.test(digits)) return [0n, NaN];
let sig = BigInt(digits);
if (negative) sig = -sig;
const implicitExp = -fracPart.length;
return normalize(sig, implicitExp + explicitExp);
}
// src/big-decimal/transcendentals.ts
function toFixedPoint(x, precision) {
const scale = pow10(precision);
const exp3 = x.exponent + precision;
if (exp3 >= 0) {
return [x.significand * pow10(exp3), scale];
}
return [x.significand / pow10(-exp3), scale];
}
function fromFixedPoint(fp, scale, targetPrecision) {
if (fp === 0n) return BigDecimal.ZERO;
const negative = fp < 0n;
let absFp = negative ? -fp : fp;
const fpDigits = bigintDigits(absFp);
if (fpDigits > targetPrecision) {
const drop = fpDigits - targetPrecision;
const divisor = pow10(drop);
const half = divisor / 2n;
const remainder2 = absFp % divisor;
absFp = absFp / divisor;
if (remainder2 >= half) absFp += 1n;
const scaleExp2 = bigintDigits(scale) - 1;
const resultExp2 = drop - scaleExp2;
const sig2 = negative ? -absFp : absFp;
return fromRaw(sig2, resultExp2);
}
const scaleExp = bigintDigits(scale) - 1;
const resultExp = -scaleExp;
const sig = negative ? -absFp : absFp;
return fromRaw(sig, resultExp);
}
BigDecimal.prototype.sqrt = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ZERO;
if (!this.isFinite()) {
if (this.significand > 0n) return BigDecimal.POSITIVE_INFINITY;
return BigDecimal.NAN;
}
if (this.significand < 0n) return BigDecimal.NAN;
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 10;
const [fp, scale] = toFixedPoint(this, workingPrec);
const sqrtFp = fpsqrt(fp, scale);
return fromFixedPoint(sqrtFp, scale, targetPrec);
};
BigDecimal.prototype.cbrt = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ZERO;
if (!this.isFinite()) {
if (this.significand > 0n) return BigDecimal.POSITIVE_INFINITY;
return BigDecimal.NEGATIVE_INFINITY;
}
if (this.significand < 0n) {
return this.neg().cbrt().neg();
}
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 10;
const [fp, scale] = toFixedPoint(this, workingPrec);
const C = fp * scale * scale;
let x;
const numVal = this.toNumber();
const scaleNum = Number(scale);
if (Number.isFinite(numVal) && numVal > 0 && Number.isFinite(scaleNum)) {
const approx = Math.cbrt(numVal);
if (Number.isFinite(approx) && approx > 0) {
x = BigInt(Math.floor(approx * scaleNum));
if (x === 0n) x = 1n;
} else {
x = cbrtSeed(fp, scale);
}
} else {
x = cbrtSeed(fp, scale);
}
let prev;
do {
prev = x;
const x2 = x * x;
if (x2 === 0n) {
x = 1n;
break;
}
x = (2n * x + C / x2) / 3n;
} whil