@cortex-js/compute-engine
Version:
Symbolic computing and numeric evaluations for JavaScript and Node.js
1,498 lines (1,493 loc) • 409 kB
JavaScript
/** Compute Engine 0.58.0 */
// 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;
} while (bigintAbs(x - prev) > 1n);
{
const next = (2n * x + C / (x * x)) / 3n;
const diffX = bigintAbs(x * x * x - C);
const diffNext = bigintAbs(next * next * next - C);
if (diffNext < diffX) x = next;
}
return fromFixedPoint(x, scale, targetPrec);
};
BigDecimal.sqrt = function(x) {
return x.sqrt();
};
BigDecimal.cbrt = function(x) {
return x.cbrt();
};
BigDecimal.prototype.exp = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) {
if (this.significand > 0n) return BigDecimal.POSITIVE_INFINITY;
return BigDecimal.ZERO;
}
if (this.isZero()) return BigDecimal.ONE;
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 15;
const [fp, scale] = toFixedPoint(this, workingPrec);
const expFp = fpexp(fp, scale);
return fromFixedPoint(expFp, scale, targetPrec);
};
BigDecimal.prototype.ln = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) {
if (this.significand > 0n) return BigDecimal.POSITIVE_INFINITY;
return BigDecimal.NAN;
}
if (this.isZero()) return BigDecimal.NEGATIVE_INFINITY;
if (this.significand < 0n) return BigDecimal.NAN;
if (this.eq(1)) return BigDecimal.ZERO;
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 15;
const [fp, scale] = toFixedPoint(this, workingPrec);
const lnFp = fpln(fp, scale);
return fromFixedPoint(lnFp, scale, targetPrec);
};
BigDecimal.prototype.log = function(base) {
const b = base instanceof BigDecimal ? base : new BigDecimal(base);
return this.ln().div(b.ln());
};
BigDecimal.exp = function(x) {
return x.exp();
};
BigDecimal.ln = function(x) {
return x.ln();
};
BigDecimal.log10 = function(x) {
return x.log(10);
};
BigDecimal.prototype.sin = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ZERO;
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 15;
const [fp, scale] = toFixedPoint(this, workingPrec);
const [sinFp] = fpsincos(fp, scale);
return fromFixedPoint(sinFp, scale, targetPrec);
};
BigDecimal.prototype.cos = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ONE;
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 15;
const [fp, scale] = toFixedPoint(this, workingPrec);
const [, cosFp] = fpsincos(fp, scale);
return fromFixedPoint(cosFp, scale, targetPrec);
};
BigDecimal.prototype.tan = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ZERO;
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 15;
const [fp, scale] = toFixedPoint(this, workingPrec);
const [sinFp, cosFp] = fpsincos(fp, scale);
if (cosFp === 0n) {
return sinFp > 0n ? BigDecimal.POSITIVE_INFINITY : BigDecimal.NEGATIVE_INFINITY;
}
const tanFp = sinFp * scale / cosFp;
return fromFixedPoint(tanFp, scale, targetPrec);
};
BigDecimal.prototype.atan = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ZERO;
if (!this.isFinite()) {
const piHalf = BigDecimal.PI.div(BigDecimal.TWO);
if (this.significand > 0n) return piHalf;
return piHalf.neg();
}
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 15;
const [fp, scale] = toFixedPoint(this, workingPrec);
const atanFp = fpatan(fp, scale);
return fromFixedPoint(atanFp, scale, targetPrec);
};
BigDecimal.prototype.asin = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ZERO;
const absThis = this.abs();
const one = BigDecimal.ONE;
if (absThis.gt(one)) return BigDecimal.NAN;
if (absThis.eq(one)) {
const piHalf = BigDecimal.PI.div(BigDecimal.TWO);
return this.significand > 0n ? piHalf : piHalf.neg();
}
const targetPrec = BigDecimal.precision;
const workingPrec = targetPrec + 20;
const [xFp, scale] = toFixedPoint(this, workingPrec);
const x2 = fpmul(xFp, xFp, scale);
const oneMinusX2 = scale - x2;
const sqrtVal = fpsqrt(oneMinusX2, scale);
const ratio = fpdiv(xFp, sqrtVal, scale);
const result = fpatan(ratio, scale);
return fromFixedPoint(result, scale, targetPrec);
};
BigDecimal.prototype.acos = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (!this.isFinite()) return BigDecimal.NAN;
const absThis = this.abs();
if (absThis.gt(BigDecimal.ONE)) return BigDecimal.NAN;
if (this.eq(1)) return BigDecimal.ZERO;
if (this.eq(-1)) return BigDecimal.PI;
const piHalf = BigDecimal.PI.div(BigDecimal.TWO);
return piHalf.sub(this.asin());
};
BigDecimal.sin = function(x) {
return x.sin();
};
BigDecimal.cos = function(x) {
return x.cos();
};
BigDecimal.tan = function(x) {
return x.tan();
};
BigDecimal.asin = function(x) {
return x.asin();
};
BigDecimal.acos = function(x) {
return x.acos();
};
BigDecimal.atan = function(x) {
return x.atan();
};
BigDecimal.atan2 = function(y, x) {
const yBd = y instanceof BigDecimal ? y : new BigDecimal(y);
if (yBd.isNaN() || x.isNaN()) return BigDecimal.NAN;
const pi = BigDecimal.PI;
const piHalf = pi.div(BigDecimal.TWO);
if (x.isZero()) {
if (yBd.isZero()) return BigDecimal.ZERO;
if (yBd.significand > 0n) return piHalf;
return piHalf.neg();
}
const ratio = yBd.div(x);
if (x.significand > 0n) {
return ratio.atan();
}
if (yBd.significand >= 0n) {
return ratio.atan().add(pi);
}
return ratio.atan().sub(pi);
};
BigDecimal.prototype.sinh = 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;
}
const expX = this.exp();
const expNegX = expX.inv();
return expX.sub(expNegX).div(BigDecimal.TWO);
};
BigDecimal.prototype.cosh = function() {
if (this.isNaN()) return BigDecimal.NAN;
if (this.isZero()) return BigDecimal.ONE;
if (!this.isFinite()) {
return BigDecimal.POSITIVE_INFINITY;
}
const expX = this.exp();
const expNegX =