@bignum/core
Advanced tools
Comparing version 0.10.1 to 0.11.0
@@ -25,73 +25,46 @@ type OverflowContext = { | ||
declare class Frac { | ||
#private; | ||
/** numerator */ | ||
private readonly n; | ||
readonly n: bigint; | ||
/** denominator */ | ||
private readonly d; | ||
readonly d: bigint; | ||
static numOf(i: bigint, e?: bigint): Frac; | ||
constructor(numerator: bigint, denominator: bigint); | ||
signum(): 1 | 0 | -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; | ||
} | ||
declare class BigNum { | ||
declare class BigNum$1 { | ||
#private; | ||
static readonly valueOf: typeof valueOf; | ||
constructor(value: string | number | bigint | boolean | BigNum | Frac | null | undefined); | ||
static valueOf(value: string | number | bigint | boolean | BigNum$1): BigNum$1; | ||
constructor(value: string | number | bigint | boolean | BigNum$1 | Frac | null | undefined); | ||
/** Returns a number indicating the sign. */ | ||
signum(): -1 | 0 | 1 | typeof NaN; | ||
/** Returns a BigNum whose value is (-this). */ | ||
negate(): BigNum; | ||
/** Returns a BigNum whose value is (this + augend) */ | ||
add(augend: BigNum | string | number | bigint): BigNum; | ||
/** Returns a BigNum whose value is (this - subtrahend) */ | ||
subtract(subtrahend: BigNum | string | number | bigint): BigNum; | ||
/** Returns a BigNum whose value is (this × multiplicand). */ | ||
multiply(multiplicand: BigNum | string | number | bigint): BigNum; | ||
/** Returns a BigNum whose value is `-this`. */ | ||
negate(): this; | ||
/** Returns a BigNum whose value is `this + augend`. */ | ||
add(augend: BigNum$1 | string | number | bigint): this; | ||
/** Returns a BigNum whose value is `this - subtrahend` */ | ||
subtract(subtrahend: BigNum$1 | string | number | bigint): this; | ||
/** Returns a BigNum whose value is `this × multiplicand`. */ | ||
multiply(multiplicand: BigNum$1 | string | number | bigint): this; | ||
/** | ||
* Returns a BigNum whose value is (this / divisor) | ||
* Returns a BigNum whose value is `this / divisor` | ||
*/ | ||
divide(divisor: BigNum | string | number | bigint): BigNum; | ||
divide(divisor: BigNum$1 | string | number | bigint): this; | ||
/** | ||
* Returns a BigNum whose value is (this % divisor) | ||
* Returns a BigNum whose value is `this % divisor` | ||
*/ | ||
modulo(divisor: BigNum | string | number | bigint): BigNum; | ||
/** | ||
* Returns a BigNum whose value is (this**n). | ||
*/ | ||
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?: MathOptions): BigNum; | ||
/** Returns an approximation to the nth root of this. */ | ||
nthRoot(n: BigNum | string | number | bigint, options?: MathOptions): BigNum; | ||
modulo(divisor: BigNum$1 | string | number | bigint): this; | ||
/** Returns a BigNum whose value is the absolute value of this BigNum. */ | ||
abs(): BigNum; | ||
abs(): this; | ||
/** Returns a BigNum that is the integral part of this BigNum, with removing any fractional digits. */ | ||
trunc(): BigNum; | ||
trunc(): this; | ||
/** Returns this BigNum rounded to the nearest integer. */ | ||
round(): BigNum; | ||
round(): this; | ||
/** Returns the greatest integer less than or equal to this BigNum. */ | ||
floor(): BigNum; | ||
floor(): this; | ||
/** Returns the smallest integer greater than or equal to this BigNum. */ | ||
ceil(): BigNum; | ||
ceil(): this; | ||
/** Compares this BigNum with the specified BigNum. */ | ||
compareTo(other: BigNum | string | number | bigint): 0 | -1 | 1 | typeof NaN; | ||
compareTo(other: BigNum$1 | string | number | bigint): 0 | -1 | 1 | typeof NaN; | ||
isNaN(): boolean; | ||
@@ -102,5 +75,15 @@ isFinite(): boolean; | ||
} | ||
/** Get a BigNum from the given value */ | ||
declare function valueOf(value: string | number | bigint | boolean | BigNum): BigNum; | ||
export { BigNum, type IsOverflow, type MathOptions, type OverflowContext, RoundingMode }; | ||
declare class BigNum extends BigNum$1 { | ||
static valueOf(value: string | number | bigint | boolean | BigNum): BigNum; | ||
/** Returns a BigNum whose value is `this**n`. */ | ||
pow(n: BigNum | string | number | bigint, options?: MathOptions): this; | ||
/** Returns a BigNum whose numerical value is equal to `this * 10 ** n`. */ | ||
scaleByPowerOfTen(n: BigNum | string | number | bigint): this; | ||
/** Returns an approximation to the square root of this. */ | ||
sqrt(options?: MathOptions): this; | ||
/** Returns an approximation to the nth root of this. */ | ||
nthRoot(n: BigNum | string | number | bigint, options?: MathOptions): this; | ||
} | ||
export { BigNum, BigNum$1 as BigNumBasic, type IsOverflow, type MathOptions, type OverflowContext, RoundingMode }; |
807
lib/index.js
@@ -1,37 +0,1 @@ | ||
// src/nth-root-utils.mts | ||
function createNthRootTable(nth) { | ||
const times = createPascalTriangleLineBy(nth); | ||
const parts = times.map(() => 0n); | ||
const len = parts.length; | ||
return { | ||
prepare() { | ||
let tmp = 1n; | ||
for (let i = 0; i < len; i++) | ||
parts[i] *= tmp *= 10n; | ||
}, | ||
amount(n) { | ||
return parts.reduce((a, v) => (a + v) * n, n); | ||
}, | ||
set(value) { | ||
let tmp = 1n; | ||
for (let i = 0; i < len; i++) | ||
parts[i] = (tmp *= value) * times[i]; | ||
} | ||
}; | ||
} | ||
var pascalTriangle = [[]]; | ||
function createPascalTriangleLineBy(n) { | ||
const result = pascalTriangle[Number(n) - 1]; | ||
if (result) | ||
return result; | ||
let table = pascalTriangle.at(-1); | ||
for (let i = pascalTriangle.length; i < n; i++) { | ||
let p = 1n; | ||
table = table.map((v) => p + (p = v)); | ||
table.push(p + 1n); | ||
pascalTriangle.push(table); | ||
} | ||
return table; | ||
} | ||
// src/options.mts | ||
@@ -46,3 +10,3 @@ var RoundingMode = /* @__PURE__ */ ((RoundingMode2) => { | ||
// src/util.mts | ||
// src/frac/util.mts | ||
function compare(a, b) { | ||
@@ -72,8 +36,47 @@ return a === b ? 0 : a > b ? 1 : -1; | ||
// 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; | ||
// src/frac/divide-digits.mts | ||
function divideDigits(n, d) { | ||
const e = BigInt(length(n) - length(d)); | ||
let initRemainder = abs(n); | ||
let initRemainderPow = 1n; | ||
if (e >= 0n) | ||
initRemainderPow = 10n ** e; | ||
else | ||
initRemainder = initRemainder * 10n ** -e; | ||
let remainder = initRemainder; | ||
const dd = { | ||
e, | ||
hasRemainder: () => remainder > 0n, | ||
*digits(infinity) { | ||
remainder = initRemainder; | ||
let pow2 = initRemainderPow; | ||
while (remainder > 0n) { | ||
let digit = 0n; | ||
if ( | ||
// Short circuit: If 1 is not available, it will not loop. | ||
remainder >= d * pow2 | ||
) { | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = d * nn * pow2; | ||
if (remainder < amount) | ||
continue; | ||
digit = nn; | ||
remainder -= amount; | ||
break; | ||
} | ||
} | ||
yield digit; | ||
if (pow2 >= 10n) | ||
pow2 /= 10n; | ||
else | ||
remainder *= 10n; | ||
} | ||
while (infinity) | ||
yield 0n; | ||
} | ||
}; | ||
return dd; | ||
} | ||
// src/frac/frac.mts | ||
var Frac = class _Frac { | ||
@@ -103,115 +106,5 @@ static numOf(i, e = 0n) { | ||
} | ||
signum() { | ||
return !this.n ? 0 : this.n > 0n ? 1 : -1; | ||
} | ||
negate() { | ||
return new _Frac(-this.n, this.d); | ||
} | ||
abs() { | ||
if (this.n >= 0n) | ||
return this; | ||
return this.negate(); | ||
} | ||
get inf() { | ||
return !this.d; | ||
} | ||
add(a) { | ||
if (this.inf && a.inf && this.n !== a.n) | ||
return null; | ||
return this.d === a.d ? new _Frac(this.n + a.n, this.d) : new _Frac(this.n * a.d + a.n * this.d, this.d * a.d); | ||
} | ||
multiply(m) { | ||
if (this.inf && !m.n || m.inf && !this.n) | ||
return null; | ||
return new _Frac(this.n * m.n, this.d * m.d); | ||
} | ||
divide(d) { | ||
if (this.inf && d.inf) | ||
return null; | ||
return d.n >= 0n ? new _Frac(this.n * d.d, this.d * d.n) : new _Frac(this.n * -d.d, this.d * -d.n); | ||
} | ||
modulo(d) { | ||
if (this.inf || !d.n) | ||
return null; | ||
if (d.inf) | ||
return this; | ||
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 < 0n ? ZERO : INF; | ||
if (!n.n) | ||
return ONE; | ||
if (n.n < 0n) | ||
return ZERO; | ||
if (this.n < 0n) { | ||
const hasFrac = n.d > 1n; | ||
if (hasFrac) | ||
return INF; | ||
if (isEven(n.n)) | ||
return INF; | ||
} | ||
return this; | ||
} | ||
if (n.inf) { | ||
const minus = n.n < 0n; | ||
const cmpO = this.abs().compareTo(ONE); | ||
return cmpO < 0 ? minus ? INF : ZERO : !cmpO ? null : minus ? ZERO : INF; | ||
} | ||
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.n < 0n ? ZERO : !this.n ? null : this.n >= 0n ? INF : N_INF; | ||
return this.multiply(TEN.pow(n)); | ||
} | ||
sqrt(options) { | ||
if (this.inf) | ||
return this.n < 0n ? null : INF; | ||
if (!this.n) | ||
return ZERO; | ||
if (this.n < 0n) | ||
return null; | ||
return _Frac.#sqrt(this, options); | ||
} | ||
nthRoot(n, options) { | ||
if (this.inf) | ||
return n.inf ? ONE : !n.compareTo(ONE) ? this : n.signum() < 0 ? ZERO : INF; | ||
if (n.d === 1n && n.n === 2n) | ||
return this.sqrt(options); | ||
if (n.inf) | ||
return this.pow(ZERO); | ||
if (!n.abs().compareTo(ONE)) | ||
return this.pow(n); | ||
if (!n.n) | ||
return this.pow(INF); | ||
if (this.n < 0n) | ||
return null; | ||
return _Frac.#pow(this, n.d, n.n, options); | ||
} | ||
trunc() { | ||
return this.#setScale(ROUND_OPTS[0 /* trunc */]); | ||
} | ||
round() { | ||
return this.#setScale(ROUND_OPTS[1 /* round */]); | ||
} | ||
floor() { | ||
return this.#setScale(ROUND_OPTS[2 /* floor */]); | ||
} | ||
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.n > 0 ? -1 : 1; | ||
if (this.d === other.d) | ||
return compare(this.n, other.n); | ||
return compare(this.n * other.d, other.n * this.d); | ||
} | ||
toString() { | ||
@@ -222,3 +115,3 @@ if (this.inf) | ||
return String(this.n); | ||
const div = divide(this.n, this.d); | ||
const div = divideDigits(this.n, this.d); | ||
let e = div.e; | ||
@@ -228,3 +121,3 @@ let integer = ""; | ||
let decimal = ""; | ||
const isFull = this.#finiteDecimal() ? () => false : () => decimal.length && integer.length + (integer.length && decimalLeadingZero.length) + decimal.length >= 20; | ||
const isFull = finiteDecimal(this) ? () => false : () => decimal.length && integer.length + (integer.length && decimalLeadingZero.length) + decimal.length >= 20; | ||
for (const d of div.digits()) { | ||
@@ -246,143 +139,26 @@ if (e-- >= 0n) { | ||
} | ||
#setScale(options) { | ||
if (this.inf) | ||
return this; | ||
const { n, d } = this; | ||
if (!n) | ||
return ZERO; | ||
if (d === 1n) | ||
return this; | ||
const div = divide(n, d); | ||
const numCtx = numberContext(n < 0n ? -1 : 1, div.e, options); | ||
for (const digit of div.digits()) { | ||
numCtx.set(digit); | ||
if (numCtx.overflow()) | ||
break; | ||
numCtx.prepareNext(); | ||
} | ||
numCtx.round(div.hasRemainder()); | ||
return _Frac.numOf(...numCtx.toNum()); | ||
} | ||
/** 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; | ||
} | ||
} | ||
return t === 1n; | ||
} | ||
/** pow() for fraction */ | ||
static #pow(base, num, denom, options) { | ||
if (!num) | ||
return ONE; | ||
const sign = num >= 0n === denom >= 0n ? 1 : -1; | ||
if (!base.n) | ||
return sign < 0 ? INF : ZERO; | ||
const n = abs(num); | ||
const d = abs(denom); | ||
const i = n / d; | ||
const remN = n % d; | ||
let a = [base.n ** i, base.d ** i]; | ||
if (remN) { | ||
if (base.n < 0n) | ||
return null; | ||
const nthRoot = _Frac.#nthRoot(base, d, options); | ||
a = [a[0] * nthRoot.n ** remN, a[1] * nthRoot.d ** remN]; | ||
} | ||
if (sign >= 0) | ||
return new _Frac(a[0], a[1]); | ||
return new _Frac(a[1], a[0]); | ||
} | ||
static #sqrt(base, options) { | ||
const div = divide(base.n, base.d); | ||
const numCtx = numberContext( | ||
1, | ||
div.e / 2n + (div.e >= 0n || isEven(div.e) ? 0n : -1n), | ||
options | ||
); | ||
let remainder = 0n; | ||
let part = 0n; | ||
const bf = []; | ||
if (isEven(div.e)) | ||
bf.push(0n); | ||
for (const digit of div.digits(true)) { | ||
if (bf.push(digit) < 2) | ||
continue; | ||
remainder = remainder * 100n + bf.shift() * 10n + bf.shift(); | ||
part *= 10n; | ||
if ( | ||
// Short circuit: If 1 is not available, it will not loop. | ||
remainder >= part + 1n | ||
) { | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = (part + nn) * nn; | ||
if (remainder < amount) | ||
continue; | ||
numCtx.set(nn); | ||
remainder -= amount; | ||
part += nn * 2n; | ||
break; | ||
} | ||
} | ||
if (numCtx.overflow()) | ||
break; | ||
numCtx.prepareNext(); | ||
} | ||
numCtx.round(remainder > 0n); | ||
return _Frac.numOf(...numCtx.toNum()); | ||
} | ||
static #nthRoot(base, n, options) { | ||
const iN = abs(n); | ||
const div = divide(base.n, base.d); | ||
const numCtx = numberContext( | ||
1, | ||
div.e / iN + (div.e >= 0n || !(div.e % iN) ? 0n : -1n), | ||
options | ||
); | ||
let remainder = 0n; | ||
const powOfTen = 10n ** iN; | ||
const table = createNthRootTable(iN); | ||
const bf = []; | ||
const initBfLen = div.e >= 0n ? iN - (abs(div.e) % iN + 1n) : abs(div.e + 1n) % iN; | ||
while (bf.length < initBfLen) | ||
bf.push(0n); | ||
for (const digit of div.digits(true)) { | ||
if (bf.push(digit) < iN) | ||
continue; | ||
remainder *= powOfTen; | ||
let bfPow = powOfTen; | ||
while (bf.length) | ||
remainder += bf.shift() * (bfPow /= 10n); | ||
table.prepare(); | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = table.amount(nn); | ||
if (remainder < amount) | ||
continue; | ||
numCtx.set(nn); | ||
remainder -= amount; | ||
table.set(numCtx.getInt()); | ||
break; | ||
} | ||
if (numCtx.overflow()) | ||
break; | ||
numCtx.prepareNext(); | ||
} | ||
numCtx.round(remainder > 0n); | ||
const a = _Frac.numOf(...numCtx.toNum()); | ||
if (n >= 0n) | ||
return a; | ||
return new _Frac(a.d, a.n); | ||
} | ||
}; | ||
var init = false; | ||
var ZERO = Frac.numOf(0n); | ||
var ONE = Frac.numOf(1n); | ||
var TEN = Frac.numOf(10n); | ||
var INF = new Frac(1n, 0n); | ||
var N_INF = new Frac(-1n, 0n); | ||
init = true; | ||
function finiteDecimal(x) { | ||
let t = x.d; | ||
for (const n of [1000n, 10n, 5n, 2n]) { | ||
while (t >= n) { | ||
if (t % n) | ||
break; | ||
t /= n; | ||
} | ||
} | ||
return t === 1n; | ||
} | ||
// src/frac/number-context.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 assertNever(value, message) { | ||
@@ -456,46 +232,249 @@ throw new Error(`${message}: ${JSON.stringify(value)}`); | ||
} | ||
function divide(n, d) { | ||
const e = BigInt(length(n) - length(d)); | ||
let initRemainder = abs(n); | ||
let initRemainderPow = 1n; | ||
if (e >= 0n) | ||
initRemainderPow = 10n ** e; | ||
else | ||
initRemainder = initRemainder * 10n ** -e; | ||
let remainder = initRemainder; | ||
const ctx = { | ||
e, | ||
hasRemainder: () => remainder > 0n, | ||
*digits(infinity) { | ||
remainder = initRemainder; | ||
let pow = initRemainderPow; | ||
while (remainder > 0n) { | ||
let digit = 0n; | ||
if ( | ||
// Short circuit: If 1 is not available, it will not loop. | ||
remainder >= d * pow | ||
) { | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = d * nn * pow; | ||
if (remainder < amount) | ||
continue; | ||
digit = nn; | ||
remainder -= amount; | ||
break; | ||
} | ||
} | ||
yield digit; | ||
if (pow >= 10n) | ||
pow /= 10n; | ||
else | ||
remainder *= 10n; | ||
} | ||
while (infinity) | ||
yield 0n; | ||
// src/frac/operations/basic.mts | ||
function signum(x) { | ||
return !x.n ? 0 : x.n > 0n ? 1 : -1; | ||
} | ||
function negate(x) { | ||
return new Frac(-x.n, x.d); | ||
} | ||
function abs2(x) { | ||
if (x.n >= 0n) | ||
return x; | ||
return negate(x); | ||
} | ||
function add(x, y) { | ||
return x.inf && y.inf && x.n !== y.n ? null : x.d === y.d ? new Frac(x.n + y.n, x.d) : new Frac(x.n * y.d + y.n * x.d, x.d * y.d); | ||
} | ||
function multiply(x, y) { | ||
return x.inf && !y.n || y.inf && !x.n ? null : new Frac(x.n * y.n, x.d * y.d); | ||
} | ||
function divide(x, y) { | ||
return x.inf && y.inf ? null : y.n >= 0n ? new Frac(x.n * y.d, x.d * y.n) : new Frac(x.n * -y.d, x.d * -y.n); | ||
} | ||
function modulo(x, y) { | ||
return x.inf || !y.n ? null : y.inf ? x : add( | ||
x, | ||
negate( | ||
multiply(y, round(divide(x, y), ROUND_OPTS[0 /* trunc */])) | ||
) | ||
); | ||
} | ||
function compareTo(x, y) { | ||
return x.d === y.d ? compare(x.n, y.n) : x.inf ? x.n > 0 ? 1 : -1 : y.inf ? y.n > 0 ? -1 : 1 : compare(x.n * y.d, y.n * x.d); | ||
} | ||
function round(x, options) { | ||
if (x.inf) | ||
return x; | ||
const { n, d } = x; | ||
if (!n) | ||
return ZERO; | ||
if (d === 1n) | ||
return x; | ||
const div = divideDigits(n, d); | ||
const numCtx = numberContext(n < 0n ? -1 : 1, div.e, options); | ||
for (const digit of div.digits()) { | ||
numCtx.set(digit); | ||
if (numCtx.overflow()) | ||
break; | ||
numCtx.prepareNext(); | ||
} | ||
numCtx.round(div.hasRemainder()); | ||
return Frac.numOf(...numCtx.toNum()); | ||
} | ||
// src/frac/nth-root-utils.mts | ||
function createNthRootTable(nth) { | ||
const times = createPascalTriangleLineBy(nth); | ||
const parts = times.map(() => 0n); | ||
const len = parts.length; | ||
return { | ||
prepare() { | ||
let tmp = 1n; | ||
for (let i = 0; i < len; i++) | ||
parts[i] *= tmp *= 10n; | ||
}, | ||
amount(n) { | ||
return parts.reduce((a, v) => (a + v) * n, n); | ||
}, | ||
set(value) { | ||
let tmp = 1n; | ||
for (let i = 0; i < len; i++) | ||
parts[i] = (tmp *= value) * times[i]; | ||
} | ||
}; | ||
return ctx; | ||
} | ||
var pascalTriangle = [[]]; | ||
function createPascalTriangleLineBy(n) { | ||
const result = pascalTriangle[Number(n) - 1]; | ||
if (result) | ||
return result; | ||
let table = pascalTriangle.at(-1); | ||
for (let i = pascalTriangle.length; i < n; i++) { | ||
let p = 1n; | ||
table = table.map((v) => p + (p = v)); | ||
table.push(p + 1n); | ||
pascalTriangle.push(table); | ||
} | ||
return table; | ||
} | ||
// src/index.mts | ||
// src/frac/operations/arithmetic.mts | ||
var ONE = Frac.numOf(1n); | ||
var TEN = Frac.numOf(10n); | ||
function pow(x, n, options) { | ||
if (x.inf) { | ||
if (n.inf) | ||
return n.n < 0n ? ZERO : INF; | ||
if (!n.n) | ||
return ONE; | ||
if (n.n < 0n) | ||
return ZERO; | ||
if (x.n < 0n) { | ||
const hasFrac = n.d > 1n; | ||
if (hasFrac) | ||
return INF; | ||
if (isEven(n.n)) | ||
return INF; | ||
} | ||
return x; | ||
} | ||
if (n.inf) { | ||
const minus = n.n < 0n; | ||
const cmpO = compareTo(abs2(x), ONE); | ||
return cmpO < 0 ? minus ? INF : ZERO : !cmpO ? null : minus ? ZERO : INF; | ||
} | ||
return _pow(x, n.n, n.d, options); | ||
} | ||
function scaleByPowerOfTen(x, n) { | ||
if (x.inf) | ||
return n.inf ? n.n > 0 ? x : null : x; | ||
if (n.inf) | ||
return n.n < 0n ? ZERO : !x.n ? null : x.n >= 0n ? INF : N_INF; | ||
return multiply(x, pow(TEN, n)); | ||
} | ||
function nthRoot(x, n, options) { | ||
if (x.inf) | ||
return n.inf ? ONE : !compareTo(n, ONE) ? x : signum(n) < 0 ? ZERO : INF; | ||
if (n.d === 1n && n.n === 2n) | ||
return sqrt(x, options); | ||
if (n.inf) | ||
return pow(x, ZERO); | ||
if (!compareTo(abs2(n), ONE)) | ||
return pow(x, n); | ||
if (!n.n) | ||
return pow(x, INF); | ||
if (x.n < 0n) | ||
return null; | ||
return _pow(x, n.d, n.n, options); | ||
} | ||
function sqrt(x, options) { | ||
if (x.inf) | ||
return x.n < 0n ? null : INF; | ||
if (!x.n) | ||
return ZERO; | ||
if (x.n < 0n) | ||
return null; | ||
const div = divideDigits(x.n, x.d); | ||
const numCtx = numberContext( | ||
1, | ||
div.e / 2n + (div.e >= 0n || isEven(div.e) ? 0n : -1n), | ||
options | ||
); | ||
let remainder = 0n; | ||
let part = 0n; | ||
const bf = []; | ||
if (isEven(div.e)) | ||
bf.push(0n); | ||
for (const digit of div.digits(true)) { | ||
if (bf.push(digit) < 2) | ||
continue; | ||
remainder = remainder * 100n + bf.shift() * 10n + bf.shift(); | ||
part *= 10n; | ||
if ( | ||
// Short circuit: If 1 is not available, it will not loop. | ||
remainder >= part + 1n | ||
) { | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = (part + nn) * nn; | ||
if (remainder < amount) | ||
continue; | ||
numCtx.set(nn); | ||
remainder -= amount; | ||
part += nn * 2n; | ||
break; | ||
} | ||
} | ||
if (numCtx.overflow()) | ||
break; | ||
numCtx.prepareNext(); | ||
} | ||
numCtx.round(remainder > 0n); | ||
return Frac.numOf(...numCtx.toNum()); | ||
} | ||
function _pow(base, num2, denom, options) { | ||
if (!num2) | ||
return ONE; | ||
const sign = num2 >= 0n === denom >= 0n ? 1 : -1; | ||
if (!base.n) | ||
return sign < 0 ? INF : ZERO; | ||
const n = abs(num2); | ||
const d = abs(denom); | ||
const i = n / d; | ||
const remN = n % d; | ||
let a = [base.n ** i, base.d ** i]; | ||
if (remN) { | ||
if (base.n < 0n) | ||
return null; | ||
const root = _nthRoot(base, d, options); | ||
a = [a[0] * root.n ** remN, a[1] * root.d ** remN]; | ||
} | ||
if (sign >= 0) | ||
return new Frac(a[0], a[1]); | ||
return new Frac(a[1], a[0]); | ||
} | ||
function _nthRoot(base, n, options) { | ||
const iN = abs(n); | ||
const div = divideDigits(base.n, base.d); | ||
const numCtx = numberContext( | ||
1, | ||
div.e / iN + (div.e >= 0n || !(div.e % iN) ? 0n : -1n), | ||
options | ||
); | ||
let remainder = 0n; | ||
const powOfTen = 10n ** iN; | ||
const table = createNthRootTable(iN); | ||
const bf = []; | ||
const initBfLen = div.e >= 0n ? iN - (abs(div.e) % iN + 1n) : abs(div.e + 1n) % iN; | ||
while (bf.length < initBfLen) | ||
bf.push(0n); | ||
for (const digit of div.digits(true)) { | ||
if (bf.push(digit) < iN) | ||
continue; | ||
remainder *= powOfTen; | ||
let bfPow = powOfTen; | ||
while (bf.length) | ||
remainder += bf.shift() * (bfPow /= 10n); | ||
table.prepare(); | ||
for (let nn = 9n; nn > 0n; nn--) { | ||
const amount = table.amount(nn); | ||
if (remainder < amount) | ||
continue; | ||
numCtx.set(nn); | ||
remainder -= amount; | ||
table.set(numCtx.getInt()); | ||
break; | ||
} | ||
if (numCtx.overflow()) | ||
break; | ||
numCtx.prepareNext(); | ||
} | ||
numCtx.round(remainder > 0n); | ||
const a = Frac.numOf(...numCtx.toNum()); | ||
if (n >= 0n) | ||
return a; | ||
return new Frac(a.d, a.n); | ||
} | ||
// src/impl/bignum-basic.mts | ||
var RE_NUMBER = /^([+-]?(?:[1-9]\d*|0)?)(?:\.(\d+))?(?:e([+-]?\d+))?$/iu; | ||
@@ -538,5 +517,24 @@ function parseValue(value) { | ||
} | ||
var num; | ||
var frac; | ||
var BigNum = class _BigNum { | ||
static valueOf(value) { | ||
if (value instanceof _BigNum) { | ||
return value; | ||
} | ||
return new _BigNum(value); | ||
} | ||
static #frac(value) { | ||
if (value instanceof _BigNum) { | ||
return value.#p; | ||
} | ||
return parseValue(value); | ||
} | ||
static #num(x, prop) { | ||
const Ctor = x.constructor; | ||
return prop ? x.#p === prop ? x : new Ctor(prop) : new Ctor(NaN); | ||
} | ||
static { | ||
this.valueOf = valueOf; | ||
num = _BigNum.#num; | ||
frac = _BigNum.#frac; | ||
} | ||
@@ -547,93 +545,84 @@ #p; | ||
this.#p = value.#p; | ||
return value; | ||
if (value.constructor === new.target) | ||
return value; | ||
return; | ||
} | ||
this.#p = parseValue(value); | ||
} | ||
#val(prop) { | ||
return prop ? this.#p === prop ? this : new _BigNum(prop) : new _BigNum(NaN); | ||
} | ||
/** Returns a number indicating the sign. */ | ||
signum() { | ||
return this.#p?.signum() ?? NaN; | ||
const x = this.#p; | ||
return x ? signum(x) : NaN; | ||
} | ||
/** Returns a BigNum whose value is (-this). */ | ||
/** Returns a BigNum whose value is `-this`. */ | ||
negate() { | ||
return this.#val(this.#p?.negate()); | ||
const x = this.#p; | ||
return num(this, x && negate(x)); | ||
} | ||
/** Returns a BigNum whose value is (this + augend) */ | ||
/** Returns a BigNum whose value is `this + augend`. */ | ||
add(augend) { | ||
const b = valueOf(augend).#p; | ||
return this.#val(b && this.#p?.add(b)); | ||
const x = this.#p; | ||
const y = frac(augend); | ||
return num(this, x && y && add(x, y)); | ||
} | ||
/** Returns a BigNum whose value is (this - subtrahend) */ | ||
/** Returns a BigNum whose value is `this - subtrahend` */ | ||
subtract(subtrahend) { | ||
return this.add(valueOf(subtrahend).negate()); | ||
const x = this.#p; | ||
const y = frac(subtrahend); | ||
return num(this, x && y && add(x, negate(y))); | ||
} | ||
/** Returns a BigNum whose value is (this × multiplicand). */ | ||
/** Returns a BigNum whose value is `this × multiplicand`. */ | ||
multiply(multiplicand) { | ||
const b = valueOf(multiplicand).#p; | ||
return this.#val(b && this.#p?.multiply(b)); | ||
const x = this.#p; | ||
const y = frac(multiplicand); | ||
return num(this, x && y && multiply(x, y)); | ||
} | ||
/** | ||
* Returns a BigNum whose value is (this / divisor) | ||
* Returns a BigNum whose value is `this / divisor` | ||
*/ | ||
divide(divisor) { | ||
const b = valueOf(divisor).#p; | ||
return this.#val(b && this.#p?.divide(b)); | ||
const x = this.#p; | ||
const y = frac(divisor); | ||
return num(this, x && y && divide(x, y)); | ||
} | ||
/** | ||
* Returns a BigNum whose value is (this % divisor) | ||
* Returns a BigNum whose value is `this % divisor` | ||
*/ | ||
modulo(divisor) { | ||
const b = valueOf(divisor).#p; | ||
return this.#val(b && this.#p?.modulo(b)); | ||
const x = this.#p; | ||
const y = frac(divisor); | ||
return num(this, x && y && modulo(x, y)); | ||
} | ||
/** | ||
* Returns a BigNum whose value is (this**n). | ||
*/ | ||
pow(n, options) { | ||
const b = valueOf(n).#p; | ||
return this.#val(b && this.#p?.pow(b, options)); | ||
} | ||
/** Returns a BigNum whose numerical value is equal to (this * 10 ** n). */ | ||
scaleByPowerOfTen(n) { | ||
const b = valueOf(n).#p; | ||
return this.#val(b && this.#p?.scaleByPowerOfTen(b)); | ||
} | ||
/** Returns an approximation to the square root of this. */ | ||
sqrt(options) { | ||
return this.#val(this.#p?.sqrt(options)); | ||
} | ||
/** Returns an approximation to the nth root of this. */ | ||
nthRoot(n, options) { | ||
const b = valueOf(n).#p; | ||
return this.#val(b && this.#p?.nthRoot(b, options)); | ||
} | ||
/** Returns a BigNum whose value is the absolute value of this BigNum. */ | ||
abs() { | ||
return this.#val(this.#p?.abs()); | ||
const x = this.#p; | ||
return num(this, x && abs2(x)); | ||
} | ||
/** Returns a BigNum that is the integral part of this BigNum, with removing any fractional digits. */ | ||
trunc() { | ||
return this.#val(this.#p?.trunc()); | ||
const x = this.#p; | ||
return num(this, x && round(x, ROUND_OPTS[0 /* trunc */])); | ||
} | ||
/** Returns this BigNum rounded to the nearest integer. */ | ||
round() { | ||
return this.#val(this.#p?.round()); | ||
const x = this.#p; | ||
return num(this, x && round(x, ROUND_OPTS[1 /* round */])); | ||
} | ||
/** Returns the greatest integer less than or equal to this BigNum. */ | ||
floor() { | ||
return this.#val(this.#p?.floor()); | ||
const x = this.#p; | ||
return num(this, x && round(x, ROUND_OPTS[2 /* floor */])); | ||
} | ||
/** Returns the smallest integer greater than or equal to this BigNum. */ | ||
ceil() { | ||
return this.#val(this.#p?.ceil()); | ||
const x = this.#p; | ||
return num(this, x && round(x, ROUND_OPTS[3 /* ceil */])); | ||
} | ||
/** Compares this BigNum with the specified BigNum. */ | ||
compareTo(other) { | ||
const a = this.#p; | ||
const b = valueOf(other).#p; | ||
if (!a || !b) | ||
const x = this.#p; | ||
const y = frac(other); | ||
if (!x || !y) | ||
return NaN; | ||
return a.compareTo(b); | ||
return compareTo(x, y); | ||
} | ||
@@ -644,3 +633,4 @@ isNaN() { | ||
isFinite() { | ||
return this.#p != null && !this.#p.inf; | ||
const x = this.#p; | ||
return x != null && !x.inf; | ||
} | ||
@@ -651,20 +641,49 @@ toString() { | ||
toJSON() { | ||
if (!this.#p) | ||
const x = this.#p; | ||
if (!x) | ||
return NaN; | ||
if (this.#p.inf) | ||
return this.#p.signum() > 0 ? Infinity : -Infinity; | ||
if (x.inf) | ||
return x.n > 0 ? Infinity : -Infinity; | ||
const str = this.toString(); | ||
const num = Number(str); | ||
return String(num) === str ? num : str; | ||
const nVal = Number(str); | ||
return String(nVal) === str ? nVal : str; | ||
} | ||
}; | ||
function valueOf(value) { | ||
if (value instanceof BigNum) { | ||
return value; | ||
// src/impl/bignum.mts | ||
var BigNum2 = class _BigNum extends BigNum { | ||
static valueOf(value) { | ||
if (value instanceof _BigNum) { | ||
return value; | ||
} | ||
return new _BigNum(value); | ||
} | ||
return new BigNum(value); | ||
} | ||
/** Returns a BigNum whose value is `this**n`. */ | ||
pow(n, options) { | ||
const x = frac(this); | ||
const y = frac(n); | ||
return num(this, x && y && pow(x, y, options)); | ||
} | ||
/** Returns a BigNum whose numerical value is equal to `this * 10 ** n`. */ | ||
scaleByPowerOfTen(n) { | ||
const x = frac(this); | ||
const y = frac(n); | ||
return num(this, x && y && scaleByPowerOfTen(x, y)); | ||
} | ||
/** Returns an approximation to the square root of this. */ | ||
sqrt(options) { | ||
const x = frac(this); | ||
return num(this, x && sqrt(x, options)); | ||
} | ||
/** Returns an approximation to the nth root of this. */ | ||
nthRoot(n, options) { | ||
const x = frac(this); | ||
const y = frac(n); | ||
return num(this, x && y && nthRoot(x, y, options)); | ||
} | ||
}; | ||
export { | ||
BigNum, | ||
BigNum2 as BigNum, | ||
BigNum as BigNumBasic, | ||
RoundingMode | ||
}; |
{ | ||
"name": "@bignum/core", | ||
"version": "0.10.1", | ||
"version": "0.11.0", | ||
"description": "Arbitrary-precision decimal arithmetic with BigInt.", | ||
"type": "module", | ||
"main": "lib/index.js", | ||
"exports": { | ||
@@ -40,3 +41,13 @@ ".": { | ||
"bigdecimal", | ||
"arbitrary-precision" | ||
"arbitrary-precision", | ||
"arbitrary", | ||
"precision", | ||
"arithmetic", | ||
"big", | ||
"number", | ||
"decimal", | ||
"float", | ||
"biginteger", | ||
"bignumber", | ||
"bigint" | ||
], | ||
@@ -49,4 +60,4 @@ "author": "Yosuke Ota", | ||
}, | ||
"homepage": "https://github.com/ota-meshi/bignum#readme", | ||
"homepage": "https://github.com/ota-meshi/bignum/tree/main/packages/core#readme", | ||
"devDependencies": {} | ||
} |
@@ -5,2 +5,7 @@ # @bignum/core | ||
## 🚀 Features | ||
- Class for arbitrary-precision arithmetics using BigInt. | ||
- It can handle very large and very small numbers. | ||
## 💿 Installation | ||
@@ -19,3 +24,14 @@ | ||
console.log(BigNum.valueOf(0.2).add(BigNum.valueOf(0.1)).toString()); // 0.3 | ||
// (Using the JavaScript built-in number:) | ||
console.log(0.2 + 0.1); // 0.30000000000000004 | ||
// Can handle very large numbers. | ||
console.log(BigNum.valueOf(1.7976931348623157e308).add(12345).toString()); // 17976931348623157000...(Repeat `0` 281 times)...00012345 | ||
// Can handle very small numbers. | ||
console.log(BigNum.valueOf(5e-324).subtract(12345).toString()); // -12344.999...(Repeat `9` 317 times)...9995 | ||
// Since the value is held as a rational number, no rounding errors occur due to division. | ||
console.log(BigNum.valueOf(1).divide(3).multiply(3).toString()); // 1 | ||
// (However, if you convert an infinite decimal value into a string, a rounding error will occur.) | ||
console.log(BigNum.valueOf(1).divide(3).toString()); // 0.33333333333333333333 | ||
``` | ||
@@ -132,6 +148,6 @@ | ||
| 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. | | ||
| 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. | | ||
@@ -156,2 +172,15 @@ - `context` parameter | ||
## 🚀 Advanced Usage | ||
### BigNumBasic | ||
```js | ||
import { BigNumBasic } from "@bignum/core"; | ||
``` | ||
If you want something smaller, use BigNumBasic. | ||
It supports four arithmetic APIs and a basic API, which can be reduced to just 5kb with tree shaking and minification.\ | ||
However, the API it provides is still experimental. | ||
## 🛸 Prior Art | ||
@@ -158,0 +187,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
50768
1448
191