extern crate num_traits;
extern crate num_bigint;
use std::str::FromStr;
use num_traits::cast::ToPrimitive;
use num_traits::identities::Zero;
use num_traits::identities::One;
use num_bigint::BigUint;
use crate::common::{
is_all_digits,
num_from_slice,
latin_prefix,
myriad_number
};
use crate::ParseError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scale {
Short,
LongBritish,
LongPeletier,
}
fn zillion_prefix(num: usize) -> Result<String, ParseError> {
let mut name = latin_prefix(num)?;
name.push_str("illi");
Ok(name)
}
fn zillion_number(num: usize, scale: Scale) -> Result<String, ParseError> {
if num == 0 { return Ok(String::from("")); }
if num == 1 { return Ok(String::from("thousand")); }
let (prefix, suffix) = match (scale, num % 2) {
(Scale::LongBritish, 1) => ("thousand ", "on"),
(Scale::LongPeletier, 1) => ("", "ard"),
(_, _) => ("", "on"),
};
let mut power = match scale {
Scale::Short => num - 1,
_ => ((num + 2) / 2) - 1,
};
let mut name = String::from(prefix);
let mut zillions = Vec::with_capacity(7);
while power > 0 {
let zillion = zillion_prefix(power % 1000)?;
zillions.push(zillion);
power /= 1000;
}
for z in zillions.iter().rev() {
name.push_str(z.as_str());
}
name.push_str(suffix);
Ok(name)
}
pub fn full_name(digits: &str, scale: Scale) -> Result<String, ParseError> {
let first_nonzero = is_all_digits(digits)
.then(|| digits)
.ok_or(ParseError::InvalidDigit)
.and_then(|d|
if d.is_empty() { Err(ParseError::Empty) }
else { Ok(d.find(|c| c != '0')) }
)?;
let (mut i, mut output) = first_nonzero.map_or_else(
|| (0, String::from("zero")),
|idx| (idx, String::from(""))
);
if !output.is_empty() { return Ok(output); }
let mut remaining = digits.len() - i;
let first = remaining % 3;
if first > 0 {
let num = num_from_slice(digits, i, first);
let leading = myriad_number(num)?;
let zillion = zillion_number(remaining / 3, scale)?;
output.push_str(leading.as_str());
if !zillion.is_empty() {
output.push(' ');
output.push_str(zillion.as_str());
}
remaining -= first;
i += first;
}
while remaining > 0 {
let num = num_from_slice(digits, i, 3);
let leading = myriad_number(num)?;
let zillion = zillion_number(remaining / 3 - 1, scale)?;
if !leading.is_empty() {
if !output.is_empty() { output.push(' '); }
output.push_str(leading.as_str());
if !zillion.is_empty() {
output.push(' ');
output.push_str(zillion.as_str());
}
}
i += 3;
remaining -= 3;
}
Ok(output)
}
pub fn power_of_ten(digits: &str, scale: Scale) -> Result<String, ParseError> {
let mut power = is_all_digits(digits)
.then(|| digits)
.ok_or(ParseError::InvalidDigit)
.and_then(|d|
if d.is_empty() { Err(ParseError::Empty) }
else { Ok(d) }
)
.and_then(|d| BigUint::from_str(d).map_err(|_| ParseError::InternalError))?;
let s = (&power % 3u32)
.to_u32()
.map(|m| match m {
0 => "one",
1 => "ten",
2 => "one hundred",
_ => ""
})
.unwrap_or("");
let mut output = String::from(s);
power /= 3u32;
if power.is_zero() { return Ok(output); }
if power.is_one() {
output.push_str(" thousand");
return Ok(output);
}
power -= 1u32;
output.push_str(" ");
let (prefix, suffix) = match (scale, (&power % 2u32).is_zero()) {
(Scale::Short, _) | (_, false) => ("", "on"),
(Scale::LongBritish, true) => ("thousand ", "on"),
(Scale::LongPeletier, true) => ("", "ard"),
};
if scale != Scale::Short {
power += 3u32;
power /= 2u32;
power -= 1u32;
}
output.push_str(prefix);
let mut zillions = Vec::new();
while !power.is_zero() {
let zillion = (&power % 1000u32)
.to_usize()
.ok_or(ParseError::InternalError)
.and_then(zillion_prefix)?;
zillions.push(zillion);
power /= 1000u32;
}
for z in zillions.iter().rev() {
output.push_str(z.as_str());
}
output.push_str(suffix);
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn very_small_numbers() -> Result<(), ParseError> {
let zero_ss = full_name("0", Scale::Short)?;
let zero_lp = full_name("0", Scale::LongPeletier)?;
assert_eq!("zero", zero_ss.as_str());
assert_eq!("zero", zero_lp.as_str());
Ok(())
}
#[test]
fn small_numbers() -> Result<(), ParseError> {
let twelve_ss = full_name("12", Scale::Short)?;
let twelve_lb = full_name("12", Scale::LongBritish)?;
assert_eq!("twelve", twelve_ss.as_str());
assert_eq!("twelve", twelve_lb.as_str());
Ok(())
}
#[test]
fn large_numbers() -> Result<(), ParseError> {
let billion = full_name("1000000000", Scale::Short)?;
let milliard = full_name("1000000000", Scale::LongPeletier)?;
let thousand_million = full_name("1000000000", Scale::LongBritish)?;
assert_eq!("one billion", billion.as_str());
assert_eq!("one milliard", milliard.as_str());
assert_eq!("one thousand million", thousand_million.as_str());
Ok(())
}
#[test]
fn large_powers() -> Result<(), ParseError> {
let googol_ss = power_of_ten("100", Scale::Short)?;
let googol_lb = power_of_ten("100", Scale::LongBritish)?;
let googol_lp = power_of_ten("100", Scale::LongPeletier)?;
assert_eq!("ten duotrigintillion", googol_ss.as_str());
assert_eq!("ten thousand sedecillion", googol_lb.as_str());
assert_eq!("ten sedecilliard", googol_lp.as_str());
Ok(())
}
}