extern crate num_traits;
extern crate num_bigint;
use std::str::FromStr;
use num_traits::cast::ToPrimitive;
use num_traits::identities::Zero;
use num_bigint::BigUint;
use crate::common::{
is_all_digits,
num_from_slice,
latin_prefix,
myriad_number
};
use crate::ParseError;
fn zyllion_number(num: usize) -> Result<(String, usize), ParseError> {
if num == 0 { return Ok((String::from(""), 0)); }
if num % 2 == 1 { return Ok((String::from("myriad"), 1)); }
let mut name = String::from("");
let greatest_power_of_two = num.trailing_zeros() as usize;
let prefix = latin_prefix(greatest_power_of_two)?;
name.push_str(prefix.as_str());
name.push_str("yllion");
Ok((name, greatest_power_of_two + 1))
}
pub fn full_name(digits: &str) -> 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 last_largest : usize = 0;
let mut remaining = digits.len() - i;
let first = remaining % 4;
if first > 0 {
let num = num_from_slice(digits, i, first);
let leading = myriad_number(num)?;
let (zyllion, largest) = zyllion_number(remaining / 4)?;
output.push_str(leading.as_str());
if !zyllion.is_empty() {
output.push(' ');
output.push_str(zyllion.as_str());
last_largest = largest;
}
remaining -= first;
i += first;
}
while remaining > 0 {
let num = num_from_slice(digits, i, 4);
let leading = myriad_number(num)?;
let (zyllion, largest) = zyllion_number((remaining - 1) / 4)?;
if !leading.is_empty() {
if !output.is_empty() { output.push(' '); }
output.push_str(leading.as_str());
if !zyllion.is_empty() {
output.push(' ');
output.push_str(zyllion.as_str());
last_largest = largest;
}
}
if largest > last_largest {
output.push(' ');
output.push_str(zyllion.as_str());
last_largest = largest;
}
i += 4;
remaining -= 4;
}
Ok(output)
}
fn latin_yllion(n: usize) -> String {
let nstr = n.to_string();
full_name(nstr.as_str())
.unwrap_or_default()
.split_whitespace()
.fold(String::from("latin"), |mut s, w| {
s.push_str(w);
s
})
}
pub fn power_of_ten(digits: &str) -> 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 % 2u32)
.to_u32()
.map(|m| match m { 0 => "one", 1 => "ten", _ => "" })
.unwrap_or("");
let mut output = String::from(s);
power /= 2u32;
let m = (&power % 2u32).to_u32();
if m == Some(1) { output.push_str(" hundred"); }
power /= 2u32;
let m = (&power % 2u32).to_u32();
if m == Some(1) { output.push_str(" myriad"); }
let mut zyl_num = 1;
while !power.is_zero() {
power /= 2u32;
let m = (&power % 2u32).to_u32();
if m == Some(1) {
let prefix = if zyl_num > 999 { latin_yllion(zyl_num) }
else { latin_prefix(zyl_num)? };
output.push(' ');
output.push_str(prefix.as_str());
output.push_str("yllion");
}
zyl_num += 1;
}
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_numbers() -> Result<(), ParseError> {
let forty_two_hundred = full_name("4200")?;
assert_eq!("forty two hundred", forty_two_hundred.as_str());
Ok(())
}
#[test]
fn very_large_numbers() -> Result<(), ParseError> {
let knuth_example = "\
8065817517094387\
8571660636856403\
7669752895054408\
83277824000000000000";
let knuth_expected = "\
eighty hundred sixty five quadryllion \
eighty one hundred seventy five myriad \
seventeen hundred nine myllion \
forty three hundred eighty seven myriad \
eighty five hundred seventy one byllion \
sixty six hundred six myriad \
thirty six hundred eighty five myllion \
sixty four hundred three myriad \
seventy six hundred sixty nine tryllion \
seventy five hundred twenty eight myriad \
ninety five hundred five myllion \
forty four hundred eight myriad \
eighty three hundred twenty seven byllion \
seventy eight hundred twenty four myriad myllion";
let example_result = full_name(knuth_example)?;
assert_eq!(knuth_expected, example_result.as_str());
Ok(())
}
#[test]
fn small_power() -> Result<(), ParseError> {
let ten_hundred_myriad = power_of_ten("7")?;
assert_eq!("ten hundred myriad", ten_hundred_myriad.as_str());
Ok(())
}
#[test]
fn semi_large_power() -> Result<(), ParseError> {
let ten_to_the_forty_second = power_of_ten("42")?;
assert_eq!(
"one hundred myllion tryllion",
ten_to_the_forty_second.as_str()
);
Ok(())
}
#[test]
fn very_large_power() -> Result<(), ParseError> {
let latin_ten_hundred_yllion = power_of_ten(
"42860344287450692837937001962400072422456192468221344\
297750015534814042044997444899727935152627834325103786\
916702125873007485811427692561743938310298794299215738\
271099296923941684298420249484567511816728612185899934\
327765069595070236662175784308251658284785910746168670\
641719326610497547348822672277504"
)?;
assert_eq!(
"one latintenhundredyllion",
latin_ten_hundred_yllion
);
Ok(())
}
}