///|
/// Non-negative fixed-point decimal with a maximum scale of 2.
pub struct Decimal {
  minor_units : Int
  scale : Int
}

///|
fn is_digit(c : Char) -> Bool {
  c >= '0' && c <= '9'
}

///|
pub fn Decimal::zero() -> Decimal {
  { minor_units: 0, scale: 2, }
}

///|
pub fn Decimal::parse(value : String) -> Decimal? {
  if value == "" || value[0] == '.' || value[value.length() - 1] == '.' {
    return None
  }
  let mut units = 0
  let mut dot_count = 0
  let mut digit_count = 0
  let mut fraction_digits = 0
  let mut seen_dot = false
  for c in value {
    if is_digit(c) {
      digit_count += 1
      units = units * 10 + (c.to_int() - '0'.to_int())
      if seen_dot {
        fraction_digits += 1
      }
    } else if c == '.' {
      dot_count += 1
      seen_dot = true
    } else {
      return None
    }
  }
  if digit_count == 0 || dot_count > 1 || fraction_digits > 2 {
    return None
  }
  if fraction_digits == 0 {
    units = units * 100
  } else if fraction_digits == 1 {
    units = units * 10
  }
  Some({ minor_units: units, scale: 2, })
}

///|
pub fn Decimal::is_positive(self : Decimal) -> Bool {
  self.minor_units > 0
}

///|
pub fn Decimal::add(self : Decimal, other : Decimal) -> Decimal {
  { minor_units: self.minor_units + other.minor_units, scale: 2, }
}

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

///|
pub fn Decimal::to_string(self : Decimal) -> String {
  let whole = self.minor_units / 100
  let fraction = self.minor_units % 100
  let fraction_text = if fraction < 10 {
    "0" + fraction.to_string()
  } else {
    fraction.to_string()
  }
  whole.to_string() + "." + fraction_text
}