use crate::base::{
clean, set_low_numwords, set_mid_numwords, splitnum, Cards, Lang, N2WError, Node, Result,
};
use crate::currency::{default_to_currency, CurrencyForms, CurrencyValue};
use crate::floatpath::FloatValue;
use num_bigint::BigInt;
use num_integer::Integer;
use num_traits::{One, Signed, ToPrimitive, Zero};
use std::collections::HashMap;
const GIGA_SUFFIX: &str = "iliard/e";
const MEGA_SUFFIX: &str = "ilion";
const MEGA_SUFFIX_I: &str = "ilioane";
const GIGA_SUFFIX_I: &str = "iliarde";
const GEN_NUMWORDS: [&str; 10] = [
"", "o", "două", "trei", "patru", "cinci", "șase", "șapte", "opt", "nouă",
];
const GEN_NUMWORDS_UNA: [&str; 10] = [
"", "una", "două", "trei", "patru", "cinci", "șase", "șapte", "opt", "nouă",
];
const GEN_NUMWORDS_N: [&str; 10] = [
"", "un", "două", "trei", "patru", "cinci", "șase", "șapte", "opt", "nouă",
];
fn build_currency_forms() -> HashMap<&'static str, CurrencyForms> {
const CENTS: [&str; 3] = ["cent", "cenţi", "de cenţi"];
let mut m: HashMap<&'static str, CurrencyForms> = HashMap::new();
m.insert(
"RON",
CurrencyForms::new(&["leu", "lei", "de lei"], &["ban", "bani", "de bani"]),
);
m.insert(
"EUR",
CurrencyForms::new(&["euro", "euro", "de euro"], &CENTS),
);
m.insert(
"USD",
CurrencyForms::new(&["dolar", "dolari", "de dolari"], &CENTS),
);
m
}
fn build_currency_adjectives() -> HashMap<&'static str, &'static str> {
[
("AUD", "Australian"),
("BYN", "Belarusian"),
("CAD", "Canadian"),
("EEK", "Estonian"),
("USD", "US"),
("RUB", "Russian"),
("NOK", "Norwegian"),
("MXN", "Mexican"),
("RON", "Romanian"),
("INR", "Indian"),
("HUF", "Hungarian"),
("ISK", "íslenskar"),
("UZS", "Uzbekistan"),
("SAR", "Saudi"),
("JPY", "Japanese"),
("KRW", "Korean"),
]
.into_iter()
.collect()
}
fn gen_high_numwords(units: &[&str], tens: &[&str], lows: &[&str]) -> Vec<String> {
let mut out: Vec<String> = Vec::new();
for t in tens {
for u in units {
out.push(format!("{}{}", u, t));
}
}
out.reverse();
const REPLACEMENTS: &[(&str, &str)] = &[
("novemn", "noven"),
("novemo", "novo"),
("octoo", "octo"),
("quintd", "quind"),
("quintn", "quin"),
("quintq", "quinq"),
("quints", "quins"),
("quintt", "quint"),
("quintv", "quinv"),
("septenn", "septen"),
("septent", "sept"),
("sexn", "sen"),
("sexs", "ses"),
("tresd", "tred"),
("tresn", "tren"),
("tress", "tres"),
("tresv", "trev"),
("unno", "uno"),
];
for (k, v) in REPLACEMENTS {
out = out.iter().map(|o| o.replace(k, v)).collect();
}
out.extend(lows.iter().map(|s| s.to_string()));
out
}
fn lookup_inflection(
table: &[(BigInt, &'static [&'static str; 10])],
key: &BigInt,
) -> Option<&'static [&'static str; 10]> {
table.iter().find(|(k, _)| k == key).map(|(_, v)| *v)
}
fn errmsg_toobig(v: &BigInt, maxval: &BigInt) -> N2WError {
N2WError::Overflow(format!(
"Numărul e prea mare pentru a fi convertit în cuvinte (abs({}) > {}).",
v, maxval
))
}
fn ro_to_cardinal<L: Lang + ?Sized>(lang: &L, value: &BigInt) -> Result<String> {
let mut out = String::new();
let mut v = value.clone();
if v.is_negative() {
v = v.abs();
out = format!("{} ", lang.negword().trim());
}
if &v >= lang.maxval() {
return Err(errmsg_toobig(&v, lang.maxval()));
}
let tree = splitnum(lang, &v).ok_or_else(|| errmsg_toobig(&v, lang.maxval()))?;
let words = match clean(lang, tree) {
Node::Leaf(t, _) => t,
Node::List(_) => return Err(N2WError::Type("clean did not reduce".into())),
};
Ok(lang.title(&format!("{}{}", out, words)))
}
pub struct LangRo {
cards: Cards,
maxval: BigInt,
numwords_inflections: Vec<(BigInt, &'static [&'static str; 10])>,
numwords_inflections_una: Vec<(BigInt, &'static [&'static str; 10])>,
exclude_title: Vec<String>,
currency_forms: HashMap<&'static str, CurrencyForms>,
currency_adjectives: HashMap<&'static str, &'static str>,
}
impl Default for LangRo {
fn default() -> Self {
Self::new()
}
}
impl LangRo {
pub fn new() -> Self {
let lows = [
"non", "oct", "sept", "sext", "quint", "quadr", "tr", "b", "m",
];
let units = [
"", "un", "duo", "tres", "quattuor", "quint", "sex", "septen", "octo", "novem",
];
let tens = [
"dec",
"vigint",
"trigint",
"quadragint",
"quinquagint",
"sexagint",
"septuagint",
"octogint",
"nonagint",
];
let mut high = vec!["cent".to_string()];
high.extend(gen_high_numwords(&units, &tens, &lows));
let mut cards = Cards::new();
let cap: i64 = 3 + 6 * high.len() as i64;
let mut n = cap;
for word in high.iter() {
if n <= 3 {
break;
}
cards.insert(
BigInt::from(10u8).pow(n as u32),
format!("{}{}", word, GIGA_SUFFIX),
);
cards.insert(
BigInt::from(10u8).pow((n - 3) as u32),
format!("{}{}", word, MEGA_SUFFIX),
);
n -= 6;
}
set_mid_numwords(
&mut cards,
&[
(1000, "mie/i"),
(100, "sută/e"),
(90, "nouăzeci"),
(80, "optzeci"),
(70, "șaptezeci"),
(60, "șaizeci"),
(50, "cincizeci"),
(40, "patruzeci"),
(30, "treizeci"),
],
);
set_low_numwords(
&mut cards,
&[
"douăzeci",
"nouăsprezece",
"optsprezece",
"șaptesprezece",
"șaisprezece",
"cincisprezece",
"paisprezece",
"treisprezece",
"doisprezece",
"unsprezece",
"zece",
"nouă",
"opt",
"șapte",
"șase",
"cinci",
"patru",
"trei",
"doi",
"unu",
"zero",
],
);
let maxval = cards.highest().cloned().unwrap_or_else(BigInt::zero) * BigInt::from(1000);
let numwords_inflections: Vec<(BigInt, &'static [&'static str; 10])> = vec![
(BigInt::from(100), &GEN_NUMWORDS),
(BigInt::from(1000), &GEN_NUMWORDS),
(BigInt::from(1_000_000i64), &GEN_NUMWORDS_N),
(BigInt::from(1_000_000_000i64), &GEN_NUMWORDS_N),
];
let numwords_inflections_una: Vec<(BigInt, &'static [&'static str; 10])> = vec![
(BigInt::from(100), &GEN_NUMWORDS_UNA),
(BigInt::from(1000), &GEN_NUMWORDS_UNA),
(BigInt::from(1_000_000i64), &GEN_NUMWORDS_N),
(BigInt::from(1_000_000_000i64), &GEN_NUMWORDS_N),
];
LangRo {
cards,
maxval,
numwords_inflections,
numwords_inflections_una,
exclude_title: vec!["și".into(), "virgulă".into(), "minus".into()],
currency_forms: build_currency_forms(),
currency_adjectives: build_currency_adjectives(),
}
}
fn plural_form(n: &BigInt) -> usize {
if n.is_one() {
0
} else if n.is_zero() {
1
} else {
let m = n.mod_floor(&BigInt::from(100));
if m > BigInt::zero() && m < BigInt::from(20) {
1
} else {
2
}
}
}
fn pluralize(n: &BigInt, forms: &[String; 3]) -> String {
forms[Self::plural_form(n)].clone()
}
fn merge_with(
&self,
l: (&str, &BigInt),
r: (&str, &BigInt),
inflections: &[(BigInt, &'static [&'static str; 10])],
) -> (String, BigInt) {
let (ltext, lnum) = l;
let (rtext, rnum) = r;
let one = BigInt::one();
let two = BigInt::from(2);
let ten = BigInt::from(10);
let hundred = BigInt::from(100);
if &one <= lnum && lnum < &ten {
match lookup_inflection(inflections, rnum) {
None => (rtext.to_string(), rnum.clone()),
Some(table) => {
let rtext_i = self.inflect(&(lnum * rnum), rtext, lnum);
let idx = lnum.to_usize().expect("1 <= lnum < 10");
(format!("{} {}", table[idx], rtext_i), rnum.clone())
}
}
} else if &ten < lnum && lnum < &hundred {
if lnum.mod_floor(&ten).is_zero() {
if lookup_inflection(inflections, rnum).is_some() {
let rtext_i = self.inflect(&(lnum * rnum), rtext, lnum);
(format!("{} {}", ltext, rtext_i), lnum * rnum)
} else {
(format!("{} și {}", ltext, rtext), lnum + rnum)
}
} else {
let rtext_i = self.inflect(&(lnum * rnum), rtext, lnum);
let ltext_i = if lnum.mod_floor(&ten) != two {
ltext.to_string()
} else {
ltext.replace("doi", "două")
};
(format!("{} {}", ltext_i, rtext_i), lnum * rnum)
}
} else {
let rtext_i = if lookup_inflection(inflections, rnum).is_some() {
self.inflect(&(lnum * rnum), rtext, lnum)
} else {
self.inflect(rnum, rtext, lnum)
};
(format!("{} {}", ltext, rtext_i), lnum * rnum)
}
}
fn inflect(&self, _value: &BigInt, text: &str, side_effect: &BigInt) -> String {
let parts: Vec<&str> = text.split('/').collect();
let mut result = parts[0].to_string();
if parts.len() > 1 {
let chars: Vec<char> = parts[0].chars().collect();
let stem: String = chars[..chars.len().saturating_sub(1)].iter().collect();
let plural = format!("{}{}", stem, parts[1]);
let forms = [
parts[0].to_string(),
plural.clone(),
format!("de {}", plural),
];
result = Self::pluralize(side_effect, &forms);
}
let one = BigInt::one();
if side_effect > &one && result.ends_with(MEGA_SUFFIX) {
let inflected = result.replace(MEGA_SUFFIX, MEGA_SUFFIX_I);
let forms = [
result.clone(),
inflected.clone(),
format!("de {}", inflected),
];
result = Self::pluralize(side_effect, &forms);
} else if side_effect > &one && result.ends_with("iliare") {
result = result.replace("iliare", GIGA_SUFFIX_I);
}
result
}
fn verify_ordinal(&self, value: &BigInt) -> Result<()> {
if value.is_negative() {
return Err(N2WError::Type(format!(
"Cannot treat negative num {} as ordinal.",
value
)));
}
Ok(())
}
}
impl Lang for LangRo {
fn default_currency(&self) -> &str {
"RON"
}
fn default_separator(&self) -> &str {
" și"
}
fn cards(&self) -> &Cards {
&self.cards
}
fn maxval(&self) -> &BigInt {
&self.maxval
}
fn negword(&self) -> &str {
"minus "
}
fn pointword(&self) -> &str {
"virgulă"
}
fn exclude_title(&self) -> &[String] {
&self.exclude_title
}
fn merge(&self, l: (&str, &BigInt), r: (&str, &BigInt)) -> (String, BigInt) {
self.merge_with(l, r, &self.numwords_inflections)
}
fn to_cardinal(&self, value: &BigInt) -> Result<String> {
ro_to_cardinal(self, value)
}
fn to_ordinal(&self, value: &BigInt) -> Result<String> {
self.verify_ordinal(value)?;
if value.is_one() {
return Ok("primul".to_string());
}
let cardinal = self.to_cardinal(value)?;
let cardinal = cardinal.replace("o sută", "una sută");
let cardinal = cardinal.replace("o mie", "una mie");
Ok(format!("al {}lea", cardinal))
}
fn to_ordinal_num(&self, value: &BigInt) -> Result<String> {
self.verify_ordinal(value)?;
if value.is_one() {
return Ok("1-ul".to_string());
}
Ok(format!("al {}-lea", value))
}
fn to_year(&self, value: &BigInt) -> Result<String> {
let mut result = self.to_cardinal(value)?;
let mut suffix: Option<&str> = None;
if result.starts_with(self.negword()) {
result = result.replace(self.negword(), "");
suffix = Some("î.Hr.");
}
if let Some(s) = suffix {
result = format!("{} {}", result, s);
}
Ok(result)
}
fn ordinal_float_entry(&self, value: &FloatValue) -> Result<String> {
match value.as_whole_int() {
Some(i) => self.to_ordinal(&i),
None => Err(N2WError::Type("Cannot treat float as ordinal.".into())),
}
}
fn ordinal_num_float_entry(&self, value: &FloatValue, repr_str: &str) -> Result<String> {
match value.as_whole_int() {
Some(i) => {
if i.is_negative() {
return Err(N2WError::Type(format!(
"Cannot treat negative num {} as ordinal.",
repr_str
)));
}
if i.is_one() {
return Ok("1-ul".to_string());
}
Ok(format!("al {}-lea", repr_str))
}
None => Err(N2WError::Type("Cannot treat float as ordinal.".into())),
}
}
fn year_float_entry(&self, value: &FloatValue) -> Result<String> {
let mut result = self.cardinal_float_entry(value, None)?;
let mut suffix: Option<&str> = None;
if result.starts_with(self.negword()) {
result = result.replace(self.negword(), "");
suffix = Some("î.Hr.");
}
if let Some(s) = suffix {
result = format!("{} {}", result, s);
}
Ok(result)
}
fn lang_name(&self) -> &str {
"Num2Word_RO"
}
fn currency_forms(&self, code: &str) -> Option<&CurrencyForms> {
self.currency_forms.get(code)
}
fn currency_adjective(&self, code: &str) -> Option<&str> {
self.currency_adjectives.get(code).copied()
}
fn pluralize(&self, n: &BigInt, forms: &[String]) -> Result<String> {
forms
.get(Self::plural_form(n))
.cloned()
.ok_or_else(|| N2WError::Index("tuple index out of range".into()))
}
fn to_currency(
&self,
val: &CurrencyValue,
currency: &str,
_cents: bool,
separator: Option<&str>,
adjective: bool,
) -> Result<String> {
let separator = separator.unwrap_or(self.default_separator());
let result = default_to_currency(&RoUna(self), val, currency, true, separator, adjective)?;
Ok(result
.replace("unu leu", "un leu")
.replace("unu ban", "un ban")
.replace(&format!("{} zero bani", separator), ""))
}
}
struct RoUna<'a>(&'a LangRo);
impl Lang for RoUna<'_> {
fn merge(&self, l: (&str, &BigInt), r: (&str, &BigInt)) -> (String, BigInt) {
self.0.merge_with(l, r, &self.0.numwords_inflections_una)
}
fn to_cardinal(&self, value: &BigInt) -> Result<String> {
ro_to_cardinal(self, value)
}
fn cards(&self) -> &Cards {
self.0.cards()
}
fn maxval(&self) -> &BigInt {
self.0.maxval()
}
fn negword(&self) -> &str {
self.0.negword()
}
fn pointword(&self) -> &str {
self.0.pointword()
}
fn is_title(&self) -> bool {
self.0.is_title()
}
fn exclude_title(&self) -> &[String] {
self.0.exclude_title()
}
fn to_ordinal(&self, value: &BigInt) -> Result<String> {
self.0.to_ordinal(value)
}
fn to_ordinal_num(&self, value: &BigInt) -> Result<String> {
self.0.to_ordinal_num(value)
}
fn to_year(&self, value: &BigInt) -> Result<String> {
self.0.to_year(value)
}
fn lang_name(&self) -> &str {
self.0.lang_name()
}
fn currency_forms(&self, code: &str) -> Option<&CurrencyForms> {
self.0.currency_forms(code)
}
fn currency_adjective(&self, code: &str) -> Option<&str> {
self.0.currency_adjective(code)
}
fn currency_precision(&self, code: &str) -> i64 {
self.0.currency_precision(code)
}
fn pluralize(&self, n: &BigInt, forms: &[String]) -> Result<String> {
self.0.pluralize(n, forms)
}
fn default_separator(&self) -> &str {
self.0.default_separator()
}
fn default_currency(&self) -> &str {
self.0.default_currency()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::currency::CurrencyValue;
use bigdecimal::BigDecimal;
use std::str::FromStr;
fn cur(arg: &str, code: &str) -> Result<String> {
let is_int = !arg.contains('.');
let v = CurrencyValue::parse(arg, is_int, !is_int, !is_int).unwrap();
LangRo::new().to_currency(&v, code, true, None, false)
}
fn cheque(arg: &str, code: &str) -> Result<String> {
LangRo::new().to_cheque(&BigDecimal::from_str(arg).unwrap(), code)
}
fn is_not_impl(r: Result<String>) -> bool {
matches!(r, Err(N2WError::NotImplemented(_)))
}
#[test]
fn corpus_currency_eur_usd() {
let rows: &[(&str, &str, &str)] = &[
("0", "EUR", "zero euro"),
("1", "EUR", "unu euro"),
("2", "EUR", "doi euro"),
("100", "EUR", "una sută de euro"),
(
"12.34",
"EUR",
"doisprezece euro și treizeci și patru de cenţi",
),
("0.01", "EUR", "zero euro și unu cent"),
("1.0", "EUR", "unu euro și zero cenţi"),
(
"99.99",
"EUR",
"nouăzeci și nouă de euro și nouăzeci și nouă de cenţi",
),
(
"1234.56",
"EUR",
"una mie două sute treizeci și patru de euro și cincizeci și șase de cenţi",
),
(
"-12.34",
"EUR",
"minus doisprezece euro și treizeci și patru de cenţi",
),
("1000000", "EUR", "un milion de euro"),
("0.5", "EUR", "zero euro și cincizeci de cenţi"),
("0", "USD", "zero dolari"),
("1", "USD", "unu dolar"),
("2", "USD", "doi dolari"),
("100", "USD", "una sută de dolari"),
(
"12.34",
"USD",
"doisprezece dolari și treizeci și patru de cenţi",
),
("0.01", "USD", "zero dolari și unu cent"),
("1.0", "USD", "unu dolar și zero cenţi"),
(
"99.99",
"USD",
"nouăzeci și nouă de dolari și nouăzeci și nouă de cenţi",
),
(
"1234.56",
"USD",
"una mie două sute treizeci și patru de dolari și cincizeci și șase de cenţi",
),
(
"-12.34",
"USD",
"minus doisprezece dolari și treizeci și patru de cenţi",
),
("1000000", "USD", "un milion de dolari"),
("0.5", "USD", "zero dolari și cincizeci de cenţi"),
];
for (arg, code, want) in rows {
assert_eq!(cur(arg, code).unwrap(), *want, "currency:{} {}", code, arg);
}
}
#[test]
fn corpus_cheque() {
assert_eq!(
cheque("1234.56", "EUR").unwrap(),
"O MIE DOUĂ SUTE TREIZECI ȘI PATRU AND 56/100 DE EURO"
);
assert_eq!(
cheque("1234.56", "USD").unwrap(),
"O MIE DOUĂ SUTE TREIZECI ȘI PATRU AND 56/100 DE DOLARI"
);
}
#[test]
fn corpus_unimplemented_codes() {
let args = [
"0", "1", "2", "100", "12.34", "0.01", "1.0", "99.99", "1234.56", "-12.34", "1000000",
"0.5",
];
for code in ["GBP", "JPY", "KWD", "BHD", "INR", "CNY", "CHF"] {
for arg in args {
assert!(is_not_impl(cur(arg, code)), "currency:{} {}", code, arg);
}
assert!(is_not_impl(cheque("1234.56", code)), "cheque:{}", code);
}
}
#[test]
fn not_implemented_message() {
match cur("1", "GBP") {
Err(N2WError::NotImplemented(m)) => {
assert_eq!(
m,
"Currency code \"GBP\" not implemented for \"Num2Word_RO\""
)
}
other => panic!("want NotImplemented, got {:?}", other),
}
match cheque("1234.56", "GBP") {
Err(N2WError::NotImplemented(m)) => {
assert_eq!(
m,
"Currency code \"GBP\" not implemented for \"Num2Word_RO\""
)
}
other => panic!("want NotImplemented, got {:?}", other),
}
}
#[test]
fn ron_replaces() {
let rows: &[(&str, &str)] = &[
("0", "zero lei"),
("1", "un leu"), ("2", "doi lei"),
("100", "una sută de lei"),
("12.34", "doisprezece lei și treizeci și patru de bani"),
("0.01", "zero lei și un ban"), ("1.0", "un leu"), ("0.0", "zero lei"), ("2.0", "doi lei"),
("1.01", "un leu și un ban"), ("21.0", "douăzeci și unu de lei"),
("-1.0", "minus un leu"),
(
"99.99",
"nouăzeci și nouă de lei și nouăzeci și nouă de bani",
),
(
"1234.56",
"una mie două sute treizeci și patru de lei și cincizeci și șase de bani",
),
(
"-12.34",
"minus doisprezece lei și treizeci și patru de bani",
),
("1000000", "un milion de lei"),
("0.5", "zero lei și cincizeci de bani"),
];
for (arg, want) in rows {
assert_eq!(cur(arg, "RON").unwrap(), *want, "currency:RON {}", arg);
}
}
#[test]
fn fractional_cents() {
let rows: &[(&str, &str, &str)] = &[
("1.011", "RON", "un leu și unu virgulă un bani"),
("0.005", "RON", "zero lei și zero virgulă cinci bani"),
("1.005", "RON", "un leu și zero virgulă cinci bani"),
(
"2.675",
"RON",
"doi lei și șaizeci și șapte virgulă cinci bani",
),
("0.005", "EUR", "zero euro și zero virgulă cinci cenţi"),
(
"2.675",
"EUR",
"doi euro și șaizeci și șapte virgulă cinci cenţi",
),
("1.005", "USD", "unu dolar și zero virgulă cinci cenţi"),
];
for (arg, code, want) in rows {
assert_eq!(cur(arg, code).unwrap(), *want, "{} {}", code, arg);
}
}
#[test]
fn separator_is_used_by_the_strip() {
let l = LangRo::new();
let v = CurrencyValue::parse("1.0", false, true, true).unwrap();
assert_eq!(
l.to_currency(&v, "RON", true, Some(" plus"), false)
.unwrap(),
"un leu"
);
assert_eq!(
l.to_currency(&v, "RON", true, Some(","), false).unwrap(),
"un leu"
);
assert_eq!(
l.to_currency(&v, "EUR", true, None, false).unwrap(),
"unu euro și zero cenţi"
);
}
#[test]
fn cents_flag_is_ignored() {
let l = LangRo::new();
let v = CurrencyValue::parse("12.34", false, true, true).unwrap();
assert_eq!(
l.to_currency(&v, "EUR", false, None, false).unwrap(),
"doisprezece euro și treizeci și patru de cenţi"
);
}
#[test]
fn adjective_prefix() {
let l = LangRo::new();
let one = CurrencyValue::parse("1", true, false, false).unwrap();
assert_eq!(
l.to_currency(&one, "RON", true, None, true).unwrap(),
"unu Romanian leu"
);
let one_f = CurrencyValue::parse("1.0", false, true, true).unwrap();
assert_eq!(
l.to_currency(&one_f, "RON", true, None, true).unwrap(),
"unu Romanian leu"
);
let two = CurrencyValue::parse("2", true, false, false).unwrap();
assert_eq!(
l.to_currency(&two, "USD", true, None, true).unwrap(),
"doi US dolari"
);
assert_eq!(
l.to_currency(&one, "EUR", true, None, true).unwrap(),
"unu euro"
);
}
#[test]
fn una_flip_does_not_leak() {
let l = LangRo::new();
let hundred = BigInt::from(100);
assert_eq!(l.to_cardinal(&hundred).unwrap(), "o sută");
assert_eq!(l.to_cardinal(&BigInt::from(1000)).unwrap(), "o mie");
assert_eq!(
l.to_cardinal(&BigInt::from(1234)).unwrap(),
"o mie două sute treizeci și patru"
);
assert_eq!(l.to_ordinal(&hundred).unwrap(), "al una sutălea");
let v = CurrencyValue::parse("100", true, false, false).unwrap();
assert_eq!(
l.to_currency(&v, "EUR", true, None, false).unwrap(),
"una sută de euro"
);
assert_eq!(l.to_cardinal(&hundred).unwrap(), "o sută");
assert!(is_not_impl(l.to_currency(&v, "GBP", true, None, false)));
assert_eq!(l.to_cardinal(&hundred).unwrap(), "o sută");
assert_eq!(
cheque("1234.56", "EUR").unwrap(),
"O MIE DOUĂ SUTE TREIZECI ȘI PATRU AND 56/100 DE EURO"
);
}
#[test]
fn mega_giga_keep_un() {
let l = LangRo::new();
for (arg, want) in [
("1000000", "un milion de euro"),
("1000000000", "un miliard de euro"),
] {
let v = CurrencyValue::parse(arg, true, false, false).unwrap();
assert_eq!(l.to_currency(&v, "EUR", true, None, false).unwrap(), want);
}
}
#[test]
fn plural_rule() {
for (arg, want) in [
("1", "unu dolar"), ("0", "zero dolari"), ("19", "nouăsprezece dolari"), ("101", "una sută unu dolari"), ("20", "douăzeci de dolari"), ("100", "una sută de dolari"), ] {
let v = CurrencyValue::parse(arg, true, false, false).unwrap();
let got = LangRo::new()
.to_currency(&v, "USD", true, None, false)
.unwrap();
assert_eq!(got, want, "{}", arg);
}
}
}