@bignum/core
Advanced tools
Comparing version 0.7.0 to 0.8.0
@@ -5,103 +5,53 @@ type OverflowContext = { | ||
}; | ||
type IsOverflow = (context: OverflowContext) => boolean | undefined | null; | ||
type IsOverflow = (context: OverflowContext) => boolean; | ||
type MathOptions = { | ||
/** You can specify an overflow test function. */ | ||
overflow?: IsOverflow; | ||
/** @deprecated The maximum number of decimal places. */ | ||
maxDp?: bigint; | ||
/** @deprecated The maximum number of precision when having decimals. */ | ||
maxDecimalPrecision?: bigint; | ||
/** | ||
* Specifies a rounding behavior for numerical operations capable of discarding precision. | ||
* If rounding must be performed to generate a result with the given scale, the specified rounding mode is applied. | ||
*/ | ||
roundingMode?: RoundingMode; | ||
}; | ||
type DivideOptions = MathOptions; | ||
type SqrtOptions = MathOptions; | ||
type NthRootOptions = MathOptions; | ||
/** This is used in negative pows. */ | ||
type PowOptions = MathOptions; | ||
/** Specifies a rounding behavior for numerical operations capable of discarding precision. */ | ||
declare enum RoundingMode { | ||
trunc = 0, | ||
round = 1, | ||
floor = 2, | ||
ceil = 3 | ||
} | ||
/** Internal Fraction class */ | ||
declare class Frac { | ||
#private; | ||
/** numerator */ | ||
readonly n: Num; | ||
private readonly n; | ||
/** denominator */ | ||
readonly d: Num; | ||
readonly opt: IsOverflow; | ||
static valueOf(n: Num, d: Num, overflowOpt: IsOverflow): Frac; | ||
private constructor(); | ||
static add(a: Num, b: Num): Frac | null; | ||
static mul(a: Num, b: Num): Frac | null; | ||
static div(n: Num, d: Num): Frac | null; | ||
resolve(overflow?: IsOverflow, useFrac?: boolean): Num | Inf; | ||
} | ||
/** Internal Number class */ | ||
declare class Num { | ||
#private; | ||
readonly inf = false; | ||
/** int value */ | ||
private i; | ||
/** exponent */ | ||
private e; | ||
/** pow of ten denominator */ | ||
private d; | ||
/** original fraction */ | ||
readonly frac: Frac | null | undefined; | ||
constructor(intValue: bigint, exponent: bigint, frac?: Frac | null | undefined); | ||
private readonly d; | ||
static numOf(i: bigint, e?: bigint): Frac; | ||
constructor(numerator: bigint, denominator: bigint); | ||
signum(): 1 | 0 | -1; | ||
negate(): Num; | ||
abs(): Num; | ||
add(augend: Num): Num; | ||
add(augend: Num | Inf): Num | Inf; | ||
subtract(subtrahend: Num): Num; | ||
subtract(subtrahend: Num | Inf): Num | Inf; | ||
multiply(multiplicand: Num): Num; | ||
multiply(multiplicand: Inf): Inf | null; | ||
multiply(multiplicand: Num | Inf): Num | Inf | null; | ||
divide(divisor: Num, options?: DivideOptions): Num; | ||
divide(divisor: Num | Inf, options?: DivideOptions): Num | Inf; | ||
modulo(divisor: Num | Inf): Num | Inf | null; | ||
pow(n: Num, options?: PowOptions): Num | null; | ||
pow(n: Num | Inf, options?: PowOptions): Num | Inf | null; | ||
scaleByPowerOfTen(n: Num | Inf): Num | Inf | null; | ||
sqrt(options?: SqrtOptions): Num; | ||
nthRoot(n: Num, options?: NthRootOptions): Num; | ||
nthRoot(n: Num | Inf, options?: NthRootOptions): Num | Inf | null; | ||
trunc(): Num; | ||
round(): Num; | ||
floor(mod?: bigint): Num; | ||
ceil(mod?: bigint): Num; | ||
compareTo(other: Num | Inf): 0 | -1 | 1; | ||
negate(): Frac; | ||
abs(): Frac; | ||
get inf(): boolean; | ||
add(a: Frac): Frac | null; | ||
multiply(m: Frac): Frac | null; | ||
divide(d: Frac): Frac | null; | ||
modulo(d: Frac): Frac | null; | ||
pow(n: Frac, options?: MathOptions): Frac | null; | ||
scaleByPowerOfTen(n: Frac): Frac | null; | ||
sqrt(options?: MathOptions): Frac | null; | ||
nthRoot(n: Frac, options?: MathOptions): Frac | null; | ||
trunc(): Frac; | ||
round(): Frac; | ||
floor(): Frac; | ||
ceil(): Frac; | ||
compareTo(other: Frac): 0 | -1 | 1; | ||
toString(): string; | ||
static div(dividend: Num, divisor: Num, overflow: IsOverflow, useFrac: boolean): Num | Inf; | ||
} | ||
/** Internal Infinity class */ | ||
declare class Inf { | ||
readonly inf = true; | ||
private readonly s; | ||
constructor(sign: 1 | -1); | ||
signum(): 1 | -1; | ||
negate(): Inf; | ||
add(augend: Inf | Num): Inf | null; | ||
subtract(subtrahend: Inf | Num): Inf | null; | ||
multiply(multiplicand: Inf | Num): Inf | null; | ||
divide(divisor: Inf | Num): Inf | null; | ||
modulo(): null; | ||
pow(n: Inf | Num): Inf | Num; | ||
sqrt(): Inf | null; | ||
nthRoot(n: Inf | Num): Inf | Num | null; | ||
scaleByPowerOfTen(n: Inf | Num): Inf | null; | ||
compareTo(n: Inf | Num): 1 | 0 | -1; | ||
abs(): Inf; | ||
trunc(): Inf; | ||
round(): Inf; | ||
ceil(): Inf; | ||
floor(): Inf; | ||
toString(): string; | ||
toNum(): number; | ||
} | ||
declare class BigNum { | ||
#private; | ||
static readonly valueOf: typeof valueOf; | ||
constructor(value: string | number | bigint | boolean | BigNum | Num | Inf | null | undefined); | ||
constructor(value: string | number | bigint | boolean | BigNum | Frac | null | undefined); | ||
/** Returns a number indicating the sign. */ | ||
@@ -120,3 +70,3 @@ signum(): -1 | 0 | 1 | typeof NaN; | ||
*/ | ||
divide(divisor: BigNum | string | number | bigint, options?: DivideOptions): BigNum; | ||
divide(divisor: BigNum | string | number | bigint): BigNum; | ||
/** | ||
@@ -129,9 +79,9 @@ * Returns a BigNum whose value is (this % divisor) | ||
*/ | ||
pow(n: BigNum | string | number | bigint, options?: PowOptions): BigNum; | ||
pow(n: BigNum | string | number | bigint, options?: MathOptions): BigNum; | ||
/** Returns a BigNum whose numerical value is equal to (this * 10 ** n). */ | ||
scaleByPowerOfTen(n: BigNum | string | number | bigint): BigNum; | ||
/** Returns an approximation to the square root of this. */ | ||
sqrt(options?: SqrtOptions): BigNum; | ||
sqrt(options?: MathOptions): BigNum; | ||
/** Returns an approximation to the nth root of this. */ | ||
nthRoot(n: BigNum | string | number | bigint, options?: NthRootOptions): BigNum; | ||
nthRoot(n: BigNum | string | number | bigint, options?: MathOptions): BigNum; | ||
/** Returns a BigNum whose value is the absolute value of this BigNum. */ | ||
@@ -157,2 +107,2 @@ abs(): BigNum; | ||
export { BigNum, type DivideOptions, type IsOverflow, type MathOptions, type NthRootOptions, type OverflowContext, type PowOptions, type SqrtOptions }; | ||
export { BigNum, type IsOverflow, type MathOptions, type OverflowContext, RoundingMode }; |
669
lib/index.js
@@ -1,29 +0,1 @@ | ||
// src/frac.mts | ||
var Frac = class _Frac { | ||
static valueOf(n, d, overflowOpt) { | ||
if (n.frac) | ||
_Frac.valueOf(n.frac.n, d.multiply(n.frac.d), overflowOpt); | ||
if (d.frac) | ||
_Frac.valueOf(n.multiply(d.frac.d), d.frac.n, overflowOpt); | ||
return new _Frac(n, d, overflowOpt); | ||
} | ||
constructor(n, d, opt) { | ||
this.n = n; | ||
this.d = d; | ||
this.opt = opt; | ||
} | ||
static add(a, b) { | ||
return a.frac ? _Frac.valueOf(a.frac.n.add(b.multiply(a.frac.d)), a.frac.d, a.frac.opt) : b.frac ? _Frac.add(b, a) : null; | ||
} | ||
static mul(a, b) { | ||
return a.frac ? _Frac.valueOf(a.frac.n.multiply(b), a.frac.d, a.frac.opt) : b.frac ? _Frac.mul(b, a) : null; | ||
} | ||
static div(n, d) { | ||
return n.frac ? _Frac.valueOf(n.frac.n, d.multiply(n.frac.d), n.frac.opt) : d.frac ? _Frac.valueOf(n.multiply(d.frac.d), d.frac.n, d.frac.opt) : null; | ||
} | ||
resolve(overflow, useFrac) { | ||
return Num.div(this.n, this.d, overflow ?? this.opt, useFrac ?? true); | ||
} | ||
}; | ||
// src/nth-root-utils.mts | ||
@@ -65,2 +37,11 @@ function createNthRootTable(nth) { | ||
// src/options.mts | ||
var RoundingMode = /* @__PURE__ */ ((RoundingMode2) => { | ||
RoundingMode2[RoundingMode2["trunc"] = 0] = "trunc"; | ||
RoundingMode2[RoundingMode2["round"] = 1] = "round"; | ||
RoundingMode2[RoundingMode2["floor"] = 2] = "floor"; | ||
RoundingMode2[RoundingMode2["ceil"] = 3] = "ceil"; | ||
return RoundingMode2; | ||
})(RoundingMode || {}); | ||
// src/util.mts | ||
@@ -79,5 +60,2 @@ function compare(a, b) { | ||
} | ||
function min(a, b) { | ||
return a <= b ? a : b; | ||
} | ||
function abs(a) { | ||
@@ -87,116 +65,127 @@ return a >= 0n ? a : -a; | ||
function gcd(a, b) { | ||
return b ? gcd(b, a % b) : a; | ||
while (b) { | ||
const t = a % b; | ||
a = b; | ||
b = t; | ||
} | ||
return a; | ||
} | ||
// src/num.mts | ||
var MOD_DIV_OPT = (ctx) => ctx.scale > 0n; | ||
// src/frac.mts | ||
var NOT_SCALE_ZERO = (ctx) => ctx.scale > 0n; | ||
var ROUND_OPTS = Object.fromEntries( | ||
Object.keys(RoundingMode).map(Number).filter(isFinite).map((k) => [k, { overflow: NOT_SCALE_ZERO, roundingMode: k }]) | ||
); | ||
var DEF_OPT = (ctx) => ctx.scale > 0n && ctx.precision > 20n; | ||
function parseOFOption(options) { | ||
const { overflow, maxDecimalPrecision, maxDp } = options ?? {}; | ||
if (overflow) | ||
return overflow; | ||
if (maxDecimalPrecision != null) { | ||
const checkPrecision = (ctx) => ctx.scale > 0n && ctx.precision > maxDecimalPrecision; | ||
if (maxDp == null) | ||
return checkPrecision; | ||
return (ctx) => ctx.scale > maxDp || checkPrecision(ctx); | ||
var Frac = class _Frac { | ||
static numOf(i, e = 0n) { | ||
return e >= 0n ? new _Frac(i * 10n ** e, 1n) : new _Frac(i, 10n ** -e); | ||
} | ||
if (maxDp != null) | ||
return (ctx) => ctx.scale > maxDp; | ||
return DEF_OPT; | ||
} | ||
function shouldUseFrac(options = {}) { | ||
if (options.overflow) | ||
return false; | ||
const { maxDecimalPrecision, maxDp } = options; | ||
return maxDecimalPrecision == null && maxDp == null; | ||
} | ||
var Num = class _Num { | ||
constructor(intValue, exponent, frac) { | ||
this.inf = false; | ||
if (exponent > 0n) { | ||
this.i = intValue * 10n ** exponent; | ||
this.e = 0n; | ||
this.d = 1n; | ||
constructor(numerator, denominator) { | ||
let n = numerator, d = denominator; | ||
if (d < 0n) { | ||
n = -n; | ||
d = -d; | ||
} | ||
if (d) { | ||
const g = gcd(abs(n), abs(d)); | ||
this.n = n / g; | ||
this.d = d / g; | ||
} else { | ||
this.i = intValue; | ||
this.e = exponent; | ||
this.d = 10n ** -exponent; | ||
this.n = n >= 0n ? 1n : -1n; | ||
this.d = d; | ||
} | ||
this.frac = frac; | ||
} | ||
signum() { | ||
return !this.i ? 0 : this.i > 0n ? 1 : -1; | ||
return !this.n ? 0 : this.n > 0n ? 1 : -1; | ||
} | ||
negate() { | ||
return new _Num(-this.i, this.e); | ||
return new _Frac(-this.n, this.d); | ||
} | ||
abs() { | ||
if (this.i >= 0n) | ||
if (this.n >= 0n) | ||
return this; | ||
return this.negate(); | ||
} | ||
add(augend) { | ||
const a = augend; | ||
get inf() { | ||
return !this.d; | ||
} | ||
add(a) { | ||
if (this.inf) | ||
return !a.inf || this.n === a.n ? this : null; | ||
if (a.inf) | ||
return a; | ||
const frac = Frac.add(this, a); | ||
if (frac) | ||
return frac.resolve(); | ||
this.#alignExponent(a); | ||
return new _Num(this.i + a.i, this.e); | ||
const n = this.n * a.d + a.n * this.d; | ||
const d = this.d * a.d; | ||
return new _Frac(n, d); | ||
} | ||
subtract(subtrahend) { | ||
return this.add(subtrahend.negate()); | ||
} | ||
multiply(multiplicand) { | ||
const m = multiplicand; | ||
multiply(m) { | ||
if (this.inf) | ||
return !m.n ? null : this.n >= 0 === m.n >= 0n ? INF : N_INF; | ||
if (m.inf) | ||
return m.multiply(this); | ||
return Frac.mul(this, m)?.resolve() ?? new _Num(this.i * m.i, this.e + m.e); | ||
return new _Frac(this.n * m.n, this.d * m.d); | ||
} | ||
divide(divisor, options) { | ||
if (divisor.inf) | ||
divide(d) { | ||
if (this.inf) | ||
return d.inf ? null : this.n >= 0 === d.n >= 0n ? INF : N_INF; | ||
if (d.inf) | ||
return ZERO; | ||
const overflow = parseOFOption(options); | ||
return this.#div(divisor, overflow, shouldUseFrac(options)); | ||
if (!d.n) | ||
return this.n >= 0 ? INF : N_INF; | ||
if (!this.n) | ||
return this; | ||
return new _Frac(this.n * d.d, this.d * d.n); | ||
} | ||
modulo(divisor) { | ||
if (divisor.inf) | ||
modulo(d) { | ||
if (this.inf) | ||
return null; | ||
if (d.inf) | ||
return this; | ||
if (!divisor.i) | ||
if (!d.n) | ||
return null; | ||
const times = this.#div(divisor, MOD_DIV_OPT, false); | ||
return this.subtract(divisor.multiply(times)); | ||
const times = this.divide(d).#setScale(ROUND_OPTS[0 /* trunc */]); | ||
return this.add(d.multiply(times).negate()); | ||
} | ||
pow(n, options) { | ||
if (this.inf) { | ||
if (n.inf) | ||
return n.n < 0 ? ZERO : INF; | ||
if (!n.n) | ||
return ONE; | ||
if (n.n < 0n) | ||
return ZERO; | ||
if (this.n < 0) { | ||
const hasFrac = n.modulo(ONE).compareTo(ZERO); | ||
if (hasFrac) | ||
return INF; | ||
const binary = !n.modulo(_Frac.numOf(2n)).compareTo(ZERO); | ||
if (binary) | ||
return INF; | ||
} | ||
return this; | ||
} | ||
if (n.inf) { | ||
const minus = n.signum() < 0; | ||
const minus = n.n < 0; | ||
const cmpO = this.abs().compareTo(ONE); | ||
return cmpO < 0 ? minus ? INF : ZERO : cmpO === 0 ? null : minus ? ZERO : INF; | ||
} | ||
if (n.frac) { | ||
n.frac.n.#alignExponent(n.frac.d); | ||
return this.#pow(n.frac.n.i, n.frac.d.i, options); | ||
} | ||
return this.#pow(n.i, n.d, options); | ||
return _Frac.#pow(this, n.n, n.d, options); | ||
} | ||
scaleByPowerOfTen(n) { | ||
if (this.inf) | ||
return n.inf ? n.n > 0 ? this : null : this; | ||
if (n.inf) | ||
return n.signum() < 0 ? ZERO : !this.i ? null : this.i >= 0n ? INF : N_INF; | ||
if (!(n.i % n.d)) { | ||
const bn = n.#trunc(); | ||
return new _Num(this.i, this.e + bn); | ||
} | ||
return this.multiply(new _Num(10n, 0n).pow(n)); | ||
return n.n < 0n ? ZERO : !this.n ? null : this.n >= 0n ? INF : N_INF; | ||
return this.multiply(_Frac.numOf(10n).pow(n)); | ||
} | ||
sqrt(options) { | ||
if (!this.i) | ||
return this; | ||
if (this.i < 0n) | ||
if (this.inf) | ||
return this.n < 0 ? null : INF; | ||
if (this.n < 0n) | ||
throw new Error("Negative number"); | ||
const overflow = parseOFOption(options); | ||
const decimalLength = this.#scale(); | ||
const [i, e] = this.#toNum(); | ||
const decimalLength = -e; | ||
const decimalLengthIsOdd = decimalLength & 1n; | ||
let remainder = this.#simplify().i; | ||
let remainder = i; | ||
if (decimalLengthIsOdd) | ||
@@ -207,16 +196,11 @@ remainder *= 10n; | ||
const pow = 100n ** digitExponent; | ||
let digits = 0n; | ||
let exponent = digitExponent - decimalLength / 2n - (decimalLengthIsOdd ? 1n : 0n); | ||
const overflowCtx = { | ||
get scale() { | ||
return -exponent; | ||
}, | ||
get precision() { | ||
return length(digits); | ||
} | ||
}; | ||
while (remainder > 0n && !overflow(overflowCtx)) { | ||
exponent--; | ||
const numCtx = numberContext( | ||
1n, | ||
digitExponent - decimalLength / 2n - (decimalLengthIsOdd ? 1n : 0n), | ||
options | ||
); | ||
while (remainder > 0n && !numCtx.overflow()) { | ||
numCtx.intVal *= 10n; | ||
numCtx.exponent--; | ||
remainder *= 100n; | ||
digits *= 10n; | ||
part *= 10n; | ||
@@ -229,3 +213,3 @@ if (remainder < (part + 1n) * pow) | ||
continue; | ||
digits += n; | ||
numCtx.intVal += n; | ||
remainder -= amount; | ||
@@ -236,60 +220,49 @@ part += n * 2n; | ||
} | ||
while (overflow(overflowCtx) && digits) { | ||
exponent++; | ||
digits /= 10n; | ||
} | ||
return new _Num(digits, exponent); | ||
numCtx.round(remainder); | ||
return _Frac.numOf(...numCtx.toNum()); | ||
} | ||
nthRoot(n, options) { | ||
if (this.inf) | ||
return n.inf ? ONE : n.compareTo(ONE) === 0 ? this : n.signum() < 0 ? ZERO : INF; | ||
if (n.inf) | ||
return this.pow(ZERO); | ||
if (n.abs().compareTo(ONE) === 0) | ||
return this.pow(n); | ||
if (!n.i) | ||
if (!n.n) | ||
return this.pow(INF); | ||
if (!this.i) | ||
return this; | ||
if (this.i < 0n) | ||
if (this.n < 0n) | ||
throw new Error("Negative number"); | ||
if (n.i % n.d) { | ||
return this.#pow(n.d, n.i, options); | ||
} | ||
const overflow = parseOFOption(options); | ||
return this.#nthRoot(n, overflow, shouldUseFrac(options)); | ||
return _Frac.#pow(this, n.d, n.n, options); | ||
} | ||
trunc() { | ||
return !this.i || !this.e ? this : new _Num(this.#trunc(), 0n); | ||
return this.#setScale(ROUND_OPTS[0 /* trunc */]); | ||
} | ||
round() { | ||
const mod = this.i % this.d; | ||
if (!mod) | ||
return this; | ||
const h = 10n ** (-this.e - 1n) * 5n; | ||
if (this.i < 0n) { | ||
return mod < -h ? this.floor(mod) : this.ceil(mod); | ||
} | ||
return mod < h ? this.floor(mod) : this.ceil(mod); | ||
return this.#setScale(ROUND_OPTS[1 /* round */]); | ||
} | ||
floor(mod = this.i % this.d) { | ||
if (!mod) | ||
return this; | ||
return this.i >= 0n ? this.trunc() : new _Num(this.#trunc() - 1n, 0n); | ||
floor() { | ||
return this.#setScale(ROUND_OPTS[2 /* floor */]); | ||
} | ||
ceil(mod = this.i % this.d) { | ||
if (!mod) | ||
return this; | ||
return this.i < 0n ? this.trunc() : new _Num(this.#trunc() + 1n, 0n); | ||
ceil() { | ||
return this.#setScale(ROUND_OPTS[3 /* ceil */]); | ||
} | ||
compareTo(other) { | ||
if (this.inf) | ||
return other.inf && this.n > 0 === other.n > 0 ? 0 : this.n > 0 ? 1 : -1; | ||
if (other.inf) | ||
return other.signum() > 0 ? -1 : 1; | ||
this.#alignExponent(other); | ||
return compare(this.i, other.i); | ||
return other.n > 0 ? -1 : 1; | ||
const a = this.n * other.d; | ||
const b = other.n * this.d; | ||
return compare(a, b); | ||
} | ||
toString() { | ||
if (!this.e) | ||
return String(this.i); | ||
let integer = abs(this.i); | ||
if (this.inf) | ||
return this.n > 0 ? "Infinity" : "-Infinity"; | ||
const [i, e] = this.#toNum(); | ||
if (e >= 0n) { | ||
return String(i * 10n ** e); | ||
} | ||
if (!e) | ||
return String(i); | ||
let integer = abs(i); | ||
const decimal = []; | ||
for (let n = this.e; n < 0; n++) { | ||
for (let n = e; n < 0; n++) { | ||
const last = integer % 10n; | ||
@@ -300,52 +273,51 @@ integer /= 10n; | ||
} | ||
const sign = this.i < 0n ? "-" : ""; | ||
const sign = i < 0n ? "-" : ""; | ||
return `${sign}${integer}${decimal.length ? `.${decimal.join("")}` : ""}`; | ||
} | ||
#div(divisor, overflow, useFrac) { | ||
if (!divisor.i) | ||
return this.d >= 0 ? INF : N_INF; | ||
if (!this.i) | ||
#setScale(options) { | ||
if (this.inf) | ||
return this; | ||
const frac = Frac.div(this, divisor); | ||
if (frac) | ||
return frac.resolve(overflow, useFrac); | ||
const alignMultiplicand = new _Num(10n ** divisor.#scale(), 0n); | ||
const alignedTarget = this.multiply(alignMultiplicand).#simplify(); | ||
const alignedDivisor = divisor.multiply(alignMultiplicand).#simplify(); | ||
if (!(alignedTarget.i % alignedDivisor.i)) { | ||
const candidate = new _Num( | ||
alignedTarget.i / alignedDivisor.i, | ||
alignedTarget.e - alignedDivisor.e | ||
); | ||
if (!overflow({ | ||
scale: candidate.#scale(), | ||
precision: candidate.#precision() | ||
})) | ||
return candidate; | ||
return _Frac.numOf(...this.#toNum(options)); | ||
} | ||
/** Checks whether the this fraction is a finite decimal. */ | ||
#finiteDecimal() { | ||
let t = this.d; | ||
for (const n of [1000n, 10n, 5n, 2n]) { | ||
while (t >= n) { | ||
if (t % n) | ||
break; | ||
t /= n; | ||
} | ||
} | ||
const iDivisor = abs(alignedDivisor.#trunc()); | ||
let remainder = abs(alignedTarget.i); | ||
const digitExponent = max(length(remainder) - length(iDivisor) + 1n, 0n); | ||
return t === 1n; | ||
} | ||
#toNum(options) { | ||
if (this.inf) | ||
throw new Error("Infinity"); | ||
const { n, d } = this; | ||
if (!n) | ||
return [0n, 0n]; | ||
if (!options) { | ||
options = this.#finiteDecimal() ? { | ||
overflow: () => false, | ||
roundingMode: 0 /* trunc */ | ||
} : { | ||
roundingMode: 0 /* trunc */ | ||
}; | ||
} | ||
let remainder = abs(n); | ||
const digitExponent = max(length(remainder) - length(d) + 1n, 0n); | ||
const pow = 10n ** digitExponent; | ||
let digits = 0n; | ||
let exponent = digitExponent + alignedTarget.e; | ||
const overflowCtx = { | ||
get scale() { | ||
return -exponent; | ||
}, | ||
get precision() { | ||
return length(digits); | ||
} | ||
}; | ||
while (remainder > 0n && !overflow(overflowCtx)) { | ||
exponent--; | ||
const numCtx = numberContext(n < 0n ? -1n : 1n, digitExponent, options); | ||
while (remainder > 0n && !numCtx.overflow()) { | ||
numCtx.intVal *= 10n; | ||
numCtx.exponent--; | ||
remainder *= 10n; | ||
digits *= 10n; | ||
if (remainder < iDivisor * pow) | ||
if (remainder < d * pow) | ||
continue; | ||
for (let n = 9n; n > 0n; n--) { | ||
const amount = iDivisor * n * pow; | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = d * nn * pow; | ||
if (remainder < amount) | ||
continue; | ||
digits += n; | ||
numCtx.intVal += nn; | ||
remainder -= amount; | ||
@@ -355,30 +327,19 @@ break; | ||
} | ||
let lost = !remainder; | ||
while (overflow(overflowCtx) && digits) { | ||
exponent++; | ||
digits /= 10n; | ||
lost = true; | ||
} | ||
if (divisor.signum() !== this.signum()) | ||
digits = -digits; | ||
return new _Num( | ||
digits, | ||
exponent, | ||
useFrac && lost ? Frac.valueOf(this, divisor, overflow) : void 0 | ||
); | ||
numCtx.round(remainder); | ||
return numCtx.toNum(); | ||
} | ||
/** pow() for fraction */ | ||
#pow(numerator, denominator, options) { | ||
const sign = numerator >= 0n === denominator >= 0n ? 1n : -1n; | ||
let n = abs(numerator); | ||
let d = abs(denominator); | ||
static #pow(base, num, denom, options) { | ||
if (!num) | ||
return ONE; | ||
if (!base.n) | ||
return ZERO; | ||
const sign = num >= 0n === denom >= 0n ? 1n : -1n; | ||
let n = abs(num); | ||
let d = abs(denom); | ||
const m = n / d; | ||
n %= d; | ||
if (!m && !n) | ||
return ONE; | ||
if (!this.i) | ||
return this; | ||
let a = new _Num(this.i ** m, this.e * m); | ||
let a = new _Frac(base.n ** m, base.d ** m); | ||
if (n % d) { | ||
if (this.i < 0n) | ||
if (base.n < 0n) | ||
return null; | ||
@@ -388,20 +349,15 @@ const g = gcd(n, d); | ||
d /= g; | ||
a = a.multiply( | ||
this.#nthRoot( | ||
new _Num(d, 0n), | ||
parseOFOption(options), | ||
shouldUseFrac(options) | ||
).pow(new _Num(n, 0n)) | ||
); | ||
a = a.multiply(_Frac.#nthRoot(base, d, options).pow(_Frac.numOf(n))); | ||
} | ||
if (sign >= 0n) | ||
return a; | ||
return ONE.#div(a, parseOFOption(options), shouldUseFrac(options)); | ||
return ONE.divide(a); | ||
} | ||
#nthRoot(n, overflow, useFrac) { | ||
const iN = n.abs().#trunc(); | ||
static #nthRoot(base, n, options) { | ||
const iN = abs(n); | ||
const powOfTen = 10n ** iN; | ||
const decimalLength = this.#scale(); | ||
const [i, e] = base.#toNum(); | ||
const decimalLength = -e; | ||
const mod = decimalLength % iN; | ||
let remainder = this.#simplify().i; | ||
let remainder = i; | ||
if (mod) | ||
@@ -412,16 +368,11 @@ remainder *= 10n ** (iN - mod); | ||
const pow = powOfTen ** digitExponent; | ||
let digits = 0n; | ||
let exponent = digitExponent - decimalLength / iN - (mod ? 1n : 0n); | ||
const overflowCtx = { | ||
get scale() { | ||
return -exponent; | ||
}, | ||
get precision() { | ||
return length(digits); | ||
} | ||
}; | ||
while (remainder > 0n && !overflow(overflowCtx)) { | ||
exponent--; | ||
const numCtx = numberContext( | ||
1n, | ||
digitExponent - decimalLength / iN - (mod ? 1n : 0n), | ||
options | ||
); | ||
while (remainder > 0n && !numCtx.overflow()) { | ||
numCtx.intVal *= 10n; | ||
numCtx.exponent--; | ||
remainder *= powOfTen; | ||
digits *= 10n; | ||
table.prepare(); | ||
@@ -432,143 +383,77 @@ for (let nn = 9n; nn > 0n; nn--) { | ||
continue; | ||
digits += nn; | ||
numCtx.intVal += nn; | ||
remainder -= amount; | ||
table.set(digits); | ||
table.set(numCtx.intVal); | ||
break; | ||
} | ||
} | ||
while (overflow(overflowCtx) && digits) { | ||
exponent++; | ||
digits /= 10n; | ||
} | ||
const a = new _Num(digits, exponent); | ||
if (n.i >= 0n) | ||
numCtx.round(remainder); | ||
const a = _Frac.numOf(...numCtx.toNum()); | ||
if (i >= 0n) | ||
return a; | ||
return ONE.#div(a, overflow, useFrac); | ||
return ONE.divide(a); | ||
} | ||
#scale() { | ||
return -this.#simplify().e; | ||
} | ||
#precision() { | ||
let n = this.i; | ||
while (!(n % 10n)) | ||
n /= 10n; | ||
return length(n); | ||
} | ||
#simplify() { | ||
if (!this.e) | ||
return this; | ||
const newE = this.e + length(this.i) - this.#precision(); | ||
this.#updateExponent(min(newE, 0n)); | ||
return this; | ||
} | ||
#trunc() { | ||
return !this.e ? this.i : this.i / this.d; | ||
} | ||
#alignExponent(other) { | ||
if (this.e === other.e) | ||
return; | ||
if (this.e > other.e) { | ||
this.#updateExponent(other.e); | ||
} else { | ||
other.#updateExponent(this.e); | ||
} | ||
} | ||
#updateExponent(exponent) { | ||
if (this.e === exponent) | ||
return; | ||
const diff = this.e - exponent; | ||
if (diff > 0) | ||
this.i *= 10n ** diff; | ||
else | ||
this.i /= 10n ** -diff; | ||
this.e = exponent; | ||
this.d = 10n ** -exponent; | ||
} | ||
static div(dividend, divisor, overflow, useFrac) { | ||
return dividend.#div(divisor, overflow, useFrac); | ||
} | ||
}; | ||
var ZERO = new Num(0n, 0n); | ||
var ONE = new Num(1n, 0n); | ||
// src/inf.mts | ||
var Inf = class { | ||
constructor(sign) { | ||
this.inf = true; | ||
this.s = sign; | ||
} | ||
signum() { | ||
return this.s; | ||
} | ||
negate() { | ||
return this.s === 1 ? N_INF : INF; | ||
} | ||
add(augend) { | ||
return !augend.inf || this === augend ? this : null; | ||
} | ||
subtract(subtrahend) { | ||
return this.add(subtrahend.negate()); | ||
} | ||
multiply(multiplicand) { | ||
return !multiplicand.signum() ? null : multiplicand.signum() === this.s ? INF : N_INF; | ||
} | ||
divide(divisor) { | ||
return divisor.inf ? null : divisor.signum() >= 0n === this.s >= 0 ? INF : N_INF; | ||
} | ||
modulo() { | ||
return null; | ||
} | ||
pow(n) { | ||
if (n.inf) | ||
return n.s < 0 ? ZERO : INF; | ||
if (!n.signum()) | ||
return ONE; | ||
if (n.signum() < 0n) | ||
return ZERO; | ||
if (this.s < 0) { | ||
const hasFrac = n.modulo(ONE).compareTo(ZERO); | ||
if (hasFrac) | ||
return INF; | ||
const binary = !n.modulo(new Num(2n, 0n)).compareTo(ZERO); | ||
if (binary) | ||
return INF; | ||
var ZERO = Frac.numOf(0n); | ||
var ONE = Frac.numOf(1n); | ||
var INF = new Frac(1n, 0n); | ||
var N_INF = new Frac(-1n, 0n); | ||
function parseOptions(options) { | ||
const { overflow, roundingMode } = options ?? {}; | ||
return { | ||
overflow: overflow ?? DEF_OPT, | ||
roundingMode: roundingMode ?? 0 /* trunc */ | ||
}; | ||
} | ||
function numberContext(sign, initExponent, options) { | ||
const { overflow, roundingMode } = parseOptions(options); | ||
const ctx = { | ||
intVal: 0n, | ||
exponent: initExponent, | ||
overflow: () => overflow(overflowCtx), | ||
toNum() { | ||
const intVal = sign >= 0 ? this.intVal : -this.intVal; | ||
return this.exponent >= 0n ? [intVal * 10n ** this.exponent, 0n] : [intVal, this.exponent]; | ||
}, | ||
round(remainder) { | ||
const nextDigits = []; | ||
while (overflow(overflowCtx) && (ctx.intVal || !nextDigits.length)) { | ||
ctx.exponent++; | ||
nextDigits.push(ctx.intVal % 10n); | ||
ctx.intVal /= 10n; | ||
} | ||
if (roundingMode === 0 /* trunc */) | ||
return; | ||
if (!remainder && !nextDigits.some((r) => r)) | ||
return; | ||
switch (roundingMode) { | ||
case 1 /* round */: { | ||
const last = nextDigits.pop(); | ||
if (last >= 5n && (sign > 0n || last > 5n || remainder || nextDigits.some((r) => r))) | ||
ctx.intVal++; | ||
break; | ||
} | ||
case 2 /* floor */: | ||
if (sign < 0n) | ||
ctx.intVal++; | ||
break; | ||
case 3 /* ceil */: | ||
if (sign > 0n) | ||
ctx.intVal++; | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
return this; | ||
} | ||
sqrt() { | ||
return this.s < 0 ? null : INF; | ||
} | ||
nthRoot(n) { | ||
return n.inf ? ONE : n.compareTo(ONE) === 0 ? this : n.signum() < 0 ? ZERO : INF; | ||
} | ||
scaleByPowerOfTen(n) { | ||
return n.inf ? n.s > 0 ? this : null : this; | ||
} | ||
compareTo(n) { | ||
return n.inf && this.s === n.s ? 0 : this.s > 0 ? 1 : -1; | ||
} | ||
abs() { | ||
return INF; | ||
} | ||
trunc() { | ||
return this; | ||
} | ||
round() { | ||
return this; | ||
} | ||
ceil() { | ||
return this; | ||
} | ||
floor() { | ||
return this; | ||
} | ||
toString() { | ||
return String(this.toNum()); | ||
} | ||
toNum() { | ||
return this.s === 1 ? Infinity : -Infinity; | ||
} | ||
}; | ||
var INF = new Inf(1); | ||
var N_INF = new Inf(-1); | ||
}; | ||
const overflowCtx = { | ||
get scale() { | ||
return -ctx.exponent; | ||
}, | ||
get precision() { | ||
return length(ctx.intVal); | ||
} | ||
}; | ||
return ctx; | ||
} | ||
@@ -580,3 +465,3 @@ // src/index.mts | ||
return null; | ||
if (value instanceof Num || value instanceof Inf) | ||
if (value instanceof Frac) | ||
return value; | ||
@@ -588,3 +473,5 @@ if (value === Infinity) | ||
const prop = parsePrimValue(value); | ||
return prop && new Num(prop.intValue, prop.exponent ?? 0n); | ||
if (!prop) | ||
return null; | ||
return Frac.numOf(prop.intValue, prop.exponent ?? 0n); | ||
} | ||
@@ -643,4 +530,3 @@ function parsePrimValue(value) { | ||
subtract(subtrahend) { | ||
const b = valueOf(subtrahend).#p; | ||
return this.#val(b && this.#p?.subtract(b)); | ||
return this.add(valueOf(subtrahend).negate()); | ||
} | ||
@@ -655,5 +541,5 @@ /** Returns a BigNum whose value is (this × multiplicand). */ | ||
*/ | ||
divide(divisor, options) { | ||
divide(divisor) { | ||
const b = valueOf(divisor).#p; | ||
return this.#val(b && this.#p?.divide(b, options)); | ||
return this.#val(b && this.#p?.divide(b)); | ||
} | ||
@@ -729,3 +615,3 @@ /** | ||
if (this.#p.inf) | ||
return this.#p.toNum(); | ||
return this.#p.signum() > 0 ? Infinity : -Infinity; | ||
const str = this.toString(); | ||
@@ -743,3 +629,4 @@ const num = Number(str); | ||
export { | ||
BigNum | ||
BigNum, | ||
RoundingMode | ||
}; |
{ | ||
"name": "@bignum/core", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "Arbitrary-precision decimal arithmetic with BigInt.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -46,25 +46,6 @@ # @bignum/core | ||
### BigNum.prototype.divide(divisor, [options]): BigNum | ||
### BigNum.prototype.divide(divisor): BigNum | ||
Returns a BigNum whose value is (this / divisor). | ||
An object can be given as an option. | ||
Options: | ||
| Name | Type | Description | | ||
| :---------------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| overflow | `(context)=>boolean` | You can specify an overflow test function. By default, if there are decimals and the number of precisions exceeds 20, it is considered an overflow. | | ||
| ~~maxDp~~ | bigint | **Deprecated**. The maximum number of decimal places. | | ||
| ~~maxDecimalPrecision~~ | bigint | **Deprecated**. The maximum number of precision when having decimals. | | ||
- `context` parameter | ||
An object that contains the following properties: | ||
| Name | Type | Description | | ||
| :-------- | :----- | :---------------------------- | | ||
| scale | bigint | The number of decimal places. | | ||
| precision | bigint | The number of precision. | | ||
### BigNum.prototype.modulo(divisor): BigNum | ||
@@ -78,7 +59,7 @@ | ||
### BigNum.prototype.pow(n, [options]): BigNum | ||
### BigNum.prototype.pow(n, [options: MathOption]): BigNum | ||
Returns a BigNum whose value is (this \*\* n). | ||
An object can be given as an option. It's the same option for `divide()`. This is used in negative, or fraction pows. | ||
An object can be given as an option. This is used in negative, or fraction pows. See [MathOption](#type-mathoption). | ||
@@ -89,3 +70,3 @@ ### BigNum.prototype.scaleByPowerOfTen(n): BigNum | ||
### BigNum.prototype.sqrt([options]): BigNum | ||
### BigNum.prototype.sqrt([options: MathOption]): BigNum | ||
@@ -96,5 +77,5 @@ Returns an approximation to the square root of this BigNum. | ||
An object can be given as an option. It's the same option for `divide()`. | ||
An object can be given as an option. See [MathOption](#type-mathoption). | ||
### BigNum.prototype.nthRoot(n, [options]): BigNum | ||
### BigNum.prototype.nthRoot(n, [options: MathOption]): BigNum | ||
@@ -106,3 +87,3 @@ Returns an approximation to the `n`th root of this BigNum. | ||
An object can be given as an option. It's the same option for `divide()`. | ||
An object can be given as an option. See [MathOption](#type-mathoption). | ||
@@ -145,2 +126,29 @@ ### BigNum.prototype.abs(): BigNum | ||
### type MathOption | ||
Options: | ||
| Name | Type | Description | | ||
| :----------- | :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| overflow | `(context)=>boolean` | You can specify an overflow test function. By default, if there are decimals and the number of precisions exceeds 20, it is considered an overflow. | | ||
| roundingMode | [RoundingMode](#enum-roundingmode).trunc` | Specifies a rounding behavior for numerical operations capable of discarding precision. | | ||
- `context` parameter | ||
An object that contains the following properties: | ||
| Name | Type | Description | | ||
| :-------- | :----- | :---------------------------- | | ||
| scale | bigint | The number of decimal places. | | ||
| precision | bigint | The number of precision. | | ||
### enum RoundingMode | ||
The following enumerated values are available: | ||
- `RoundingMode.trunc` | ||
- `RoundingMode.round` | ||
- `RoundingMode.floor` | ||
- `RoundingMode.ceil` | ||
## 🛸 Prior Art | ||
@@ -147,0 +155,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
157
47564
1349