///|
pub fn mpf_pos(x : RawMpf, prec : Int, rnd : RoundMode) -> RawMpf {
  if prec <= 0 || !is_finite(x) {
    x
  } else {
    normalize(x.sign, x.man, x.exp, x.bc, prec, rnd)
  }
}

///|
pub fn mpf_neg(x : RawMpf, prec : Int, rnd : RoundMode) -> RawMpf {
  if is_nan(x) {
    fnan
  } else if is_inf(x) {
    if x.sign == 1 {
      finf
    } else {
      fninf
    }
  } else if is_zero(x) {
    x
  } else {
    let sign = 1 - x.sign
    if prec <= 0 {
      { sign, man: x.man, exp: x.exp, bc: x.bc }
    } else {
      normalize(sign, x.man, x.exp, x.bc, prec, rnd)
    }
  }
}

///|
pub fn mpf_abs(x : RawMpf, prec : Int, rnd : RoundMode) -> RawMpf {
  if is_nan(x) {
    fnan
  } else if is_inf(x) {
    finf
  } else if is_zero(x) {
    x
  } else if x.sign == 0 {
    mpf_pos(x, prec, rnd)
  } else if prec <= 0 {
    { sign: 0, man: x.man, exp: x.exp, bc: x.bc }
  } else {
    normalize(0, x.man, x.exp, x.bc, prec, rnd)
  }
}

///|
pub fn mpf_sign(x : RawMpf) -> Int {
  if is_nan(x) || is_zero(x) {
    0
  } else if x.sign == 1 {
    -1
  } else {
    1
  }
}

///|
pub fn mpf_eq(x : RawMpf, y : RawMpf) -> Bool {
  if is_nan(x) || is_nan(y) {
    false
  } else {
    x == y
  }
}

///|
pub fn mpf_cmp(x : RawMpf, y : RawMpf) -> Int {
  if x == y {
    return 0
  }
  if is_nan(y) {
    return 1
  }
  if is_nan(x) {
    return -1
  }
  if is_inf(x) {
    return if x.sign == 1 { -1 } else { 1 }
  }
  if is_inf(y) {
    return if y.sign == 1 { 1 } else { -1 }
  }
  if is_zero(x) {
    return -mpf_sign(y)
  }
  if is_zero(y) {
    return mpf_sign(x)
  }
  if x.sign != y.sign {
    return if x.sign == 1 { -1 } else { 1 }
  }
  let sign_factor = if x.sign == 1 { -1 } else { 1 }
  let x_top = x.bc + x.exp
  let y_top = y.bc + y.exp
  if x_top > y_top {
    return sign_factor
  }
  if x_top < y_top {
    return -sign_factor
  }
  if x.exp == y.exp {
    if x.man > y.man {
      sign_factor
    } else {
      -sign_factor
    }
  } else if x.exp > y.exp {
    let lhs = x.man << (x.exp - y.exp)
    if lhs > y.man {
      sign_factor
    } else {
      -sign_factor
    }
  } else {
    let rhs = y.man << (y.exp - x.exp)
    if x.man > rhs {
      sign_factor
    } else {
      -sign_factor
    }
  }
}

///|
pub fn mpf_lt(x : RawMpf, y : RawMpf) -> Bool {
  if is_nan(x) || is_nan(y) {
    false
  } else {
    mpf_cmp(x, y) < 0
  }
}

///|
pub fn mpf_le(x : RawMpf, y : RawMpf) -> Bool {
  if is_nan(x) || is_nan(y) {
    false
  } else {
    mpf_cmp(x, y) <= 0
  }
}

///|
pub fn mpf_gt(x : RawMpf, y : RawMpf) -> Bool {
  if is_nan(x) || is_nan(y) {
    false
  } else {
    mpf_cmp(x, y) > 0
  }
}

///|
pub fn mpf_ge(x : RawMpf, y : RawMpf) -> Bool {
  if is_nan(x) || is_nan(y) {
    false
  } else {
    mpf_cmp(x, y) >= 0
  }
}

///|
pub fn mpf_shift(x : RawMpf, n : Int) -> RawMpf {
  if is_zero(x) || !is_finite(x) {
    x
  } else {
    { sign: x.sign, man: x.man, exp: x.exp + n, bc: x.bc }
  }
}

///|
pub fn mpf_mul_int(x : RawMpf, n : Int, prec : Int, rnd : RoundMode) -> RawMpf {
  if n == 0 || !is_finite(x) || is_zero(x) {
    return mpf_mul(x, from_int(n), prec, rnd)
  }
  let mut sign = x.sign
  let mut abs_n = n
  if n < 0 {
    sign = 1 - sign
    abs_n = -n
  }
  let man = x.man * BigInt::from_int(abs_n)
  normalize(sign, man, x.exp, man.bit_length(), prec, rnd)
}

///|
pub fn mpf_rdiv_int(
  n : Int,
  x : RawMpf,
  prec : Int,
  rnd : RoundMode,
) -> RawMpf raise MpfError {
  if n == 0 || is_zero(x) || !is_finite(x) {
    return mpf_div(from_int(n), x, prec, rnd)
  }
  let mut sign = x.sign
  let mut abs_n = n
  if n < 0 {
    sign = 1 - sign
    abs_n = -n
  }
  let target_prec = if prec > 0 { prec } else { 1 }
  let mut extra = target_prec + x.bc + 5
  let numerator = BigInt::from_int(abs_n) << extra
  let q = numerator / x.man
  let r = numerator % x.man
  let mut man = q
  if r != 0N {
    man = (man << 1) + 1N
    extra += 1
  }
  let exp = -x.exp - extra
  normalize(sign, man, exp, man.bit_length(), target_prec, rnd)
}

///|
fn mpf_round_int_inner(x : RawMpf, mode : RoundMode) -> RawMpf {
  if !is_finite(x) || is_zero(x) || x.exp >= 0 {
    return x
  }
  let shift = -x.exp
  let int_part = x.man >> shift
  let rem_mask = (1N << shift) - 1N
  let rem = x.man & rem_mask
  let has_frac = rem != 0N
  let bump = if has_frac { 1N } else { 0N }
  let signed = match mode {
    Floor => if x.sign == 0 { int_part } else { -(int_part + bump) }
    Ceiling => if x.sign == 0 { int_part + bump } else { -int_part }
    Down => if x.sign == 0 { int_part } else { -int_part }
    Up => if x.sign == 0 { int_part + bump } else { -(int_part + bump) }
    Nearest => {
      let half = 1N << (shift - 1)
      let nearest = if rem > half || (rem == half && (int_part & 1N) == 1N) {
        int_part + 1N
      } else {
        int_part
      }
      if x.sign == 1 {
        -nearest
      } else {
        nearest
      }
    }
  }
  from_man_exp(signed, 0, 0, round_down)
}

///|
pub fn mpf_round_int(x : RawMpf, rnd : RoundMode) -> RawMpf {
  match rnd {
    Floor => mpf_round_int_inner(x, round_floor)
    Ceiling => mpf_round_int_inner(x, round_ceiling)
    Down => mpf_round_int_inner(x, round_down)
    Up => mpf_round_int_inner(x, round_up)
    Nearest => mpf_round_int_inner(x, round_nearest)
  }
}

///|
pub fn mpf_min_max(seq : ArrayView[RawMpf]) -> (RawMpf, RawMpf) raise MpfError {
  if seq.length() == 0 {
    raise ValueError("mpf_min_max: empty input")
  }
  let mut min_v = seq[0]
  let mut max_v = seq[0]
  for i in 1.. RawMpf {
  let v = mpf_round_int_inner(x, round_floor)
  if prec > 0 {
    mpf_pos(v, prec, rnd)
  } else {
    v
  }
}

///|
pub fn mpf_ceil(x : RawMpf, prec : Int, rnd : RoundMode) -> RawMpf {
  let v = mpf_round_int_inner(x, round_ceiling)
  if prec > 0 {
    mpf_pos(v, prec, rnd)
  } else {
    v
  }
}

///|
pub fn mpf_nint(x : RawMpf, prec : Int, rnd : RoundMode) -> RawMpf {
  let v = mpf_round_int_inner(x, round_nearest)
  if prec > 0 {
    mpf_pos(v, prec, rnd)
  } else {
    v
  }
}

///|
pub fn mpf_frac(x : RawMpf, prec : Int, rnd : RoundMode) -> RawMpf {
  mpf_sub(x, mpf_floor(x, 0, round_down), prec, rnd)
}