///|
/// Fixed-point quantity in milli-units: `1000` milli-units are one unit, so
/// `0.001` is the smallest step and no binary floating point is involved in any
/// calculation. `2` is written `Qty::int(2)`, `0.25` as `Qty::milli(250L)`.
pub(all) struct Qty {
  milli : Int64
} derive(Eq, @debug.Debug)

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

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

///|
/// Whole units, for example `Qty::int(3)` is `3`.
pub fn Qty::int(units : Int) -> Qty {
  { milli: units.to_int64() * 1000L, }
}

///|
/// Raw milli-units, for example `Qty::milli(250L)` is `0.25`.
pub fn Qty::milli(milli : Int64) -> Qty {
  { milli, }
}

///|
pub fn Qty::zero() -> Qty {
  { milli: 0L, }
}

///|
pub fn Qty::add(self : Qty, other : Qty) -> Qty {
  { milli: self.milli + other.milli, }
}

///|
pub fn Qty::sub(self : Qty, other : Qty) -> Qty {
  { milli: self.milli - other.milli, }
}

///|
pub fn Qty::is_zero(self : Qty) -> Bool {
  self.milli == 0L
}

///|
pub fn Qty::compare(self : Qty, other : Qty) -> Int {
  if self.milli < other.milli {
    -1
  } else if self.milli > other.milli {
    1
  } else {
    0
  }
}

///|
/// Render a quantity in decimal notation, trimming trailing zeros, so `1500`
/// milli becomes `1.5` and `2000` becomes `2`.
pub fn Qty::to_string(self : Qty) -> String {
  let value = self.milli
  let sign = if value < 0L { "-" } else { "" }
  let abs = if value < 0L { -value } else { value }
  let whole = abs / 1000L
  let fraction = abs % 1000L
  if fraction == 0L {
    sign + whole.to_string()
  } else {
    sign + whole.to_string() + "." + fraction_text(fraction)
  }
}

///|
fn fraction_text(fraction : Int64) -> String {
  let hundreds = fraction / 100L
  let tens = fraction % 100L / 10L
  let ones = fraction % 10L
  if ones != 0L {
    hundreds.to_string() + tens.to_string() + ones.to_string()
  } else if tens != 0L {
    hundreds.to_string() + tens.to_string()
  } else {
    hundreds.to_string()
  }
}