///|
/// Exact JSON numeric identity and wire spelling, including fractional tokens.
/// This is not an arithmetic API; conversion to Double is explicitly lossy.
pub struct JsonNumber {
  wire : Json
  canonical : String
} derive(Debug)

///|
pub fn JsonNumber::from_json(value : Json) -> JsonNumber raise MCPError {
  guard value is Number(number, repr~) else {
    raise InvalidParams("Expected a JSON number")
  }
  let literal = match repr {
    Some(literal) => literal
    None => {
      guard !number.is_nan() && !number.is_inf() else {
        raise InvalidParams("Non-finite number has no JSON literal")
      }
      value.stringify()
    }
  }
  guard integer_key(literal, integer_only=false) is Some(canonical) else {
    raise InvalidParams("Invalid JSON number literal")
  }
  let decoded = @json.parse(literal) catch {
    _ => raise InvalidParams("Invalid JSON number representation")
  }
  guard decoded is Number(expected, ..) &&
    !number.is_nan() &&
    expected == number else {
    raise InvalidParams(
      "JSON number representation contradicts its numeric value",
    )
  }
  { wire: Json::number(number, repr=literal), canonical, }
}

///|
pub fn JsonNumber::from_literal(literal : String) -> JsonNumber raise MCPError {
  let parsed = @json.parse(literal) catch {
    _ => raise InvalidParams("Invalid JSON number literal")
  }
  guard parsed is Number(number, ..) else {
    raise InvalidParams("Expected a JSON number literal")
  }
  JsonNumber::from_json(Json::number(number, repr=literal))
}

///|
/// Lossless for the Double itself: the shortest round-trip literal is kept,
/// so programmatic doubles serialize exactly. Digits already lost to binary64
/// cannot be recovered; non-finite doubles have no JSON literal.
pub fn JsonNumber::from_double(value : Double) -> JsonNumber raise MCPError {
  JsonNumber::from_json(Json::number(value))
}

///|
pub fn JsonNumber::to_json(self : JsonNumber) -> Json {
  self.wire
}

///|
/// Explicitly lossy Double view for display or limit checks; the wire
/// identity and equality stay exact.
pub fn JsonNumber::as_double(self : JsonNumber) -> Double {
  match self.wire {
    Number(value, ..) => value
    _ => abort("JsonNumber always carries a JSON number")
  }
}

///|
pub fn JsonNumber::key(self : JsonNumber) -> String {
  self.canonical
}

///|
/// Exact mathematical ordering, including fractional spellings. Comparison
/// uses canonical identities, never rounded doubles, and does not expand
/// huge exponents.
pub fn JsonNumber::compare(self : JsonNumber, other : JsonNumber) -> Int32 {
  decimal_key_compare(self.canonical, other.canonical)
}

///|
/// Canonical key layout is `[sign]digits e exponent` without leading or
/// trailing zeros, so ordering needs only digit and exponent comparisons and
/// never expands huge exponents. `"0e0"` is the single zero spelling.
fn decimal_key_compare(left : String, right : String) -> Int32 {
  let left_zero = left == "0e0"
  let right_zero = right == "0e0"
  match (left_zero, right_zero) {
    (true, true) => 0
    (true, false) => if right[0] == '-' { 1 } else { -1 }
    (false, true) => if left[0] == '-' { -1 } else { 1 }
    (false, false) => {
      let (left_negative, left_digits, left_exponent) = decimal_key_parts(left)
      let (right_negative, right_digits, right_exponent) = decimal_key_parts(
        right,
      )
      if left_negative != right_negative {
        return if left_negative { -1 } else { 1 }
      }
      let magnitude = decimal_magnitude_compare(
        left_digits, left_exponent, right_digits, right_exponent,
      )
      if left_negative {
        -magnitude
      } else {
        magnitude
      }
    }
  }
}

///|
fn decimal_key_parts(key : String) -> (Bool, String, @bigint.BigInt) {
  let negative = key[0] == '-'
  let mut end = if negative { 1 } else { 0 }
  while key[end] != 'e' {
    end += 1
  }
  let digits = key[if negative { 1 } else { 0 }:end].to_owned()
  let exponent = @bigint.BigInt::from_string(key[end + 1:].to_owned())
  (negative, digits, exponent)
}

///|
fn decimal_magnitude_compare(
  left_digits : String,
  left_exponent : @bigint.BigInt,
  right_digits : String,
  right_exponent : @bigint.BigInt,
) -> Int32 {
  let left_shift = left_exponent +
    @bigint.BigInt::from_int(left_digits.length() - 1)
  let right_shift = right_exponent +
    @bigint.BigInt::from_int(right_digits.length() - 1)
  if left_shift != right_shift {
    return if left_shift < right_shift { -1 } else { 1 }
  }
  let shared = if left_digits.length() < right_digits.length() {
    left_digits.length()
  } else {
    right_digits.length()
  }
  for index in 0..