///|
pub suberror NumberError {
Invalid(String)
} derive(Debug)
///|
fn digits(financial : Bool) -> Array[String] {
if financial {
["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"]
} else {
["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]
}
}
///|
fn group(n : Int, financial : Bool) -> String {
let names = digits(financial)
let units = if financial {
["", "拾", "佰", "仟"]
} else {
["", "十", "百", "千"]
}
let mut result = ""
let mut zero = false
let powers = [1000, 100, 10, 1]
for i in 0..<4 {
let d = n / powers[i] % 10
if d == 0 {
if result != "" {
zero = true
}
} else {
if zero {
result += "零"
zero = false
}
result += names[d] + units[3 - i]
}
}
result
}
///|
/// Range: -9,999,999,999,999,999 through +9,999,999,999,999,999.
pub fn format_integer(
n : Int64,
financial? : Bool = false,
) -> String raise NumberError {
if n < -9999999999999999L || n > 9999999999999999L {
raise Invalid("integer outside supported range")
}
if n == 0L {
return "零"
}
let value = if n < 0L { -n } else { n }
let powers = [1000000000000L, 100000000L, 10000L, 1L]
let units = ["兆", "亿", "万", ""]
let mut out = ""
let mut skipped = false
for i in 0..<4 {
let part = (value / powers[i] % 10000L).to_int()
if part == 0 {
if out != "" {
skipped = true
}
} else {
if out != "" && (skipped || part < 1000) {
out += "零"
}
out += group(part, financial) + units[i]
skipped = false
}
}
if !financial && out.has_prefix("一十") {
out = out[1:].to_owned()
}
(if n < 0L { "负" } else { "" }) + out
}
///|
fn normalize(s : String) -> String {
let mut out = ""
for c in s.iter() {
out += match c {
'壹' => "一"
'贰' => "二"
'叁' => "三"
'肆' => "四"
'伍' => "五"
'陆' => "六"
'柒' => "七"
'捌' => "八"
'玖' => "九"
'拾' => "十"
'佰' => "百"
'仟' => "千"
_ => c.to_string()
}
}
out
}
///|
/// Parses canonical output of format_integer (common or financial).
/// Ambiguous colloquialisms such as 一万二 and serial digits 一二三 are rejected.
pub fn parse_integer(
text : String,
variants? : Bool = false,
) -> Int64 raise NumberError {
if text == "" || text.length() > 128 {
raise Invalid("invalid length")
}
let s = normalize(if variants { numeral_variants(text) } else { text })
let neg = s.has_prefix("负")
let body = if neg { s[1:].to_owned() } else { s }
let mut total = 0L
let mut section = 0L
let mut digit = 0L
let mut previous_big = 10000000000000000L
for c in body.iter() {
let d = match c {
'零' => 0
'一' => 1
'二' => 2
'三' => 3
'四' => 4
'五' => 5
'六' => 6
'七' => 7
'八' => 8
'九' => 9
_ => -1
}
if d >= 0 {
digit = d.to_int64()
continue
}
let unit = match c {
'十' => 10L
'百' => 100L
'千' => 1000L
'万' => 10000L
'亿' => 100000000L
'兆' => 1000000000000L
_ => raise Invalid("unknown numeral")
}
if unit < 10000L {
section += (if digit == 0L { 1L } else { digit }) * unit
} else {
if unit >= previous_big {
raise Invalid("invalid group order")
}
previous_big = unit
if section + digit > 9999L {
raise Invalid("group overflow")
}
total += (section + digit) * unit
section = 0L
}
digit = 0L
}
let value = (total + section + digit) * (if neg { -1L } else { 1L })
let normal = normalize(format_integer(value))
let finance = normalize(format_integer(value, financial=true))
if s != normal && s != finance {
raise Invalid("noncanonical or ambiguous numeral")
}
value
}