///|
/// RFC 4506 section 4.8 binary128, retained as two words on every backend.
/// This is a numeric wire type, not a Double approximation or a software arithmetic library.
pub(all) struct Quadruple {
  high : UInt64
  low : UInt64
} derive(Eq, Debug)

///|
pub(all) enum QuadrupleClass {
  Zero
  Subnormal
  Normal
  Infinite
  QuietNaN
  SignalingNaN
} derive(Eq, Debug)

///|
pub fn Quadruple::sign(self : Quadruple) -> Bool {
  self.high >> 63 != 0UL
}

///|
pub fn Quadruple::exponent(self : Quadruple) -> Int {
  ((self.high >> 48) & 0x7fffUL).to_int()
}

///|
pub fn Quadruple::fraction(self : Quadruple) -> (UInt64, UInt64) {
  (self.high & 0x0000ffffffffffffUL, self.low)
}

///|
pub fn Quadruple::classify(self : Quadruple) -> QuadrupleClass {
  let (hi, lo) = self.fraction()
  let empty = hi == 0UL && lo == 0UL
  match self.exponent() {
    0 => if empty { Zero } else { Subnormal }
    32767 =>
      if empty {
        Infinite
      } else if (hi & 0x0000800000000000UL) != 0UL {
        QuietNaN
      } else {
        SignalingNaN
      }
    _ => Normal
  }
}

///|
/// Exact widening from binary64. No arithmetic or conversion through decimal strings.
/// NaN sign, quiet/signaling bit and payload are widened without canonicalization.
pub fn Quadruple::from_double(value : Double) -> Quadruple {
  let bits = value.reinterpret_as_uint64()
  let sign = bits & 0x8000000000000000UL
  let exponent = ((bits >> 52) & 0x7ffUL).to_int()
  let fraction = bits & 0x000fffffffffffffUL
  if exponent == 0 {
    if fraction == 0UL {
      return { high: sign, low: 0UL, }
    }
    let mut p = 0
    let mut n = fraction
    while n > 1UL {
      n = n >> 1
      p += 1
    }
    let tail = fraction - (1UL << p)
    let shift = 112 - p
    let hi = if shift >= 64 {
      tail << (shift - 64)
    } else {
      tail >> (64 - shift)
    }
    let lo = if shift >= 64 { 0UL } else { tail << shift }
    return {
      high: sign | ((p - 1074 + 16383).to_uint64() << 48) | hi,
      low: lo,
    }
  }
  let e = if exponent == 2047 { 32767 } else { exponent + 15360 }
  { high: sign | (e.to_uint64() << 48) | (fraction >> 4), low: fraction << 60, }
}

///|
pub fn Quadruple::to_bytes(self : Quadruple) -> Array[Byte] {
  let out : Array[Byte] = []
  for word in [self.high, self.low] {
    for i = 7; i >= 0; i = i - 1 {
      out.push((word >> (i * 8)).to_byte())
    }
  }
  out
}

///|
pub fn Quadruple::to_hex(self : Quadruple) -> String {
  to_hex(self.to_bytes())
}

///|
pub fn Quadruple::from_hex(text : String) -> Quadruple raise XdrError {
  if text.length() != 32 {
    raise Invalid(0, "quadruple requires exactly 32 hexadecimal digits")
  }
  let r = Reader::new(from_hex(text))
  let q = r.read_quadruple()
  r.finish()
  q
}

///|
pub fn Writer::write_quadruple(
  self : Writer,
  value : Quadruple,
) -> Unit raise XdrError {
  self.write_raw(value.to_bytes())
}

///|
pub fn Reader::read_quadruple(self : Reader) -> Quadruple raise XdrError {
  if self.remaining() < 16 {
    raise Invalid(self.pos, "truncated quadruple")
  }
  { high: self.read_uhyper(), low: self.read_uhyper(), }
}

///|
pub extend Quadruple with Eq::{equal, not_equal}

///|
pub extend QuadrupleClass with Eq::{equal, not_equal}

///|
pub extend Quadruple with @debug.Debug::{to_repr}

///|
pub extend QuadrupleClass with @debug.Debug::{to_repr}