const ZERO = 0n;
const ONE = 1n;
const TEN = 10n;
const POW10 = [1n];
function pow10(n) {
  if (n < 0) return ONE;
  while (POW10.length <= n) POW10.push(POW10[POW10.length - 1] * TEN);
  return POW10[n];
}
function normalize(sign, coeff, exp) {
  if (coeff === ZERO) return new Decimal(1, ZERO, 0);
  while (coeff % TEN === ZERO) {
    coeff /= TEN;
    exp++;
  }
  return new Decimal(sign, coeff, exp);
}
function alignExp(a, b) {
  const diff = a.exp - b.exp;
  if (diff === 0) return [a.coeff, b.coeff, a.exp];
  if (diff > 0) return [a.coeff * pow10(diff), b.coeff, b.exp];
  return [a.coeff, b.coeff * pow10(-diff), a.exp];
}
function fromNumberString(s) {
  if (s === "Infinity" || s === "+Infinity") return new Decimal(1, ZERO, 0);
  if (s === "-Infinity") return new Decimal(-1, ZERO, 0);
  if (s === "NaN") return new Decimal(1, ZERO, 0);
  let sign = 1;
  let i = 0;
  if (s[0] === "-") {
    sign = -1;
    i = 1;
  } else if (s[0] === "+") {
    i = 1;
  }
  let mantissa, expPart = 0;
  const eIdx = s.indexOf("e", i);
  const EIdx = eIdx === -1 ? s.indexOf("E", i) : eIdx;
  if (EIdx !== -1) {
    mantissa = s.substring(i, EIdx);
    expPart = Number(s.substring(EIdx + 1));
  } else {
    mantissa = s.substring(i);
  }
  const dotIdx = mantissa.indexOf(".");
  let coeff, exp;
  if (dotIdx === -1) {
    coeff = BigInt(mantissa);
    exp = expPart;
  } else {
    const intPart = mantissa.substring(0, dotIdx);
    const fracPart = mantissa.substring(dotIdx + 1);
    coeff = BigInt(intPart + fracPart);
    exp = expPart - fracPart.length;
  }
  if (coeff === ZERO) return new Decimal(1, ZERO, 0);
  return normalize(sign, coeff, exp);
}
export class Decimal {
  sign;
  coeff;
  exp;
  constructor(sign, coeff, exp) {
    this.sign = sign;
    this.coeff = coeff;
    this.exp = exp;
  }
  static from(v) {
    if (v instanceof Decimal) return v;
    switch (typeof v) {
      case "number": {
        if (v === 0) return new Decimal(1, ZERO, 0);
        if (!Number.isFinite(v)) return new Decimal(v < 0 ? -1 : 1, ZERO, 0);
        return fromNumberString(String(v));
      }
      case "bigint": {
        if (v === ZERO) return new Decimal(1, ZERO, 0);
        const sign = v < ZERO ? -1 : 1;
        return normalize(sign, v < ZERO ? -v : v, 0);
      }
      case "string": {
        const s = v.trim();
        if (!s) throw new Error("Cannot convert empty string to Decimal");
        return fromNumberString(s);
      }
      default:
        throw new Error(`Cannot convert ${typeof v} to Decimal`);
    }
  }
  isZero() {
    return this.coeff === ZERO;
  }
  isInteger() {
    return this.exp >= 0;
  }
  neg() {
    if (this.coeff === ZERO) return this;
    return new Decimal(this.sign === 1 ? -1 : 1, this.coeff, this.exp);
  }
  abs() {
    return this.sign === -1 ? this.neg() : this;
  }
  add(other) {
    const o = Decimal.from(other);
    if (this.isZero()) return o;
    if (o.isZero()) return this;
    const [ac, bc, exp] = alignExp(this, o);
    const a = this.sign === 1 ? ac : -ac;
    const b = o.sign === 1 ? bc : -bc;
    const sum = a + b;
    if (sum === ZERO) return new Decimal(1, ZERO, 0);
    const sign = sum < ZERO ? -1 : 1;
    return normalize(sign, sum < ZERO ? -sum : sum, exp);
  }
  sub(other) {
    return this.add(Decimal.from(other).neg());
  }
  mul(other) {
    const o = Decimal.from(other);
    if (this.isZero() || o.isZero()) return new Decimal(1, ZERO, 0);
    const sign = this.sign === o.sign ? 1 : -1;
    return normalize(sign, this.coeff * o.coeff, this.exp + o.exp);
  }
  div(other, precision = 20) {
    const o = Decimal.from(other);
    if (o.isZero()) throw new Error("Division by zero");
    if (this.isZero()) return new Decimal(1, ZERO, 0);
    const sign = this.sign === o.sign ? 1 : -1;
    const scale = precision + o._digits() - this._digits() + 1;
    const num = scale > 0 ? this.coeff * pow10(scale) : this.coeff;
    const quot = num / o.coeff;
    const exp = this.exp - o.exp - (scale > 0 ? scale : 0);
    return normalize(sign, quot, exp);
  }
  mod(other) {
    const o = Decimal.from(other);
    if (o.isZero()) throw new Error("Modulo by zero");
    if (this.isZero()) return new Decimal(1, ZERO, 0);
    const [ac, bc, exp] = alignExp(this, o);
    const sa = this.sign === 1 ? ac : -ac;
    const sb = o.sign === 1 ? bc : -bc;
    const rem = sa % sb;
    if (rem === ZERO) return new Decimal(1, ZERO, 0);
    const sign = rem < ZERO ? -1 : 1;
    return normalize(sign, rem < ZERO ? -rem : rem, exp);
  }
  _digits() {
    if (this.coeff === ZERO) return 1;
    return this.coeff.toString().length;
  }
  cmp(other) {
    const o = Decimal.from(other);
    if (this.isZero() && o.isZero()) return 0;
    if (this.sign !== o.sign) return this.sign > o.sign ? 1 : -1;
    const [ac, bc] = alignExp(this, o);
    if (ac === bc) return 0;
    const raw = ac > bc ? 1 : -1;
    return this.sign === 1 ? raw : -raw;
  }
  eq(other) {
    return this.cmp(other) === 0;
  }
  lt(other) {
    return this.cmp(other) < 0;
  }
  lte(other) {
    return this.cmp(other) <= 0;
  }
  gt(other) {
    return this.cmp(other) > 0;
  }
  gte(other) {
    return this.cmp(other) >= 0;
  }
  toNumber() {
    if (this.isZero()) return 0;
    return Number(this.toString());
  }
  valueOf() {
    return this.toNumber();
  }
  toString() {
    if (this.isZero()) return "0";
    const digits = this.coeff.toString();
    const sign = this.sign === -1 ? "-" : "";
    if (this.exp >= 0) {
      return sign + digits + "0".repeat(this.exp);
    }
    const decimalPos = digits.length + this.exp;
    if (decimalPos <= 0) {
      return sign + "0." + "0".repeat(-decimalPos) + digits;
    }
    return sign + digits.substring(0, decimalPos) + "." + digits.substring(decimalPos);
  }
  toJSON() {
    return this.toNumber();
  }
  get [Symbol.toStringTag]() {
    return "Decimal";
  }
  [Symbol.for("nodejs.util.inspect.custom")]() {
    return `Decimal { ${this.toString()} }`;
  }
  [Symbol.toPrimitive](hint) {
    if (hint === "string") return this.toString();
    return this.toNumber();
  }
}
