///|
/// Parse the canonical output of format_money, including financial digits.
pub fn parse_money(
  source : String,
  variants? : Bool = false,
) -> Int64 raise NumberError {
  if source.length() > 100 {
    raise Invalid("amount text too long")
  }
  let source = if variants {
    numeral_variants(source)
    .replace_all(old="圓", new="元")
    .replace_all(old="圆", new="元")
    .replace_all(old="正", new="整")
  } else {
    source
  }
  let negative = source.has_prefix("负")
  let s = if negative { source[1:].to_owned() } else { source }
  let parts = s.split("元").collect()
  if parts.length() != 2 {
    raise Invalid("expected yuan separator")
  }
  let whole = parse_integer(parts[0].to_owned())
  if whole < 0L || whole > 99999999999999L {
    raise Invalid("whole amount range")
  }
  let tail = parts[1].to_owned()
  let mut fraction = 0
  if tail != "整" {
    let cs = tail.to_array()
    let mut i = if cs.length() > 0 && cs[0] == '零' { 1 } else { 0 }
    let names = digits(true)
    while i < cs.length() {
      if i + 1 >= cs.length() {
        raise Invalid("fraction unit missing")
      }
      let mut value = -1
      for n in 1..<10 {
        if names[n] == cs[i].to_string() {
          value = n
        }
      }
      if value < 1 {
        raise Invalid("fraction digit")
      }
      if cs[i + 1] == '角' {
        fraction += value * 10
      } else if cs[i + 1] == '分' {
        fraction += value
      } else {
        raise Invalid("fraction unit")
      }
      i += 2
    }
  }
  let amount = (whole * 100L + fraction.to_int64()) *
    (if negative { -1L } else { 1L })
  if format_money(amount) != source {
    raise Invalid("noncanonical financial amount")
  }
  amount
}