use crate::base::{
clean, set_low_numwords, set_mid_numwords, splitnum, Cards, Lang, N2WError, Node, Result,
};
use crate::currency::CurrencyForms;
use num_bigint::BigInt;
use num_traits::{One, Signed, Zero};
use std::collections::HashMap;
const GIGA_SUFFIX: &str = "иллиард";
const MEGA_SUFFIX: &str = "иллион";
const LOW_NUMWORDS: [&str; 21] = [
"бист",
"нуздаҳ",
"ҳаждаҳ",
"ҳабдаҳ",
"шонздаҳ",
"понздаҳ",
"чордаҳ",
"сенздаҳ",
"дувоздаҳ",
"ёздаҳ",
"даҳ",
"нӯҳ",
"ҳашт",
"ҳафт",
"шаш",
"панҷ",
"чор",
"се",
"ду",
"як",
"сифр",
];
fn build_currency_forms() -> HashMap<&'static str, CurrencyForms> {
const GENERIC_DOLLARS: [&str; 2] = ["доллар", "доллар"];
const GENERIC_CENTS: [&str; 2] = ["сент", "сент"];
let mut m: HashMap<&'static str, CurrencyForms> = HashMap::new();
m.insert("EUR", CurrencyForms::new(&["евро", "евро"], &GENERIC_CENTS));
m.insert("USD", CurrencyForms::new(&GENERIC_DOLLARS, &GENERIC_CENTS));
m.insert("RUB", CurrencyForms::new(&["рубл", "рубл"], &["копейк", "копейк"]));
m.insert("TJS", CurrencyForms::new(&["сомонӣ", "сомонӣ"], &["дирам", "дирам"]));
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()
}
pub struct LangTg {
cards: Cards,
maxval: BigInt,
exclude_title: Vec<String>,
hundred: BigInt,
currency_forms: HashMap<&'static str, CurrencyForms>,
currency_adjectives: HashMap<&'static str, &'static str>,
}
impl Default for LangTg {
fn default() -> Self {
Self::new()
}
}
impl LangTg {
pub fn new() -> Self {
let mut cards = Cards::new();
let high = ["квинт", "квадр", "тр", "м", "м"];
let cap = 3 * (high.len() as i64 + 1);
let mut n = cap;
for word in high.iter() {
if n <= 5 {
break;
}
let suffix = if n == 9 { GIGA_SUFFIX } else { MEGA_SUFFIX };
cards.insert(BigInt::from(10u8).pow(n as u32), format!("{}{}", word, suffix));
n -= 3;
}
set_mid_numwords(
&mut cards,
&[
(1000, "ҳазор"),
(100, "сад"),
(90, "навад"),
(80, "ҳаштод"),
(70, "ҳафтод"),
(60, "шаст"),
(50, "панҷоҳ"),
(40, "чил"),
(30, "си"),
],
);
set_low_numwords(&mut cards, &LOW_NUMWORDS);
let maxval = cards.highest().cloned().unwrap_or_else(BigInt::zero) * BigInt::from(1000);
LangTg {
cards,
maxval,
exclude_title: vec!["ва".into(), "минус".into(), "нуқта".into()],
hundred: BigInt::from(100),
currency_forms: build_currency_forms(),
currency_adjectives: build_currency_adjectives(),
}
}
fn verify_ordinal(&self, value: &BigInt) -> Result<()> {
if value.is_negative() {
return Err(N2WError::Type(format!(
"Cannot treat negative num {} as ordinal.",
value
)));
}
Ok(())
}
fn verify_ordinal_float(&self, value: &crate::floatpath::FloatValue) -> Result<BigInt> {
if let crate::floatpath::FloatValue::Float { value: f, .. } = value {
if f.is_nan() {
return Err(N2WError::Value(
"cannot convert float NaN to integer".into(),
));
}
if f.is_infinite() {
return Err(N2WError::Overflow(
"cannot convert float infinity to integer".into(),
));
}
}
match value.as_whole_int() {
None => Err(N2WError::Type(
"Cannot treat float as ordinal.".into(),
)),
Some(i) if i.is_negative() => Err(N2WError::Type(format!(
"Cannot treat negative num {} as ordinal.",
i
))),
Some(i) => Ok(i),
}
}
}
impl Lang for LangTg {
fn default_currency(&self) -> &str {
"EUR"
}
fn default_separator(&self) -> &str {
","
}
fn cards(&self) -> &Cards {
&self.cards
}
fn maxval(&self) -> &BigInt {
&self.maxval
}
fn negword(&self) -> &str {
"минус "
}
fn pointword(&self) -> &str {
"нуқта"
}
fn exclude_title(&self) -> &[String] {
&self.exclude_title
}
fn merge(&self, l: (&str, &BigInt), r: (&str, &BigInt)) -> (String, BigInt) {
let (ltext, lnum) = l;
let (rtext, rnum) = r;
let hundred = &self.hundred;
if lnum.is_one() && rnum < hundred {
(rtext.to_string(), rnum.clone())
} else if hundred > lnum && lnum > rnum {
if ltext == "си" {
(format!("{}ю {}", ltext, rtext), lnum + rnum)
} else if ltext == "панҷоҳ" {
(format!("панҷову {}", rtext), lnum + rnum)
} else {
(format!("{}у {}", ltext, rtext), lnum + rnum)
}
} else if lnum >= hundred && hundred > rnum {
(format!("{}у {}", ltext, rtext), lnum + rnum)
} else if rnum > lnum {
if ltext == "яксад" && !LOW_NUMWORDS.contains(&rtext) {
(format!("сад {}", rtext), lnum * rnum)
} else if rtext == "сад" {
(format!("{}{}", ltext, rtext), lnum * rnum)
} else {
(format!("{} {}", ltext, rtext), lnum * rnum)
}
} else {
(format!("{}у {}", ltext, rtext), lnum + rnum)
}
}
fn to_cardinal(&self, value: &BigInt) -> Result<String> {
let mut out = String::new();
let mut v = value.clone();
if v.is_negative() {
v = v.abs();
out = self.negword().to_string();
}
if v >= self.maxval {
return Err(N2WError::Overflow(format!(
"abs({}) must be less than {}.",
v, self.maxval
)));
}
if v == self.hundred {
return Ok(self.title(&format!("{}сад", out)));
}
let tree = splitnum(self, &v).ok_or_else(|| {
N2WError::Type(format!("type({}) not in [long, int, float]", v))
})?;
let words = match clean(self, tree) {
Node::Leaf(t, _) => t,
Node::List(_) => return Err(N2WError::Type("clean did not reduce".into())),
};
Ok(self.title(&format!("{}{}", out, words)))
}
fn to_ordinal(&self, value: &BigInt) -> Result<String> {
self.verify_ordinal(value)?;
let cardinal = self.to_cardinal(value)?;
let lastword = cardinal.split(' ').next_back().unwrap_or("");
if lastword == "ду" || lastword == "се" || lastword == "си" {
Ok(format!("{}юм", cardinal))
} else {
Ok(format!("{}ум", cardinal))
}
}
fn to_ordinal_num(&self, value: &BigInt) -> Result<String> {
self.verify_ordinal(value)?;
let ord = self.to_ordinal(value)?;
let n = ord.chars().count();
let suffix: String = ord.chars().skip(n.saturating_sub(2)).collect();
Ok(format!("{}{}", value, suffix))
}
fn ordinal_float_entry(&self, value: &crate::floatpath::FloatValue) -> Result<String> {
let i = self.verify_ordinal_float(value)?;
self.to_ordinal(&i)
}
fn ordinal_num_float_entry(
&self,
value: &crate::floatpath::FloatValue,
repr_str: &str,
) -> Result<String> {
let i = self.verify_ordinal_float(value)?;
let ord = self.to_ordinal(&i)?;
let n = ord.chars().count();
let suffix: String = ord.chars().skip(n.saturating_sub(2)).collect();
Ok(format!("{}{}", repr_str, suffix))
}
fn lang_name(&self) -> &str {
"Num2Word_TG"
}
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> {
let form = if n.is_one() { 0 } else { 1 };
forms
.get(form)
.cloned()
.ok_or_else(|| N2WError::Index("tuple index out of range".into()))
}
}