/**
 * Decimal arithmetic backed by an unscaled `bigint` and a decimal scale.
 *
 * `BigDecimal` is useful when values must keep their decimal representation
 * instead of inheriting JavaScript's binary floating-point rounding, such as
 * money, quantities, measurements, or protocol values exchanged as decimal
 * strings. The module includes constructors, parsers, arithmetic operations,
 * comparisons, rounding helpers, and string formatting.
 *
 * **Mental model**
 *
 * - A `BigDecimal` stores `value * 10^-scale`; `make(12345n, 2)` represents
 *   `123.45`
 * - Values are immutable; arithmetic returns new `BigDecimal` values
 * - Equivalent values can have different internal scales, so equality and
 *   ordering normalize trailing zeros
 * - Division can produce repeating decimals, so {@link divide} and
 *   {@link divideUnsafe} use the module's default division precision
 *
 * **Common tasks**
 *
 * - Construct values: {@link make}, {@link fromBigInt}, {@link fromString},
 *   {@link fromNumber}
 * - Render values: {@link format}, {@link toExponential},
 *   {@link toNumberUnsafe}
 * - Do arithmetic: {@link sum}, {@link subtract}, {@link multiply},
 *   {@link divide}, {@link remainder}, {@link negate}, {@link abs}
 * - Compare and constrain values: {@link equals}, {@link Order},
 *   {@link isLessThan}, {@link between}, {@link clamp}, {@link min},
 *   {@link max}
 * - Adjust decimal places: {@link scale}, {@link round}, {@link truncate},
 *   {@link ceil}, {@link floor}
 *
 * **Gotchas**
 *
 * - Prefer {@link fromString} or {@link fromBigInt} for external decimal data.
 *   {@link fromNumber} can only preserve the decimal spelling of a finite
 *   JavaScript number after any binary floating-point precision has already
 *   been lost.
 * - {@link divide} and {@link remainder} return `Option.none()` for division
 *   by zero; {@link divideUnsafe} and {@link remainderUnsafe} throw instead.
 * - Parsed scales must fit in JavaScript's safe integer range.
 *
 * **Example** (Decimal arithmetic)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const subtotal = BigDecimal.multiply(
 *   BigDecimal.fromStringUnsafe("19.99"),
 *   BigDecimal.fromBigInt(3n)
 * )
 * const total = BigDecimal.sum(subtotal, BigDecimal.fromStringUnsafe("1.50"))
 *
 * console.log(BigDecimal.format(total)) // "61.47"
 * ```
 *
 * @since 2.0.0
 */
import * as Equal from "./Equal.js";
import * as Equ from "./Equivalence.js";
import { dual } from "./Function.js";
import * as Hash from "./Hash.js";
import { NodeInspectSymbol } from "./Inspectable.js";
import * as Option from "./Option.js";
import * as order from "./Order.js";
import { pipeArguments } from "./Pipeable.js";
import { hasProperty } from "./Predicate.js";
const DEFAULT_PRECISION = 100;
const FINITE_INT_REGEXP = /^[+-]?\d+$/;
const TypeId = "~effect/BigDecimal";
const BigDecimalProto = {
  [TypeId]: TypeId,
  [Hash.symbol]() {
    const normalized = normalize(this);
    return Hash.combine(Hash.hash(normalized.value), Hash.number(normalized.scale));
  },
  [Equal.symbol](that) {
    return isBigDecimal(that) && equals(this, that);
  },
  toString() {
    return `BigDecimal(${format(this)})`;
  },
  toJSON() {
    return {
      _id: "BigDecimal",
      value: String(this.value),
      scale: this.scale
    };
  },
  [NodeInspectSymbol]() {
    return this.toJSON();
  },
  pipe() {
    return pipeArguments(this, arguments);
  }
};
/**
 * Checks whether a given value is a `BigDecimal`.
 *
 * **When to use**
 *
 * Use to validate unknown input and narrow it to `BigDecimal`.
 *
 * **Example** (Checking BigDecimal values)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const decimal = BigDecimal.fromNumber(123.45)
 * console.log(BigDecimal.isBigDecimal(decimal)) // true
 * console.log(BigDecimal.isBigDecimal(123.45)) // false
 * console.log(BigDecimal.isBigDecimal("123.45")) // false
 * ```
 *
 * @category guards
 * @since 2.0.0
 */
export const isBigDecimal = u => hasProperty(u, TypeId);
/**
 * Creates a `BigDecimal` from a `bigint` value and a scale.
 *
 * **When to use**
 *
 * Use to construct a decimal directly from its unscaled integer value and
 * decimal scale.
 *
 * **Example** (Creating decimals from bigint and scale)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * // Create 123.45 (12345 with scale 2)
 * const decimal = BigDecimal.make(12345n, 2)
 * console.log(BigDecimal.format(decimal)) // "123.45"
 *
 * // Create 42 (42 with scale 0)
 * const integer = BigDecimal.make(42n, 0)
 * console.log(BigDecimal.format(integer)) // "42"
 * ```
 *
 * @see {@link fromBigInt} for constructing an integer decimal from a `bigint`
 *
 * @category constructors
 * @since 2.0.0
 */
export const make = (value, scale) => {
  const o = Object.create(BigDecimalProto);
  o.value = value;
  o.scale = scale;
  return o;
};
/**
 * Internal function used to create pre-normalized `BigDecimal`s.
 *
 * @internal
 */
export const makeNormalizedUnsafe = (value, scale) => {
  if (value !== bigint0 && value % bigint10 === bigint0) {
    throw new RangeError("Value must be normalized");
  }
  const o = make(value, scale);
  o.normalized = o;
  return o;
};
const bigint0 = /*#__PURE__*/BigInt(0);
const bigint1 = /*#__PURE__*/BigInt(1);
const bigint_1 = /*#__PURE__*/BigInt(-1);
const bigint2 = /*#__PURE__*/BigInt(2);
const bigint5 = /*#__PURE__*/BigInt(5);
const bigint_5 = /*#__PURE__*/BigInt(-5);
const bigint10 = /*#__PURE__*/BigInt(10);
const zero = /*#__PURE__*/makeNormalizedUnsafe(bigint0, 0);
const one = /*#__PURE__*/makeNormalizedUnsafe(bigint1, 0);
/**
 * Normalizes a given `BigDecimal` by removing trailing zeros.
 *
 * **When to use**
 *
 * Use to canonicalize decimals that have equivalent values but different
 * internal scales.
 *
 * **Example** (Normalizing trailing zeros)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.normalize(BigDecimal.fromStringUnsafe("123.00000")),
 *   BigDecimal.normalize(BigDecimal.make(123n, 0))
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.normalize(BigDecimal.fromStringUnsafe("12300000")),
 *   BigDecimal.normalize(BigDecimal.make(123n, -5))
 * )
 * ```
 *
 * @see {@link format} for rendering normalized decimals as strings
 *
 * @category scaling
 * @since 2.0.0
 */
export const normalize = self => {
  if (self.normalized === undefined) {
    if (self.value === bigint0) {
      self.normalized = zero;
    } else {
      const digits = `${self.value}`;
      let trail = 0;
      for (let i = digits.length - 1; i >= 0; i--) {
        if (digits[i] === "0") {
          trail++;
        } else {
          break;
        }
      }
      if (trail === 0) {
        self.normalized = self;
      }
      const value = BigInt(digits.substring(0, digits.length - trail));
      const scale = self.scale - trail;
      self.normalized = makeNormalizedUnsafe(value, scale);
    }
  }
  return self.normalized;
};
/**
 * Changes a `BigDecimal` to the specified scale.
 *
 * **When to use**
 *
 * Use to change how many decimal places are represented by a `BigDecimal`.
 *
 * **Details**
 *
 * Increasing the scale appends decimal zeros. Decreasing the scale discards
 * digits beyond the target scale by `bigint` division, which truncates toward
 * zero.
 *
 * **Example** (Scaling decimal precision)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const decimal = BigDecimal.fromNumberUnsafe(123.45)
 *
 * // Increase scale (add more precision)
 * const scaled = BigDecimal.scale(decimal, 4)
 * console.log(BigDecimal.format(scaled)) // "123.4500"
 *
 * // Decrease scale (reduce precision, rounds down)
 * const reduced = BigDecimal.scale(decimal, 1)
 * console.log(BigDecimal.format(reduced)) // "123.4"
 * ```
 *
 * @see {@link round} for changing scale with configurable rounding
 *
 * @category scaling
 * @since 2.0.0
 */
export const scale = /*#__PURE__*/dual(2, (self, scale) => {
  if (scale > self.scale) {
    return make(self.value * bigint10 ** BigInt(scale - self.scale), scale);
  }
  if (scale < self.scale) {
    return make(self.value / bigint10 ** BigInt(self.scale - scale), scale);
  }
  return self;
});
/**
 * Provides an addition operation on `BigDecimal`s.
 *
 * **When to use**
 *
 * Use to add two `BigDecimal` values.
 *
 * **Example** (Adding decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.sum(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   BigDecimal.fromStringUnsafe("5")
 * )
 * ```
 *
 * @see {@link sumAll} for summing an iterable of `BigDecimal` values
 *
 * @category math
 * @since 2.0.0
 */
export const sum = /*#__PURE__*/dual(2, (self, that) => {
  if (that.value === bigint0) {
    return self;
  }
  if (self.value === bigint0) {
    return that;
  }
  if (self.scale > that.scale) {
    return make(scale(that, self.scale).value + self.value, self.scale);
  }
  if (self.scale < that.scale) {
    return make(scale(self, that.scale).value + that.value, that.scale);
  }
  return make(self.value + that.value, self.scale);
});
/**
 * Takes an `Iterable` of `BigDecimal`s and returns their sum as a single `BigDecimal`.
 *
 * **When to use**
 *
 * Use to sum all `BigDecimal` values in an iterable.
 *
 * **Example** (Adding multiple decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.sumAll([BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("4")]),
 *   BigDecimal.fromStringUnsafe("9")
 * )
 * ```
 *
 * @see {@link sum} for adding two `BigDecimal` values
 *
 * @category math
 * @since 3.16.0
 */
export const sumAll = collection => {
  let out = zero;
  for (const n of collection) {
    out = sum(out, n);
  }
  return out;
};
/**
 * Provides a multiplication operation on `BigDecimal`s.
 *
 * **When to use**
 *
 * Use to multiply two `BigDecimal` values.
 *
 * **Example** (Multiplying decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.multiply(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   BigDecimal.fromStringUnsafe("6")
 * )
 * ```
 *
 * @see {@link multiplyAll} for multiplying an iterable of `BigDecimal` values
 *
 * @category math
 * @since 2.0.0
 */
export const multiply = /*#__PURE__*/dual(2, (self, that) => {
  if (that.value === bigint0 || self.value === bigint0) {
    return zero;
  }
  return make(self.value * that.value, self.scale + that.scale);
});
/**
 * Takes an `Iterable` of `BigDecimal`s and returns their multiplication as a single `BigDecimal`.
 *
 * **When to use**
 *
 * Use to multiply all `BigDecimal` values in an iterable.
 *
 * **Example** (Multiplying multiple decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.multiplyAll([BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("4")]),
 *   BigDecimal.fromStringUnsafe("24")
 * )
 * ```
 *
 * @see {@link multiply} for multiplying two `BigDecimal` values
 *
 * @category math
 * @since 4.0.0
 */
export const multiplyAll = collection => {
  let out = one;
  for (const n of collection) {
    if (n.value === bigint0) {
      return zero;
    }
    out = multiply(out, n);
  }
  return out;
};
/**
 * Provides a subtraction operation on `BigDecimal`s.
 *
 * **When to use**
 *
 * Use to subtract one `BigDecimal` value from another.
 *
 * **Example** (Subtracting decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.subtract(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   BigDecimal.fromStringUnsafe("-1")
 * )
 * ```
 *
 * @category math
 * @since 2.0.0
 */
export const subtract = /*#__PURE__*/dual(2, (self, that) => {
  if (that.value === bigint0) {
    return self;
  }
  if (self.value === bigint0) {
    return make(-that.value, that.scale);
  }
  if (self.scale > that.scale) {
    return make(self.value - scale(that, self.scale).value, self.scale);
  }
  if (self.scale < that.scale) {
    return make(scale(self, that.scale).value - that.value, that.scale);
  }
  return make(self.value - that.value, self.scale);
});
/**
 * Internal function used for arbitrary precision division.
 */
const divideWithPrecision = (num, den, scale, precision) => {
  const numNegative = num < bigint0;
  const denNegative = den < bigint0;
  const negateResult = numNegative !== denNegative;
  num = numNegative ? -num : num;
  den = denNegative ? -den : den;
  // Shift digits until numerator is larger than denominator (set scale appropriately).
  while (num < den) {
    num *= bigint10;
    scale++;
  }
  // First division.
  let quotient = num / den;
  let remainder = num % den;
  if (remainder === bigint0) {
    // No remainder, return immediately.
    return make(negateResult ? -quotient : quotient, scale);
  }
  // The quotient is guaranteed to be non-negative at this point. No need to consider sign.
  let count = `${quotient}`.length;
  // Shift the remainder by 1 decimal; The quotient will be 1 digit upon next division.
  remainder *= bigint10;
  while (remainder !== bigint0 && count < precision) {
    const q = remainder / den;
    const r = remainder % den;
    quotient = quotient * bigint10 + q;
    remainder = r * bigint10;
    count++;
    scale++;
  }
  if (remainder !== bigint0) {
    // Round final number with remainder.
    quotient += roundTerminal(remainder / den);
  }
  return make(negateResult ? -quotient : quotient, scale);
};
/**
 * Internal function used for rounding.
 *
 * Returns 1 if the most significant digit is >= 5, otherwise 0.
 *
 * This is used after dividing a number by a power of ten and rounding the last digit.
 *
 * @internal
 */
export const roundTerminal = n => {
  const pos = n >= bigint0 ? 0 : 1;
  return Number(`${n}`[pos]) < 5 ? bigint0 : bigint1;
};
/**
 * Divides `BigDecimal`s safely.
 *
 * **When to use**
 *
 * Use to divide `BigDecimal` values while representing division by zero as
 * `Option.none`.
 *
 * **Details**
 *
 * If the dividend is not a multiple of the divisor, the result will be a `BigDecimal` value
 * with up to the default division precision. If the divisor is `0`, the result
 * will be `Option.none()`.
 *
 * **Example** (Dividing decimals safely)
 *
 * ```ts
 * import { BigDecimal, Option } from "effect"
 *
 * console.log(
 *   Option.getOrThrow(
 *     BigDecimal.divide(
 *       BigDecimal.fromStringUnsafe("6"),
 *       BigDecimal.fromStringUnsafe("3")
 *     )
 *   )
 * ) // BigDecimal(2)
 * console.log(
 *   Option.getOrThrow(
 *     BigDecimal.divide(
 *       BigDecimal.fromStringUnsafe("6"),
 *       BigDecimal.fromStringUnsafe("4")
 *     )
 *   )
 * ) // BigDecimal(1.5)
 * console.log(
 *   Option.isNone(
 *     BigDecimal.divide(
 *       BigDecimal.fromStringUnsafe("6"),
 *       BigDecimal.fromStringUnsafe("0")
 *     )
 *   )
 * ) // true
 * ```
 *
 * @see {@link divideUnsafe} for division that throws when the divisor is zero
 * @see {@link remainder} for the decimal remainder operation
 *
 * @category math
 * @since 2.0.0
 */
export const divide = /*#__PURE__*/dual(2, (self, that) => {
  if (that.value === bigint0) {
    return Option.none();
  }
  if (self.value === bigint0) {
    return Option.some(zero);
  }
  const scale = self.scale - that.scale;
  if (self.value === that.value) {
    return Option.some(make(bigint1, scale));
  }
  return Option.some(divideWithPrecision(self.value, that.value, scale, DEFAULT_PRECISION));
});
/**
 * Provides an unsafe division operation on `BigDecimal`s.
 *
 * **When to use**
 *
 * Use when you need the decimal quotient and the divisor is known to be
 * non-zero, so division by zero should be a thrown exception.
 *
 * **Details**
 *
 * If the dividend is not a multiple of the divisor, the result will be a `BigDecimal` value
 * with up to the default division precision.
 *
 * **Gotchas**
 *
 * Throws a `RangeError` if the divisor is `0`.
 *
 * **Example** (Dividing decimals unsafely)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * console.log(BigDecimal.divideUnsafe(BigDecimal.fromStringUnsafe("6"), BigDecimal.fromStringUnsafe("3"))) // BigDecimal(2)
 * console.log(BigDecimal.divideUnsafe(BigDecimal.fromStringUnsafe("6"), BigDecimal.fromStringUnsafe("4"))) // BigDecimal(1.5)
 * ```
 *
 * @see {@link divide} for division that returns `Option.none` when the divisor is zero
 *
 * @category math
 * @since 4.0.0
 */
export const divideUnsafe = /*#__PURE__*/dual(2, (self, that) => {
  if (that.value === bigint0) {
    throw new RangeError("Division by zero");
  }
  if (self.value === bigint0) {
    return zero;
  }
  const scale = self.scale - that.scale;
  if (self.value === that.value) {
    return make(bigint1, scale);
  }
  return divideWithPrecision(self.value, that.value, scale, DEFAULT_PRECISION);
});
/**
 * Provides an `Order` instance for `BigDecimal` that allows comparing and sorting BigDecimal values.
 *
 * **When to use**
 *
 * Use when sorting or comparing decimal values through APIs that accept an
 * ordering instance.
 *
 * **Example** (Comparing decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const a = BigDecimal.fromNumberUnsafe(1.5)
 * const b = BigDecimal.fromNumberUnsafe(2.3)
 * const c = BigDecimal.fromNumberUnsafe(1.5)
 *
 * console.log(BigDecimal.Order(a, b)) // -1 (a < b)
 * console.log(BigDecimal.Order(b, a)) // 1 (b > a)
 * console.log(BigDecimal.Order(a, c)) // 0 (a === c)
 * ```
 *
 * @category instances
 * @since 2.0.0
 */
export const Order = /*#__PURE__*/order.make((self, that) => {
  const scmp = order.Number(sign(self), sign(that));
  if (scmp !== 0) {
    return scmp;
  }
  if (self.scale > that.scale) {
    return order.BigInt(self.value, scale(that, self.scale).value);
  }
  if (self.scale < that.scale) {
    return order.BigInt(scale(self, that.scale).value, that.value);
  }
  return order.BigInt(self.value, that.value);
});
/**
 * Returns `true` if the first argument is less than the second, otherwise `false`.
 *
 * **When to use**
 *
 * Use to test whether one `BigDecimal` is strictly less than another.
 *
 * **Example** (Checking less-than comparisons)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.isLessThan(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   true
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isLessThan(BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("3")),
 *   false
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isLessThan(BigDecimal.fromStringUnsafe("4"), BigDecimal.fromStringUnsafe("3")),
 *   false
 * )
 * ```
 *
 * @category predicates
 * @since 4.0.0
 */
export const isLessThan = /*#__PURE__*/order.isLessThan(Order);
/**
 * Checks whether a given `BigDecimal` is less than or equal to the provided one.
 *
 * **When to use**
 *
 * Use to test whether one `BigDecimal` is less than or equal to another.
 *
 * **Example** (Checking less-than-or-equal comparisons)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.isLessThanOrEqualTo(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   true
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isLessThanOrEqualTo(BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("3")),
 *   true
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isLessThanOrEqualTo(BigDecimal.fromStringUnsafe("4"), BigDecimal.fromStringUnsafe("3")),
 *   false
 * )
 * ```
 *
 * @category predicates
 * @since 4.0.0
 */
export const isLessThanOrEqualTo = /*#__PURE__*/order.isLessThanOrEqualTo(Order);
/**
 * Returns `true` if the first argument is greater than the second, otherwise `false`.
 *
 * **When to use**
 *
 * Use to test whether one `BigDecimal` is strictly greater than another.
 *
 * **Example** (Checking greater-than comparisons)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.isGreaterThan(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   false
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isGreaterThan(BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("3")),
 *   false
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isGreaterThan(BigDecimal.fromStringUnsafe("4"), BigDecimal.fromStringUnsafe("3")),
 *   true
 * )
 * ```
 *
 * @category predicates
 * @since 4.0.0
 */
export const isGreaterThan = /*#__PURE__*/order.isGreaterThan(Order);
/**
 * Checks whether a given `BigDecimal` is greater than or equal to the provided one.
 *
 * **When to use**
 *
 * Use to test whether one `BigDecimal` is greater than or equal to another.
 *
 * **Example** (Checking greater-than-or-equal comparisons)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.isGreaterThanOrEqualTo(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   false
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isGreaterThanOrEqualTo(BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("3")),
 *   true
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.isGreaterThanOrEqualTo(BigDecimal.fromStringUnsafe("4"), BigDecimal.fromStringUnsafe("3")),
 *   true
 * )
 * ```
 *
 * @category predicates
 * @since 4.0.0
 */
export const isGreaterThanOrEqualTo = /*#__PURE__*/order.isGreaterThanOrEqualTo(Order);
/**
 * Checks whether a `BigDecimal` is between a `minimum` and `maximum` value (inclusive).
 *
 * **When to use**
 *
 * Use to test whether a `BigDecimal` falls inside an inclusive range.
 *
 * **Example** (Checking decimal ranges)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * const between = BigDecimal.between({
 *   minimum: BigDecimal.fromStringUnsafe("1"),
 *   maximum: BigDecimal.fromStringUnsafe("5")
 * })
 *
 * assert.deepStrictEqual(between(BigDecimal.fromStringUnsafe("3")), true)
 * assert.deepStrictEqual(between(BigDecimal.fromStringUnsafe("0")), false)
 * assert.deepStrictEqual(between(BigDecimal.fromStringUnsafe("6")), false)
 * ```
 *
 * @see {@link clamp} for forcing a `BigDecimal` into an inclusive range
 *
 * @category predicates
 * @since 2.0.0
 */
export const between = /*#__PURE__*/order.isBetween(Order);
/**
 * Restricts the given `BigDecimal` to be within the range specified by the `minimum` and `maximum` values.
 *
 * **When to use**
 *
 * Use to force a `BigDecimal` into an inclusive range.
 *
 * **Details**
 *
 * If the `BigDecimal` is less than the `minimum` value, the function returns
 * the `minimum` value. If it is greater than the `maximum` value, the function
 * returns the `maximum` value. Otherwise, it returns the original `BigDecimal`.
 *
 * **Example** (Clamping decimals to a range)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * const clamp = BigDecimal.clamp({
 *   minimum: BigDecimal.fromStringUnsafe("1"),
 *   maximum: BigDecimal.fromStringUnsafe("5")
 * })
 *
 * assert.deepStrictEqual(
 *   clamp(BigDecimal.fromStringUnsafe("3")),
 *   BigDecimal.fromStringUnsafe("3")
 * )
 * assert.deepStrictEqual(
 *   clamp(BigDecimal.fromStringUnsafe("0")),
 *   BigDecimal.fromStringUnsafe("1")
 * )
 * assert.deepStrictEqual(
 *   clamp(BigDecimal.fromStringUnsafe("6")),
 *   BigDecimal.fromStringUnsafe("5")
 * )
 * ```
 *
 * @see {@link between} for checking whether a `BigDecimal` is already inside a range
 *
 * @category math
 * @since 2.0.0
 */
export const clamp = /*#__PURE__*/order.clamp(Order);
/**
 * Returns the minimum between two `BigDecimal`s.
 *
 * **When to use**
 *
 * Use to select the smaller of two `BigDecimal` values.
 *
 * **Example** (Selecting the smaller decimal)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.min(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   BigDecimal.fromStringUnsafe("2")
 * )
 * ```
 *
 * @see {@link max} for selecting the larger value
 *
 * @category math
 * @since 2.0.0
 */
export const min = /*#__PURE__*/order.min(Order);
/**
 * Returns the maximum between two `BigDecimal`s.
 *
 * **When to use**
 *
 * Use to select the larger of two `BigDecimal` values.
 *
 * **Example** (Selecting the larger decimal)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.max(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("3")),
 *   BigDecimal.fromStringUnsafe("3")
 * )
 * ```
 *
 * @see {@link min} for selecting the smaller value
 *
 * @category math
 * @since 2.0.0
 */
export const max = /*#__PURE__*/order.max(Order);
/**
 * Determines the sign of a given `BigDecimal`.
 *
 * **When to use**
 *
 * Use to classify a `BigDecimal` as negative, zero, or positive.
 *
 * **Example** (Reading decimal signs)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.sign(BigDecimal.fromStringUnsafe("-5")), -1)
 * assert.deepStrictEqual(BigDecimal.sign(BigDecimal.fromStringUnsafe("0")), 0)
 * assert.deepStrictEqual(BigDecimal.sign(BigDecimal.fromStringUnsafe("5")), 1)
 * ```
 *
 * @category math
 * @since 2.0.0
 */
export const sign = n => n.value === bigint0 ? 0 : n.value < bigint0 ? -1 : 1;
/**
 * Determines the absolute value of a given `BigDecimal`.
 *
 * **When to use**
 *
 * Use to remove the sign from a `BigDecimal` while preserving its magnitude.
 *
 * **Example** (Calculating absolute values)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.abs(BigDecimal.fromStringUnsafe("-5")), BigDecimal.fromStringUnsafe("5"))
 * assert.deepStrictEqual(BigDecimal.abs(BigDecimal.fromStringUnsafe("0")), BigDecimal.fromStringUnsafe("0"))
 * assert.deepStrictEqual(BigDecimal.abs(BigDecimal.fromStringUnsafe("5")), BigDecimal.fromStringUnsafe("5"))
 * ```
 *
 * @category math
 * @since 2.0.0
 */
export const abs = n => n.value < bigint0 ? make(-n.value, n.scale) : n;
/**
 * Provides a negate operation on `BigDecimal`s.
 *
 * **When to use**
 *
 * Use to flip the sign of a `BigDecimal`.
 *
 * **Example** (Negating decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.negate(BigDecimal.fromStringUnsafe("3")), BigDecimal.fromStringUnsafe("-3"))
 * assert.deepStrictEqual(BigDecimal.negate(BigDecimal.fromStringUnsafe("-6")), BigDecimal.fromStringUnsafe("6"))
 * ```
 *
 * @category math
 * @since 2.0.0
 */
export const negate = n => make(-n.value, n.scale);
/**
 * Computes the decimal remainder safely when one operand is divided by a second
 * operand.
 *
 * **When to use**
 *
 * Use to compute a decimal remainder while representing division by zero as
 * `Option.none`.
 *
 * **Details**
 *
 * If the divisor is `0`, the result will be `Option.none()`.
 *
 * **Example** (Computing remainders safely)
 *
 * ```ts
 * import { BigDecimal, Option } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.remainder(
 *     BigDecimal.fromStringUnsafe("2"),
 *     BigDecimal.fromStringUnsafe("2")
 *   ),
 *   Option.some(BigDecimal.fromStringUnsafe("0"))
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.remainder(
 *     BigDecimal.fromStringUnsafe("3"),
 *     BigDecimal.fromStringUnsafe("2")
 *   ),
 *   Option.some(BigDecimal.fromStringUnsafe("1"))
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.remainder(
 *     BigDecimal.fromStringUnsafe("-4"),
 *     BigDecimal.fromStringUnsafe("2")
 *   ),
 *   Option.some(BigDecimal.fromStringUnsafe("0"))
 * )
 * ```
 *
 * @see {@link remainderUnsafe} for remainder calculation that throws when the divisor is zero
 * @see {@link divide} for decimal quotient calculation
 *
 * @category math
 * @since 2.0.0
 */
export const remainder = /*#__PURE__*/dual(2, (self, divisor) => {
  if (divisor.value === bigint0) {
    return Option.none();
  }
  const max = Math.max(self.scale, divisor.scale);
  return Option.some(make(scale(self, max).value % scale(divisor, max).value, max));
});
/**
 * Returns the decimal remainder left over when one operand is divided by a
 * non-zero second operand, throwing for division by zero.
 *
 * **When to use**
 *
 * Use when you need the decimal remainder and the divisor is known to be
 * non-zero, so division by zero should be a thrown exception.
 *
 * **Gotchas**
 *
 * Throws a `RangeError` if the divisor is `0`.
 *
 * **Example** (Computing remainders unsafely)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.remainderUnsafe(BigDecimal.fromStringUnsafe("2"), BigDecimal.fromStringUnsafe("2")),
 *   BigDecimal.fromStringUnsafe("0")
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.remainderUnsafe(BigDecimal.fromStringUnsafe("3"), BigDecimal.fromStringUnsafe("2")),
 *   BigDecimal.fromStringUnsafe("1")
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.remainderUnsafe(BigDecimal.fromStringUnsafe("-4"), BigDecimal.fromStringUnsafe("2")),
 *   BigDecimal.fromStringUnsafe("0")
 * )
 * ```
 *
 * @see {@link remainder} for returning `Option.none` when the divisor is zero
 *
 * @category math
 * @since 4.0.0
 */
export const remainderUnsafe = /*#__PURE__*/dual(2, (self, divisor) => {
  if (divisor.value === bigint0) {
    throw new RangeError("Division by zero");
  }
  const max = Math.max(self.scale, divisor.scale);
  return make(scale(self, max).value % scale(divisor, max).value, max);
});
/**
 * Provides an `Equivalence` instance for `BigDecimal` that determines equality between BigDecimal values.
 *
 * **When to use**
 *
 * Use when comparing decimal values through APIs that accept an equivalence
 * relation.
 *
 * **Example** (Checking decimal equivalence)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const a = BigDecimal.fromStringUnsafe("1.50")
 * const b = BigDecimal.fromStringUnsafe("1.5")
 * const c = BigDecimal.fromStringUnsafe("2.0")
 *
 * console.log(BigDecimal.Equivalence(a, b)) // true (1.50 === 1.5)
 * console.log(BigDecimal.Equivalence(a, c)) // false (1.50 !== 2.0)
 * ```
 *
 * @category instances
 * @since 2.0.0
 */
export const Equivalence = /*#__PURE__*/Equ.make((self, that) => {
  if (self.scale > that.scale) {
    return scale(that, self.scale).value === self.value;
  }
  if (self.scale < that.scale) {
    return scale(self, that.scale).value === that.value;
  }
  return self.value === that.value;
});
/**
 * Checks whether two `BigDecimal`s are equal.
 *
 * **When to use**
 *
 * Use to compare two `BigDecimal` values for numeric equality.
 *
 * **Example** (Checking decimal equality)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const a = BigDecimal.fromStringUnsafe("1.5")
 * const b = BigDecimal.fromStringUnsafe("1.50")
 * const c = BigDecimal.fromStringUnsafe("2.0")
 *
 * console.log(BigDecimal.equals(a, b)) // true
 * console.log(BigDecimal.equals(a, c)) // false
 * ```
 *
 * @see {@link Equivalence} for passing decimal equality to APIs that require an `Equivalence`
 *
 * @category predicates
 * @since 2.0.0
 */
export const equals = /*#__PURE__*/dual(2, (self, that) => Equivalence(self, that));
/**
 * Creates a `BigDecimal` from a `bigint` value.
 *
 * **When to use**
 *
 * Use to construct an integer `BigDecimal` from a `bigint`.
 *
 * **Example** (Creating decimals from bigint)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * const decimal = BigDecimal.fromBigInt(123n)
 * console.log(BigDecimal.format(decimal)) // "123"
 *
 * const largeBigInt = BigDecimal.fromBigInt(9007199254740991n)
 * console.log(BigDecimal.format(largeBigInt)) // "9007199254740991"
 * ```
 *
 * @see {@link make} for constructing a decimal with an explicit scale
 *
 * @category constructors
 * @since 2.0.0
 */
export const fromBigInt = n => make(n, 0);
/**
 * Creates a `BigDecimal` from a finite `number`, throwing for non-finite input.
 *
 * **When to use**
 *
 * Use when a finite JavaScript number must become a `BigDecimal` and invalid
 * input should throw.
 *
 * **Gotchas**
 *
 * It is not recommended to convert a floating point number to a decimal
 * directly, as the floating point representation may be unexpected. Throws a
 * `RangeError` if the number is not finite (`NaN`, `+Infinity` or `-Infinity`).
 *
 * **Example** (Creating decimals from finite numbers)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.fromNumberUnsafe(123), BigDecimal.make(123n, 0))
 * assert.deepStrictEqual(BigDecimal.fromNumberUnsafe(123.456), BigDecimal.make(123456n, 3))
 * ```
 *
 * @see {@link fromNumber} for returning `Option.none` when the number is not finite
 *
 * @category constructors
 * @since 4.0.0
 */
export const fromNumberUnsafe = n => {
  return Option.getOrThrowWith(fromNumber(n), () => new RangeError(`Number must be finite, got ${n}`));
};
/**
 * Creates a `BigDecimal` safely from a finite `number`.
 *
 * **When to use**
 *
 * Use to convert a finite JavaScript number to a `BigDecimal` without throwing
 * on invalid input.
 *
 * **Details**
 *
 * Returns `Option.none()` for `NaN`, `+Infinity` or `-Infinity`.
 *
 * **Gotchas**
 *
 * It is not recommended to convert a floating point number to a decimal
 * directly, as the floating point representation may be unexpected.
 *
 * **Example** (Creating decimals from numbers safely)
 *
 * ```ts
 * import { BigDecimal, Option } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.fromNumber(123), Option.some(BigDecimal.make(123n, 0)))
 * assert.deepStrictEqual(
 *   BigDecimal.fromNumber(123.456),
 *   Option.some(BigDecimal.make(123456n, 3))
 * )
 * assert.deepStrictEqual(BigDecimal.fromNumber(Infinity), Option.none())
 * ```
 *
 * @see {@link fromNumberUnsafe} for throwing when the number is not finite
 * @see {@link fromString} for parsing decimal strings directly
 *
 * @category constructors
 * @since 2.0.0
 */
export const fromNumber = n => {
  if (!Number.isFinite(n)) {
    return Option.none();
  }
  const string = `${n}`;
  if (string.includes("e")) {
    return fromString(string);
  }
  const [lead, trail = ""] = string.split(".");
  return Option.some(make(BigInt(`${lead}${trail}`), trail.length));
};
/**
 * Parses a decimal string into a `BigDecimal` safely.
 *
 * **When to use**
 *
 * Use to parse external decimal text without throwing on invalid input.
 *
 * **Details**
 *
 * Returns `Option.some` for valid decimal or exponent notation and
 * `Option.none` when the string cannot be parsed or would produce an unsafe
 * scale. The empty string parses as zero.
 *
 * **Example** (Parsing decimal strings safely)
 *
 * ```ts
 * import { BigDecimal, Option } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.fromString("123"), Option.some(BigDecimal.make(123n, 0)))
 * assert.deepStrictEqual(
 *   BigDecimal.fromString("123.456"),
 *   Option.some(BigDecimal.make(123456n, 3))
 * )
 * assert.deepStrictEqual(BigDecimal.fromString("123.abc"), Option.none())
 * ```
 *
 * @see {@link fromStringUnsafe} for parsing that throws on invalid input
 * @see {@link fromNumber} for converting finite JavaScript numbers
 *
 * @category constructors
 * @since 2.0.0
 */
export const fromString = s => {
  if (s === "") {
    return Option.some(zero);
  }
  let base;
  let exp;
  const seperator = s.search(/[eE]/);
  if (seperator !== -1) {
    const trail = s.slice(seperator + 1);
    base = s.slice(0, seperator);
    exp = Number(trail);
    if (base === "" || !Number.isSafeInteger(exp) || !FINITE_INT_REGEXP.test(trail)) {
      return Option.none();
    }
  } else {
    base = s;
    exp = 0;
  }
  let digits;
  let offset;
  const dot = base.search(/\./);
  if (dot !== -1) {
    const lead = base.slice(0, dot);
    const trail = base.slice(dot + 1);
    digits = `${lead}${trail}`;
    offset = trail.length;
  } else {
    digits = base;
    offset = 0;
  }
  if (!FINITE_INT_REGEXP.test(digits)) {
    return Option.none();
  }
  const scale = offset - exp;
  if (!Number.isSafeInteger(scale)) {
    return Option.none();
  }
  return Option.some(make(BigInt(digits), scale));
};
/**
 * Parses a decimal string into a `BigDecimal`, throwing if the string is
 * invalid.
 *
 * **When to use**
 *
 * Use when decimal text is expected to be valid and parse errors should throw.
 *
 * **Details**
 *
 * Accepts the same syntax as `fromString`. Use `fromString` when invalid input
 * should be represented as `Option.none` instead of throwing.
 *
 * **Example** (Parsing decimal strings unsafely)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.fromStringUnsafe("123"), BigDecimal.make(123n, 0))
 * assert.deepStrictEqual(BigDecimal.fromStringUnsafe("123.456"), BigDecimal.make(123456n, 3))
 * assert.throws(() => BigDecimal.fromStringUnsafe("123.abc"))
 * ```
 *
 * @see {@link fromString} for returning `Option.none` on invalid input
 *
 * @category constructors
 * @since 4.0.0
 */
export const fromStringUnsafe = s => {
  return Option.getOrThrowWith(fromString(s), () => new Error(`Invalid numerical string: ${s}`));
};
/**
 * Formats a `BigDecimal` as a string.
 *
 * **When to use**
 *
 * Use to render a `BigDecimal` as plain decimal text when possible.
 *
 * **Details**
 *
 * The value is normalized before formatting. Scientific notation is used when
 * the absolute value of the normalized scale is at least `16`; otherwise plain
 * decimal notation is used.
 *
 * **Example** (Formatting decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.format(BigDecimal.fromStringUnsafe("-5")), "-5")
 * assert.deepStrictEqual(BigDecimal.format(BigDecimal.fromStringUnsafe("123.456")), "123.456")
 * assert.deepStrictEqual(BigDecimal.format(BigDecimal.fromStringUnsafe("-0.00000123")), "-0.00000123")
 * ```
 *
 * @see {@link toExponential} for always rendering scientific notation
 *
 * @category converting
 * @since 2.0.0
 */
export const format = n => {
  const normalized = normalize(n);
  if (Math.abs(normalized.scale) >= 16) {
    return toExponential(normalized);
  }
  const negative = normalized.value < bigint0;
  const absolute = negative ? `${normalized.value}`.substring(1) : `${normalized.value}`;
  let before;
  let after;
  if (normalized.scale >= absolute.length) {
    before = "0";
    after = "0".repeat(normalized.scale - absolute.length) + absolute;
  } else {
    const location = absolute.length - normalized.scale;
    if (location > absolute.length) {
      const zeros = location - absolute.length;
      before = `${absolute}${"0".repeat(zeros)}`;
      after = "";
    } else {
      after = absolute.slice(location);
      before = absolute.slice(0, location);
    }
  }
  const complete = after === "" ? before : `${before}.${after}`;
  return negative ? `-${complete}` : complete;
};
/**
 * Formats a given `BigDecimal` as a `string` in scientific notation.
 *
 * **When to use**
 *
 * Use to render a `BigDecimal` in scientific notation.
 *
 * **Example** (Formatting decimals exponentially)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.toExponential(BigDecimal.make(123456n, -5)), "1.23456e+10")
 * ```
 *
 * @see {@link format} for plain decimal formatting when possible
 *
 * @category converting
 * @since 3.11.0
 */
export const toExponential = n => {
  if (isZero(n)) {
    return "0e+0";
  }
  const normalized = normalize(n);
  const digits = `${abs(normalized).value}`;
  const head = digits.slice(0, 1);
  const tail = digits.slice(1);
  let output = `${isNegative(normalized) ? "-" : ""}${head}`;
  if (tail !== "") {
    output += `.${tail}`;
  }
  const exp = tail.length - normalized.scale;
  return `${output}e${exp >= 0 ? "+" : ""}${exp}`;
};
/**
 * Converts a `BigDecimal` to a JavaScript `number`.
 *
 * **When to use**
 *
 * Use when an interop boundary requires a JavaScript number and can tolerate
 * precision loss.
 *
 * **Gotchas**
 *
 * This conversion is unsafe because the result can lose integer or fractional
 * precision, round to a nearby representable value, or become `Infinity` when
 * the decimal cannot be represented as a finite JavaScript `number`.
 *
 * **Example** (Converting decimals to numbers)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.toNumberUnsafe(BigDecimal.fromStringUnsafe("123.456")), 123.456)
 * ```
 *
 * @see {@link format} for preserving decimal precision as text
 *
 * @category converting
 * @since 4.0.0
 */
export const toNumberUnsafe = n => Number(format(n));
/**
 * Checks whether a given `BigDecimal` is an integer.
 *
 * **When to use**
 *
 * Use to test whether a `BigDecimal` has no fractional decimal part.
 *
 * **Example** (Checking integer decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.isInteger(BigDecimal.fromStringUnsafe("0")), true)
 * assert.deepStrictEqual(BigDecimal.isInteger(BigDecimal.fromStringUnsafe("1")), true)
 * assert.deepStrictEqual(BigDecimal.isInteger(BigDecimal.fromStringUnsafe("1.1")), false)
 * ```
 *
 * @category predicates
 * @since 2.0.0
 */
export const isInteger = n => normalize(n).scale <= 0;
/**
 * Checks whether a given `BigDecimal` is `0`.
 *
 * **When to use**
 *
 * Use to test whether a `BigDecimal` is exactly zero.
 *
 * **Example** (Checking zero decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.isZero(BigDecimal.fromStringUnsafe("0")), true)
 * assert.deepStrictEqual(BigDecimal.isZero(BigDecimal.fromStringUnsafe("1")), false)
 * ```
 *
 * @category predicates
 * @since 2.0.0
 */
export const isZero = n => n.value === bigint0;
/**
 * Checks whether a given `BigDecimal` is negative.
 *
 * **When to use**
 *
 * Use to test whether a `BigDecimal` is less than zero.
 *
 * **Example** (Checking negative decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.isNegative(BigDecimal.fromStringUnsafe("-1")), true)
 * assert.deepStrictEqual(BigDecimal.isNegative(BigDecimal.fromStringUnsafe("0")), false)
 * assert.deepStrictEqual(BigDecimal.isNegative(BigDecimal.fromStringUnsafe("1")), false)
 * ```
 *
 * @category predicates
 * @since 2.0.0
 */
export const isNegative = n => n.value < bigint0;
/**
 * Checks whether a given `BigDecimal` is positive.
 *
 * **When to use**
 *
 * Use to test whether a `BigDecimal` is greater than zero.
 *
 * **Example** (Checking positive decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(BigDecimal.isPositive(BigDecimal.fromStringUnsafe("-1")), false)
 * assert.deepStrictEqual(BigDecimal.isPositive(BigDecimal.fromStringUnsafe("0")), false)
 * assert.deepStrictEqual(BigDecimal.isPositive(BigDecimal.fromStringUnsafe("1")), true)
 * ```
 *
 * @category predicates
 * @since 2.0.0
 */
export const isPositive = n => n.value > bigint0;
const isBigDecimalArgs = args => isBigDecimal(args[0]);
/**
 * Computes a rounded `BigDecimal` at the given scale with the specified rounding mode.
 *
 * **When to use**
 *
 * Use to round a decimal at a requested scale with an explicit rounding mode.
 *
 * **Example** (Rounding decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.round(BigDecimal.fromStringUnsafe("145"), { mode: "from-zero", scale: -1 }),
 *   BigDecimal.fromStringUnsafe("150")
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.round(BigDecimal.fromStringUnsafe("-14.5")),
 *   BigDecimal.fromStringUnsafe("-15")
 * )
 * ```
 *
 * @see {@link ceil} for fixed rounding toward positive infinity
 * @see {@link floor} for fixed rounding toward negative infinity
 * @see {@link truncate} for fixed rounding toward zero
 *
 * @category math
 * @since 3.16.0
 */
export const round = /*#__PURE__*/dual(isBigDecimalArgs, (self, options) => {
  const mode = options?.mode ?? "half-from-zero";
  const scale = options?.scale ?? 0;
  switch (mode) {
    case "ceil":
      return ceil(self, scale);
    case "floor":
      return floor(self, scale);
    case "to-zero":
      return truncate(self, scale);
    case "from-zero":
      return isPositive(self) ? ceil(self, scale) : floor(self, scale);
    case "half-ceil":
      return floor(sum(self, make(bigint5, scale + 1)), scale);
    case "half-floor":
      return ceil(sum(self, make(bigint_5, scale + 1)), scale);
    case "half-to-zero":
      return isNegative(self) ? floor(sum(self, make(bigint5, scale + 1)), scale) : ceil(sum(self, make(bigint_5, scale + 1)), scale);
    case "half-from-zero":
      return isNegative(self) ? ceil(sum(self, make(bigint_5, scale + 1)), scale) : floor(sum(self, make(bigint5, scale + 1)), scale);
  }
  const halfCeil = floor(sum(self, make(bigint5, scale + 1)), scale);
  const halfFloor = ceil(sum(self, make(bigint_5, scale + 1)), scale);
  const digit = digitAt(halfCeil, scale);
  switch (mode) {
    case "half-even":
      return equals(halfCeil, halfFloor) ? halfCeil : digit % bigint2 === bigint0 ? halfCeil : halfFloor;
    case "half-odd":
      return equals(halfCeil, halfFloor) ? halfCeil : digit % bigint2 === bigint0 ? halfFloor : halfCeil;
  }
});
/**
 * Computes a truncated `BigDecimal` at the given scale. This removes fractional digits beyond the scale,
 * rounding toward zero.
 *
 * **When to use**
 *
 * Use to remove digits beyond a requested scale by rounding toward zero.
 *
 * **Example** (Truncating decimals)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 *
 * console.log(BigDecimal.truncate(BigDecimal.fromStringUnsafe("145"), -1)) // BigDecimal(140)
 * console.log(BigDecimal.truncate(BigDecimal.fromStringUnsafe("-14.5"))) // BigDecimal(-14)
 * ```
 *
 * @see {@link round} for configurable rounding modes
 * @see {@link ceil} for rounding toward positive infinity
 * @see {@link floor} for rounding toward negative infinity
 *
 * @category math
 * @since 3.16.0
 */
export const truncate = /*#__PURE__*/dual(isBigDecimalArgs, (self, scale = 0) => {
  if (self.scale <= scale) {
    return self;
  }
  // BigInt division truncates towards zero
  return make(self.value / bigint10 ** BigInt(self.scale - scale), scale);
});
/**
 * Computes the ceiling of a `BigDecimal` at the given scale.
 *
 * **When to use**
 *
 * Use to round a decimal toward positive infinity at a requested scale.
 *
 * **Details**
 *
 * The default scale is `0`. Positive scales keep digits to the right of the
 * decimal point, and negative scales round positions to the left of the decimal
 * point.
 *
 * @see {@link floor} for rounding toward negative infinity
 * @see {@link truncate} for rounding toward zero
 * @see {@link round} for configurable rounding modes
 *
 * **Example** (Rounding decimals up)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.ceil(BigDecimal.fromStringUnsafe("145"), -1),
 *   BigDecimal.fromStringUnsafe("150")
 * )
 * assert.deepStrictEqual(BigDecimal.ceil(BigDecimal.fromStringUnsafe("-14.5")), BigDecimal.fromStringUnsafe("-14"))
 * ```
 *
 * @category math
 * @since 3.16.0
 */
export const ceil = /*#__PURE__*/dual(isBigDecimalArgs, (self, scale = 0) => {
  const truncated = truncate(self, scale);
  if (isPositive(self) && isLessThan(truncated, self)) {
    return sum(truncated, make(bigint1, scale));
  }
  return truncated;
});
/**
 * Internal function used by `round` for `half-even` and `half-odd` rounding modes.
 *
 * Returns the digit at the position of the given `scale` within the `BigDecimal`.
 *
 * @internal
 */
export const digitAt = /*#__PURE__*/dual(2, (self, scale) => {
  if (self.scale < scale) {
    return bigint0;
  }
  const scaled = self.value / bigint10 ** BigInt(self.scale - scale);
  return scaled % bigint10;
});
/**
 * Computes the floor of a `BigDecimal` at the given scale.
 *
 * **When to use**
 *
 * Use to round a decimal toward negative infinity at a requested scale.
 *
 * **Example** (Rounding decimals down)
 *
 * ```ts
 * import { BigDecimal } from "effect"
 * import * as assert from "node:assert"
 *
 * assert.deepStrictEqual(
 *   BigDecimal.floor(BigDecimal.fromStringUnsafe("145"), -1),
 *   BigDecimal.fromStringUnsafe("140")
 * )
 * assert.deepStrictEqual(
 *   BigDecimal.floor(BigDecimal.fromStringUnsafe("-14.5")),
 *   BigDecimal.fromStringUnsafe("-15")
 * )
 * ```
 *
 * @see {@link ceil} for rounding toward positive infinity
 * @see {@link truncate} for rounding toward zero
 * @see {@link round} for configurable rounding modes
 *
 * @category math
 * @since 3.16.0
 */
export const floor = /*#__PURE__*/dual(isBigDecimalArgs, (self, scale = 0) => {
  const truncated = truncate(self, scale);
  if (isNegative(self) && isGreaterThan(truncated, self)) {
    return sum(truncated, make(bigint_1, scale));
  }
  return truncated;
});
//# sourceMappingURL=BigDecimal.js.map