"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decimal = exports.isDecimal = exports.DEFAULT_DIV_PRECISION = void 0;
/**
 * Maps built-in preset names to their {@link DecimalRoundingOptions}.
 * Used internally by {@link DecimalImpl.round}.
 *
 * @internal
 */
const ROUNDING_PRESETS = {
    'ubb-usage-count': { mode: 'significant-figures', value: 15 },
    'v1-api': { mode: 'decimal-places', value: 12 },
};
/**
 * The IEEE 754 decimal128 coefficient size (34 digits) — the recommended
 * precision for {@link DecimalImpl.div} when full precision is desired.
 *
 * @remarks
 * Pass this as the `precision` argument to `div()` when you want the
 * maximum available precision. Division requires explicit precision —
 * no invisible defaults in financial code.
 *
 * @example
 * ```ts
 * // Use the full decimal128 precision explicitly
 * a.div(b, DEFAULT_DIV_PRECISION, 'half-even');
 * ```
 *
 * @public
 */
exports.DEFAULT_DIV_PRECISION = 34;
/**
 * Maximum number of digits in plain (non-exponential) notation produced
 * by {@link DecimalImpl.toString}. Values exceeding this threshold are
 * emitted in scientific notation (`1.23E+40`).
 *
 * @internal
 */
const PLAIN_NOTATION_DIGIT_LIMIT = 30;
/**
 * Maximum absolute value for the internal exponent.
 *
 * @remarks
 * This bound also implicitly limits exponent differences used in
 * arithmetic (e.g., scaling by `10^exponentDiff`), preventing
 * astronomically large BigInt allocations that could hang or
 * exhaust the process.
 *
 * The chosen limit is intentionally conservative but still far beyond
 * any magnitude needed for typical financial or billing calculations.
 *
 * @internal
 */
const MAX_EXPONENT = 1000000;
/**
 * Internal implementation of arbitrary-precision decimal arithmetic.
 *
 * @remarks
 * Represents a decimal value as `coefficient × 10^exponent` using
 * native `BigInt` for the coefficient, giving unlimited precision with
 * no rounding on construction. Instances are always
 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze | frozen}
 * and all arithmetic produces new instances.
 *
 * This class is **not** exported directly — consumers interact with
 * the branded {@link Decimal} type and the {@link Decimal | Decimal companion object}.
 *
 * @internal
 */
class DecimalImpl {
    /**
     * Construct and normalise a decimal value.
     *
     * @param coefficient - The unscaled integer value.
     * @param exponent - The power-of-ten scale factor.
     *
     * @internal
     */
    constructor(coefficient, exponent) {
        const [normalizedCoef, normalizedExp] = DecimalImpl.normalize(coefficient, exponent);
        this._coefficient = normalizedCoef;
        this._exponent = normalizedExp;
        Object.freeze(this);
    }
    /**
     * Strip trailing zeros from `coefficient`, incrementing `exponent`
     * for each zero removed. Zero always normalises to `(0n, 0)`.
     *
     * @param coefficient - Raw coefficient before normalisation.
     * @param exponent - Raw exponent before normalisation.
     * @returns A `[coefficient, exponent]` tuple with trailing zeros removed.
     *
     * @internal
     */
    static normalize(coefficient, exponent) {
        if (coefficient === 0n) {
            return [0n, 0];
        }
        let coef = coefficient;
        let exp = exponent;
        while (coef !== 0n && coef % 10n === 0n) {
            coef /= 10n;
            exp += 1;
        }
        return [coef, exp];
    }
    /**
     * Apply rounding to the result of an integer division.
     *
     * @remarks
     * BigInt division truncates toward zero. This helper inspects the
     * `remainder` to decide whether to adjust the truncated `quotient`
     * by ±1 according to the chosen {@link RoundDirection}.
     *
     * The rounding direction is derived from the signs of `remainder`
     * and `divisor`: when they agree the exact fractional part is
     * positive (the truncation point is below the true value, so +1
     * rounds to nearest); when they disagree the fractional part is
     * negative (−1 rounds to nearest).
     *
     * @param quotient - Truncated integer quotient (`dividend / divisor`).
     * @param remainder - Division remainder (`dividend % divisor`).
     * @param divisor - The divisor used in the division.
     * @param direction - The rounding strategy to apply.
     * @returns The rounded quotient.
     *
     * @internal
     */
    static roundDivision(quotient, remainder, divisor, direction) {
        if (remainder === 0n) {
            return quotient;
        }
        // 'round-down': truncate toward zero — BigInt division already does this.
        if (direction === 'round-down') {
            return quotient;
        }
        // The sign of remainder/divisor tells us which side of the truncation
        // point the exact value lies on.
        // Same sign → fractional part is positive (exact value > quotient) → +1 adjusts upward.
        // Opposite sign → fractional part is negative (exact value < quotient) → -1 adjusts downward.
        const roundDir = remainder > 0n === divisor > 0n ? 1n : -1n;
        // 'round-up': away from zero whenever there is any remainder.
        if (direction === 'round-up') {
            return quotient + roundDir;
        }
        // 'ceil': toward positive infinity.
        // If the fractional part is positive (roundDir === 1n), round up.
        // If the fractional part is negative (roundDir === -1n), truncation already went toward +∞.
        if (direction === 'ceil') {
            return roundDir === 1n ? quotient + 1n : quotient;
        }
        // 'floor': toward negative infinity.
        // If the fractional part is negative (roundDir === -1n), round down.
        // If the fractional part is positive (roundDir === 1n), truncation already went toward -∞.
        if (direction === 'floor') {
            return roundDir === -1n ? quotient - 1n : quotient;
        }
        // For the half-* modes we need to compare the remainder to exactly half the divisor.
        const absRemainder = remainder < 0n ? -remainder : remainder;
        const absDivisor = divisor < 0n ? -divisor : divisor;
        const doubled = absRemainder * 2n;
        let cmp;
        if (doubled === absDivisor) {
            cmp = 0;
        }
        else if (doubled < absDivisor) {
            cmp = -1;
        }
        else {
            cmp = 1;
        }
        if (cmp < 0) {
            // Less than half — truncation is already the nearest value.
            return quotient;
        }
        if (cmp > 0) {
            // More than half — round to nearest (away from truncation point).
            return quotient + roundDir;
        }
        // Exactly half — tie-breaking depends on the chosen mode.
        if (direction === 'half-up') {
            // Round away from zero.
            return quotient + roundDir;
        }
        if (direction === 'half-down') {
            // Round toward zero — stay at the truncated quotient.
            return quotient;
        }
        // HALF_EVEN: round to nearest even.
        if (quotient % 2n === 0n) {
            // Already even — stay at truncation.
            return quotient;
        }
        else {
            // Odd — adjust to make even.
            return quotient + roundDir;
        }
    }
    // -------------------------------------------------------------------
    // Arithmetic
    // -------------------------------------------------------------------
    /**
     * Return the sum of this value and `other`.
     *
     * @param other - The addend.
     * @returns A new {@link Decimal} equal to `this + other`.
     *
     * @public
     */
    add(other) {
        const otherImpl = other;
        // Align exponents — use the smaller (more precision) exponent as target.
        if (this._exponent === otherImpl._exponent) {
            return new DecimalImpl(this._coefficient + otherImpl._coefficient, this._exponent);
        }
        if (this._exponent < otherImpl._exponent) {
            const scale = 10n ** BigInt(otherImpl._exponent - this._exponent);
            return new DecimalImpl(this._coefficient + otherImpl._coefficient * scale, this._exponent);
        }
        else {
            const scale = 10n ** BigInt(this._exponent - otherImpl._exponent);
            return new DecimalImpl(this._coefficient * scale + otherImpl._coefficient, otherImpl._exponent);
        }
    }
    /**
     * Return the difference of this value and `other`.
     *
     * @param other - The subtrahend.
     * @returns A new {@link Decimal} equal to `this - other`.
     *
     * @public
     */
    sub(other) {
        const otherImpl = other;
        if (this._exponent === otherImpl._exponent) {
            return new DecimalImpl(this._coefficient - otherImpl._coefficient, this._exponent);
        }
        if (this._exponent < otherImpl._exponent) {
            const scale = 10n ** BigInt(otherImpl._exponent - this._exponent);
            return new DecimalImpl(this._coefficient - otherImpl._coefficient * scale, this._exponent);
        }
        else {
            const scale = 10n ** BigInt(this._exponent - otherImpl._exponent);
            return new DecimalImpl(this._coefficient * scale - otherImpl._coefficient, otherImpl._exponent);
        }
    }
    /**
     * Return the product of this value and `other`.
     *
     * @param other - The multiplicand.
     * @returns A new {@link Decimal} equal to `this × other`.
     *
     * @public
     */
    mul(other) {
        const otherImpl = other;
        return new DecimalImpl(this._coefficient * otherImpl._coefficient, this._exponent + otherImpl._exponent);
    }
    /**
     * Return the quotient of this value divided by `other`.
     *
     * @remarks
     * Division scales the dividend to produce `precision` decimal digits
     * in the result, then applies integer division and rounds the
     * remainder according to `direction`.
     *
     * Division requires explicit rounding control — no invisible defaults
     * in financial code. For full precision use {@link DEFAULT_DIV_PRECISION}
     * (34, matching the IEEE 754 decimal128 coefficient size).
     *
     * @example
     * ```ts
     * Decimal.from('1').div(Decimal.from('3'), 5, 'half-up');   // "0.33333"
     * Decimal.from('5').div(Decimal.from('2'), 0, 'half-up');   // "3"
     * Decimal.from('5').div(Decimal.from('2'), 0, 'half-even'); // "2"
     * ```
     *
     * @param other - The divisor. Must not be zero.
     * @param precision - Maximum number of decimal digits in the result.
     * @param direction - How to round when the exact quotient cannot
     *   be represented at the requested precision.
     * @returns A new {@link Decimal} equal to `this ÷ other`, rounded to
     *   `precision` decimal places.
     * @throws {@link Error} if `other` is zero.
     * @throws {@link Error} if `precision` is negative or non-integer.
     *
     * @public
     */
    div(other, precision, direction) {
        if (precision < 0 || !Number.isInteger(precision)) {
            throw new Error('precision must be a non-negative integer');
        }
        const otherImpl = other;
        if (otherImpl._coefficient === 0n) {
            throw new Error('Division by zero');
        }
        // result_coefficient = this.coefficient × 10^(thisExp - otherExp + precision) / other.coefficient
        // result_exponent   = -precision
        const scale = this._exponent - otherImpl._exponent + precision;
        let quotient;
        let remainder;
        let roundingDivisor;
        if (scale >= 0) {
            const scaledDividend = this._coefficient * 10n ** BigInt(scale);
            quotient = scaledDividend / otherImpl._coefficient;
            remainder = scaledDividend % otherImpl._coefficient;
            roundingDivisor = otherImpl._coefficient;
        }
        else {
            // Negative scale: shift the power onto the divisor side to avoid
            // BigInt exponentiation with a negative exponent (which throws).
            const scaledDivisor = otherImpl._coefficient * 10n ** BigInt(-scale);
            quotient = this._coefficient / scaledDivisor;
            remainder = this._coefficient % scaledDivisor;
            roundingDivisor = scaledDivisor;
        }
        const roundedQuotient = DecimalImpl.roundDivision(quotient, remainder, roundingDivisor, direction);
        return new DecimalImpl(roundedQuotient, -precision);
    }
    // -------------------------------------------------------------------
    // Comparison
    // -------------------------------------------------------------------
    /**
     * Three-way comparison of this value with `other`.
     *
     * @example
     * ```ts
     * const a = Decimal.from('1.5');
     * const b = Decimal.from('2');
     * a.cmp(b); // -1
     * b.cmp(a); //  1
     * a.cmp(a); //  0
     * ```
     *
     * @param other - The value to compare against.
     * @returns `-1` if `this \< other`, `0` if equal, `1` if `this \> other`.
     *
     * @public
     */
    cmp(other) {
        const otherImpl = other;
        if (this._exponent === otherImpl._exponent) {
            if (this._coefficient < otherImpl._coefficient)
                return -1;
            if (this._coefficient > otherImpl._coefficient)
                return 1;
            return 0;
        }
        if (this._exponent < otherImpl._exponent) {
            // this has smaller exponent — scale other's coefficient to match.
            const scale = 10n ** BigInt(otherImpl._exponent - this._exponent);
            const scaledOther = otherImpl._coefficient * scale;
            if (this._coefficient < scaledOther)
                return -1;
            if (this._coefficient > scaledOther)
                return 1;
            return 0;
        }
        else {
            // other has smaller exponent — scale this's coefficient to match.
            const scale = 10n ** BigInt(this._exponent - otherImpl._exponent);
            const scaledThis = this._coefficient * scale;
            if (scaledThis < otherImpl._coefficient)
                return -1;
            if (scaledThis > otherImpl._coefficient)
                return 1;
            return 0;
        }
    }
    /**
     * Return `true` if this value is numerically equal to `other`.
     *
     * @param other - The value to compare against.
     * @returns `true` if `this === other` in value, `false` otherwise.
     *
     * @public
     */
    eq(other) {
        return this.cmp(other) === 0;
    }
    /**
     * Return `true` if this value is strictly less than `other`.
     *
     * @param other - The value to compare against.
     * @returns `true` if `this \< other`, `false` otherwise.
     *
     * @public
     */
    lt(other) {
        return this.cmp(other) === -1;
    }
    /**
     * Return `true` if this value is less than or equal to `other`.
     *
     * @param other - The value to compare against.
     * @returns `true` if `this ≤ other`, `false` otherwise.
     *
     * @public
     */
    lte(other) {
        return this.cmp(other) <= 0;
    }
    /**
     * Return `true` if this value is strictly greater than `other`.
     *
     * @param other - The value to compare against.
     * @returns `true` if `this \> other`, `false` otherwise.
     *
     * @public
     */
    gt(other) {
        return this.cmp(other) === 1;
    }
    /**
     * Return `true` if this value is greater than or equal to `other`.
     *
     * @param other - The value to compare against.
     * @returns `true` if `this ≥ other`, `false` otherwise.
     *
     * @public
     */
    gte(other) {
        return this.cmp(other) >= 0;
    }
    // -------------------------------------------------------------------
    // Predicates
    // -------------------------------------------------------------------
    /**
     * Return `true` if this value is exactly zero.
     *
     * @returns `true` if the value is zero, `false` otherwise.
     *
     * @public
     */
    isZero() {
        return this._coefficient === 0n;
    }
    /**
     * Return `true` if this value is strictly less than zero.
     *
     * @returns `true` if negative, `false` if zero or positive.
     *
     * @public
     */
    isNegative() {
        return this._coefficient < 0n;
    }
    /**
     * Return `true` if this value is strictly greater than zero.
     *
     * @returns `true` if positive, `false` if zero or negative.
     *
     * @public
     */
    isPositive() {
        return this._coefficient > 0n;
    }
    // -------------------------------------------------------------------
    // Unary operations
    // -------------------------------------------------------------------
    /**
     * Return the additive inverse of this value.
     *
     * @returns A new {@link Decimal} equal to `-this`.
     *
     * @public
     */
    neg() {
        return new DecimalImpl(-this._coefficient, this._exponent);
    }
    /**
     * Return the absolute value.
     *
     * @returns A new {@link Decimal} equal to `|this|`. If this value is
     *   already non-negative, returns `this` (no allocation).
     *
     * @public
     */
    abs() {
        if (this._coefficient < 0n) {
            return new DecimalImpl(-this._coefficient, this._exponent);
        }
        return this;
    }
    // -------------------------------------------------------------------
    // Rounding
    // -------------------------------------------------------------------
    /**
     * Round this value to a specified precision.
     *
     * @remarks
     * **Rounding directions** (IEEE 754-2019 §4.3):
     *
     * | Direction      | Behavior                                       |
     * | -------------- | ---------------------------------------------- |
     * | `'ceil'`       |  1.1→2, -1.1→-1, 1.0→1 (toward +∞)             |
     * | `'floor'`      |  1.9→1, -1.1→-2, 1.0→1 (toward -∞)             |
     * | `'round-down'` |  1.9→1, -1.9→-1 (toward zero / truncate)       |
     * | `'round-up'`   |  1.1→2, -1.1→-2 (away from zero)               |
     * | `'half-up'`    |  0.5→1, 1.5→2, -0.5→-1 (ties away from zero)   |
     * | `'half-down'`  |  0.5→0, 1.5→1, -0.5→0 (ties toward zero)       |
     * | `'half-even'`  |  0.5→0, 1.5→2, 2.5→2, 3.5→4 (ties to even)     |
     *
     * **Precision** is specified as a {@link DecimalRoundingOptions} object
     * or a preset name from {@link DecimalRoundingPresets}:
     *
     * @example
     * ```ts
     * // Using a preset
     * amount.round('half-even', 'v1-api');
     *
     * // Using explicit options
     * amount.round('half-even', { mode: 'decimal-places', value: 2 });
     * amount.round('half-up', { mode: 'significant-figures', value: 4 });
     * ```
     *
     * @param direction - How to round.
     * @param options - A {@link DecimalRoundingOptions} object or key of {@link DecimalRoundingPresets}.
     * @returns A new {@link Decimal} rounded to the specified precision.
     * @throws {@link Error} if `options.value` is negative or non-integer.
     * @throws {@link Error} if the preset name is not recognized.
     *
     * @public
     */
    round(direction, options) {
        const resolved = typeof options === 'string'
            ? // Declaration merging allows consumers to add keys at compile time, but
                // ROUNDING_PRESETS only knows about built-in keys at runtime.  The double
                // cast through `unknown` is intentional: we want an undefined-safe lookup
                // so the runtime guard below can produce a clear error for unrecognised
                // (e.g. declaration-merged) preset names that were not also added to
                // ROUNDING_PRESETS.
                ROUNDING_PRESETS[options]
            : options;
        if (resolved === undefined) {
            throw new Error(`Unknown rounding preset: "${options}"`);
        }
        if (resolved.value < 0 || !Number.isInteger(resolved.value)) {
            throw new Error('DecimalRoundingOptions.value must be a non-negative integer');
        }
        if (resolved.mode === 'decimal-places') {
            // Reuse toFixed logic: round to resolved.value decimal places then re-parse.
            const fixed = this.toFixed(resolved.value, direction);
            return exports.Decimal.from(fixed);
        }
        // significant-figures: round to resolved.value total significant digits.
        if (this._coefficient === 0n) {
            return this;
        }
        const coeffStr = this._coefficient < 0n
            ? (-this._coefficient).toString()
            : this._coefficient.toString();
        const currentSigFigs = coeffStr.length;
        if (resolved.value === 0) {
            // 0 significant figures is a degenerate case — return zero.
            return exports.Decimal.zero;
        }
        if (currentSigFigs <= resolved.value) {
            // Already at or below requested precision — no rounding needed.
            return this;
        }
        // We need to reduce the number of significant figures.
        // The number of digits to drop from the coefficient:
        const digitsToTrim = currentSigFigs - resolved.value;
        const divisor = 10n ** BigInt(digitsToTrim);
        const quotient = this._coefficient / divisor;
        const remainder = this._coefficient % divisor;
        const rounded = DecimalImpl.roundDivision(quotient, remainder, divisor, direction);
        // The new exponent shifts to account for trimmed digits.
        return new DecimalImpl(rounded, this._exponent + digitsToTrim);
    }
    // -------------------------------------------------------------------
    // Conversion / serialisation
    // -------------------------------------------------------------------
    /**
     * Return a human-readable string representation.
     *
     * @remarks
     * Plain notation for values whose digit count is at most 30, and
     * scientific notation (`1.23E+40`) for larger values. Trailing zeros
     * are never present because the internal representation is normalised.
     *
     * @public
     */
    toString() {
        if (this._coefficient === 0n) {
            return '0';
        }
        const coeffStr = this._coefficient.toString();
        const isNeg = coeffStr.startsWith('-');
        const absCoeffStr = isNeg ? coeffStr.slice(1) : coeffStr;
        if (this._exponent < 0) {
            const decimalPlaces = -this._exponent;
            // Guard against unbounded string allocation for extreme negative
            // exponents (e.g. 1e-1000000 would otherwise produce a million-char
            // string of leading zeros). Switch to scientific notation when the
            // number of leading zeros alone exceeds the digit limit. Normal
            // fractional values (e.g. 34-digit division results) pass through.
            const leadingZeroCount = decimalPlaces >= absCoeffStr.length
                ? decimalPlaces - absCoeffStr.length
                : 0;
            if (leadingZeroCount > PLAIN_NOTATION_DIGIT_LIMIT) {
                if (absCoeffStr.length === 1) {
                    return `${coeffStr}E${String(this._exponent)}`;
                }
                const intPart = absCoeffStr[0] ?? '';
                const fracPart = absCoeffStr.slice(1);
                const adjustedExp = this._exponent + absCoeffStr.length - 1;
                return `${isNeg ? '-' : ''}${intPart}.${fracPart}E${String(adjustedExp)}`;
            }
            if (decimalPlaces >= absCoeffStr.length) {
                const leadingZeros = '0'.repeat(decimalPlaces - absCoeffStr.length);
                return `${isNeg ? '-' : ''}0.${leadingZeros}${absCoeffStr}`;
            }
            else {
                const integerPart = absCoeffStr.slice(0, absCoeffStr.length - decimalPlaces);
                const fractionalPart = absCoeffStr.slice(absCoeffStr.length - decimalPlaces);
                return `${isNeg ? '-' : ''}${integerPart}.${fractionalPart}`;
            }
        }
        const plainLength = absCoeffStr.length + this._exponent;
        if (plainLength <= PLAIN_NOTATION_DIGIT_LIMIT) {
            if (this._exponent === 0) {
                return coeffStr;
            }
            const trailingZeros = '0'.repeat(this._exponent);
            return `${isNeg ? '-' : ''}${absCoeffStr}${trailingZeros}`;
        }
        else {
            if (absCoeffStr.length === 1) {
                return `${coeffStr}E+${String(this._exponent)}`;
            }
            const integerPart = absCoeffStr[0] ?? '';
            const fractionalPart = absCoeffStr.slice(1);
            const adjustedExponent = this._exponent + absCoeffStr.length - 1;
            return `${isNeg ? '-' : ''}${integerPart}.${fractionalPart}E+${String(adjustedExponent)}`;
        }
    }
    /**
     * Return the JSON-serialisable representation.
     *
     * @remarks
     * Returns a plain string matching the Stripe API convention where
     * decimal values are serialised as strings in JSON. Called
     * automatically by `JSON.stringify`.
     *
     * @public
     */
    toJSON() {
        return this.toString();
    }
    /**
     * Convert to a JavaScript `number`.
     *
     * @remarks
     * This is an explicit, intentionally lossy conversion. Use it only
     * when you need a numeric value for display or interop with APIs
     * that require `number`. Prefer {@link Decimal.toString | toString}
     * or {@link Decimal.toFixed | toFixed} for lossless output.
     *
     * @public
     */
    toNumber() {
        return Number(this.toString());
    }
    /**
     * Format this value as a fixed-point string with exactly
     * `decimalPlaces` digits after the decimal point.
     *
     * @remarks
     * Values are rounded according to `direction` when the internal
     * precision exceeds the requested number of decimal places.
     * The rounding direction is always required — no invisible defaults
     * in financial code.
     *
     * @example
     * ```ts
     * Decimal.from('1.235').toFixed(2, 'half-up');   // "1.24"
     * Decimal.from('1.225').toFixed(2, 'half-even'); // "1.22"
     * Decimal.from('42').toFixed(3, 'half-up');      // "42.000"
     * ```
     *
     * @param decimalPlaces - Number of digits after the decimal point.
     *   Must be a non-negative integer.
     * @param direction - How to round when truncating excess digits.
     * @returns A string with exactly `decimalPlaces` fractional digits.
     * @throws {@link Error} if `decimalPlaces` is negative or non-integer.
     *
     * @public
     */
    toFixed(decimalPlaces, direction) {
        if (decimalPlaces < 0 || !Number.isInteger(decimalPlaces)) {
            throw new Error('decimalPlaces must be a non-negative integer');
        }
        const formatFixed = (coef) => {
            const coeffStr = coef.toString();
            const isNeg = coeffStr.startsWith('-');
            const absCoeffStr = isNeg ? coeffStr.slice(1) : coeffStr;
            if (decimalPlaces === 0) {
                return coeffStr;
            }
            if (decimalPlaces >= absCoeffStr.length) {
                const leadingZeros = '0'.repeat(decimalPlaces - absCoeffStr.length);
                return `${isNeg ? '-' : ''}0.${leadingZeros}${absCoeffStr}`;
            }
            else {
                const integerPart = absCoeffStr.slice(0, absCoeffStr.length - decimalPlaces);
                const fractionalPart = absCoeffStr.slice(absCoeffStr.length - decimalPlaces);
                return `${isNeg ? '-' : ''}${integerPart}.${fractionalPart}`;
            }
        };
        const targetExponent = -decimalPlaces;
        if (this._exponent === targetExponent) {
            return formatFixed(this._coefficient);
        }
        if (this._exponent < targetExponent) {
            // Need to reduce precision — round the excess digits.
            const scaleDiff = targetExponent - this._exponent;
            const divisor = 10n ** BigInt(scaleDiff);
            const quotient = this._coefficient / divisor;
            const remainder = this._coefficient % divisor;
            const rounded = DecimalImpl.roundDivision(quotient, remainder, divisor, direction);
            return formatFixed(rounded);
        }
        else {
            // Need to increase precision — pad with trailing zeros.
            const scaleDiff = this._exponent - targetExponent;
            const scaled = this._coefficient * 10n ** BigInt(scaleDiff);
            return formatFixed(scaled);
        }
    }
    /**
     * Return a string primitive when the runtime coerces the value.
     *
     * @remarks
     * Deliberately returns a `string` (not a `number`) to discourage
     * silent precision loss through implicit arithmetic coercion.
     * When used in a numeric context (for example, `+myDecimal`), the
     * JavaScript runtime will first call this method and then coerce
     * the resulting string to a `number`, which may lose precision.
     * Callers should prefer the explicit
     * {@link Decimal.toNumber | toNumber} method when an IEEE 754
     * `number` is required.
     *
     * @public
     */
    valueOf() {
        return this.toString();
    }
}
/**
 * Check whether a value is a {@link Decimal} instance.
 *
 * @remarks
 * Use this instead of `instanceof` — the underlying class is not
 * publicly exported, so `instanceof` checks are not available to
 * consumers.
 *
 * @example
 * ```ts
 * if (isDecimal(value)) {
 *   value.add(Decimal.from('1')); // value is Decimal
 * }
 * ```
 *
 * @public
 */
function isDecimal(value) {
    return value instanceof DecimalImpl;
}
exports.isDecimal = isDecimal;
/**
 * Companion object for creating {@link Decimal} instances.
 *
 * @public
 */
exports.Decimal = {
    /**
     * Create a {@link Decimal} from a string, number, or bigint.
     *
     * @remarks
     * - **string**: Parsed as a decimal literal. Accepts an optional sign,
     *   integer digits, an optional fractional part, and an optional `e`/`E`
     *   exponent. Leading/trailing whitespace is trimmed.
     * - **number**: Must be finite. Converted via `Number.prototype.toString()`
     *   then parsed, so `Decimal.from(0.1)` produces `"0.1"` (not the
     *   53-bit binary approximation).
     * - **bigint**: Treated as an integer with exponent 0.
     *
     * @example
     * ```ts
     * Decimal.from('1.23');   // string
     * Decimal.from(42);       // number
     * Decimal.from(100n);     // bigint
     * Decimal.from('1.5e3');  // scientific notation → 1500
     * ```
     *
     * @param value - The value to convert.
     * @returns A new frozen {@link Decimal} instance.
     * @throws {@link Error} if `value` is a non-finite number, an empty
     *   string, or a string that does not match the decimal literal grammar.
     *
     * @public
     */
    from(value) {
        if (typeof value === 'bigint') {
            return new DecimalImpl(value, 0);
        }
        if (typeof value === 'number') {
            if (!Number.isFinite(value)) {
                throw new Error('Number must be finite');
            }
            return exports.Decimal.from(value.toString());
        }
        // Parse string.
        const trimmed = value.trim();
        if (trimmed === '') {
            throw new Error('Cannot parse empty string as Decimal');
        }
        // Match: optional sign, integer digits, optional fraction, optional exponent.
        const match = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/.exec(trimmed);
        if (!match) {
            throw new Error(`Invalid decimal string: ${value}`);
        }
        const sign = match[1] === '-' ? -1n : 1n;
        const integerPart = match[2] ?? '';
        const fractionalPart = match[3] ?? '';
        const exponentPart = match[4] ? Number(match[4]) : 0;
        if (!Number.isSafeInteger(exponentPart) ||
            exponentPart > MAX_EXPONENT ||
            exponentPart < -MAX_EXPONENT) {
            throw new Error(`Exponent out of range: ${String(match[4])} exceeds safe integer bounds`);
        }
        const coefficientStr = integerPart + fractionalPart;
        const coefficient = sign * BigInt(coefficientStr);
        const exponent = exponentPart - fractionalPart.length;
        if (!Number.isSafeInteger(exponent) ||
            exponent > MAX_EXPONENT ||
            exponent < -MAX_EXPONENT) {
            throw new Error(`Computed exponent out of range: ${String(exponent)} exceeds safe integer bounds`);
        }
        return new DecimalImpl(coefficient, exponent);
    },
    /**
     * The {@link Decimal} value representing zero.
     *
     * @remarks
     * Pre-allocated singleton — prefer `Decimal.zero` over
     * `Decimal.from(0)` to avoid an unnecessary allocation.
     *
     * @public
     */
    zero: new DecimalImpl(0n, 0),
};
//# sourceMappingURL=Decimal.js.map