///|
pub struct ArithmeticDiagnostics {
  inexact : Bool
  rounded : Bool
  overflow : Bool
  underflow : Bool
  subnormal : Bool
  clamped : Bool
} derive(Eq, Debug)

///|
pub fn ArithmeticDiagnostics::empty() -> ArithmeticDiagnostics {
  {
    inexact: false,
    rounded: false,
    overflow: false,
    underflow: false,
    subnormal: false,
    clamped: false,
  }
}

///|
pub fn ArithmeticDiagnostics::new(
  inexact? : Bool = false,
  rounded? : Bool = false,
  overflow? : Bool = false,
  underflow? : Bool = false,
  subnormal? : Bool = false,
  clamped? : Bool = false,
) -> ArithmeticDiagnostics {
  { inexact, rounded, overflow, underflow, subnormal, clamped }
}

///|
pub fn ArithmeticDiagnostics::combine(
  self : ArithmeticDiagnostics,
  other : ArithmeticDiagnostics,
) -> ArithmeticDiagnostics {
  {
    inexact: self.inexact || other.inexact,
    rounded: self.rounded || other.rounded,
    overflow: self.overflow || other.overflow,
    underflow: self.underflow || other.underflow,
    subnormal: self.subnormal || other.subnormal,
    clamped: self.clamped || other.clamped,
  }
}

///|
pub struct ArithmeticOutcome[T] {
  value : T
  diagnostics : ArithmeticDiagnostics
} derive(Eq, Debug)

///|
pub fn[T] ArithmeticOutcome::exact(value : T) -> ArithmeticOutcome[T] {
  { value, diagnostics: ArithmeticDiagnostics::empty() }
}

///|
pub fn[T] ArithmeticOutcome::with_diagnostics(
  value : T,
  diagnostics : ArithmeticDiagnostics,
) -> ArithmeticOutcome[T] {
  { value, diagnostics }
}

///|
pub(open) trait AddContextual {
  fn add_contextual(Self, Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait SubContextual {
  fn sub_contextual(Self, Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait MulContextual {
  fn mul_contextual(Self, Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait DivContextual {
  fn div_contextual(Self, Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait AbsContextual {
  fn abs_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait SqrtContextual {
  fn sqrt_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait ExpContextual {
  fn exp_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

/// MoonBit `Int` embedding under an explicit arithmetic context.
///
/// Implementations report successful rounding or range conditions through the
/// returned diagnostics. A backend may reject an unsupported context with a
/// structured arithmetic error. Wider or arbitrary-precision integer sources
/// require separate capabilities rather than changing this trait's source type.

///|
pub(open) trait IntegralContextual {
  fn from_int_contextual(Int, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

/// Adjacent representable values under an explicit arithmetic context.
///
/// `next_plus_contextual` and `next_minus_contextual` move by one representable
/// value in the corresponding direction. `next_toward_contextual` selects the
/// direction from its second operand. Equal values do not step; implementations
/// preserve the target sign when both operands are zero.
///
/// Fixed-format backends may treat selecting an adjacent value as exact and
/// return empty diagnostics. Backends whose representable set depends on the
/// context report any rounding or range conditions through the outcome.

///|
pub(open) trait AdjacentContextual {
  fn next_plus_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
  fn next_minus_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
  fn next_toward_contextual(Self, Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

/// Context-dependent mathematical constants.
///
/// Implementations should expose this capability only when they honor the
/// supplied context and can report meaningful diagnostics. Proof-backed
/// implementations may return an `ArithmeticError` whose kind is
/// `ArithmeticErrorKind::CertificationFailure` when target rounding cannot be
/// certified.

///|
pub(open) trait ConstantsContextual {
  fn pi_contextual(ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
  fn tau_contextual(ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
  fn e_contextual(ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

/// Context-dependent hyperbolic functions.
///
/// Implementations should expose this capability only when they honor the
/// supplied context and can report meaningful diagnostics or structured
/// failures.

///|
pub(open) trait HyperbolicContextual {
  fn sinh_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
  fn cosh_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
  fn tanh_contextual(Self, ArithmeticContext) -> Result[
    ArithmeticOutcome[Self],
    ArithmeticError,
  ]
}

///|
pub(open) trait NumericFormatContextual {
  fn zero_contextual(ArithmeticContext) -> Self
  fn one_contextual(ArithmeticContext) -> Self
  fn epsilon_contextual(ArithmeticContext) -> Self
  fn min_normal_contextual(ArithmeticContext) -> Self
  fn max_finite_contextual(ArithmeticContext) -> Self
  fn classify_contextual(Self) -> FpClass
}