use crate::base::{
set_low_numwords, set_mid_numwords, Cards, KwVal, Kwargs, Lang, N2WError, Result,
};
use crate::currency::{CurrencyForms, CurrencyValue};
use crate::floatpath::{float2tuple, FloatValue};
use crate::strnum::python_decimal_str;
use bigdecimal::BigDecimal;
use num_bigint::BigInt;
use num_traits::{One, Signed, Zero};
use std::collections::HashMap;
const ZERO: &str = "nulla";
const NEGWORD: &str = "mínusz ";
const GIGA_SUFFIX: &str = "illiárd";
const MEGA_SUFFIX: &str = "illió";
const MID: [(i64, &str); 9] = [
(1000, "ezer"),
(100, "száz"),
(90, "kilencven"),
(80, "nyolcvan"),
(70, "hetven"),
(60, "hatvan"),
(50, "ötven"),
(40, "negyven"),
(30, "harminc"),
];
const LOW_BASE: [&str; 9] = [
"kilenc", "nyolc", "hét", "hat", "öt", "négy", "három", "kettő", "egy",
];
const PARTIAL_ORDS: [(&str, &str); 23] = [
("nulla", "nullad"),
("egy", "egyed"),
("kettő", "ketted"),
("három", "harmad"),
("négy", "negyed"),
("öt", "ötöd"),
("hat", "hatod"),
("hét", "heted"),
("nyolc", "nyolcad"),
("kilenc", "kilenced"),
("tíz", "tized"),
("húsz", "huszad"),
("harminc", "harmincad"),
("negyven", "negyvened"),
("ötven", "ötvened"),
("hatvan", "hatvanad"),
("hetven", "hetvened"),
("nyolcvan", "nyolcvanad"),
("kilencven", "kilencvened"),
("száz", "század"),
("ezer", "ezred"),
("illió", "milliomod"),
("illiárd", "milliárdod"),
];
fn pow10(n: u32) -> BigInt {
BigInt::from(10u8).pow(n)
}
fn recursion_error() -> N2WError {
N2WError::Custom {
module: "builtins",
class: "RecursionError",
msg: "maximum recursion depth exceeded".into(),
}
}
fn py_float_repr(f: f64) -> String {
if !f.is_finite() {
return format!("{}", f);
}
let sci = format!("{:e}", f);
let (mant, exp) = sci.split_once('e').expect("{:e} always has an exponent");
let exp: i32 = exp.parse().expect("{:e} exponent is an integer");
if !(-4..16).contains(&exp) {
return format!(
"{}e{}{:02}",
mant,
if exp < 0 { "-" } else { "+" },
exp.abs()
);
}
let s = format!("{}", f);
if s.contains('.') {
s
} else {
format!("{}.0", s)
}
}
fn py_floordiv(x: f64, y: f64) -> f64 {
let m = x % y; let div = (x - m) / y;
let mut fd = div.floor();
if div - fd > 0.5 {
fd += 1.0;
}
fd
}
fn fv_lt_zero(v: &FloatValue) -> bool {
match v {
FloatValue::Float { value, .. } => *value < 0.0,
FloatValue::Decimal { value, .. } => value.is_negative(),
}
}
fn fv_neg(v: &FloatValue) -> FloatValue {
match v {
FloatValue::Float { value, precision } => FloatValue::Float {
value: -value,
precision: *precision,
},
FloatValue::Decimal { value, precision } => FloatValue::Decimal {
value: -value.clone(),
precision: *precision,
},
}
}
fn dec_repr(v: &BigInt, scale: i64) -> BigDecimal {
if scale >= 0 {
BigDecimal::new(v * pow10(scale as u32), scale)
} else {
BigDecimal::new(v / pow10((-scale) as u32), scale)
}
}
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 build_low_numwords() -> Vec<String> {
let mut teens: Vec<String> = LOW_BASE.iter().map(|w| format!("tizen{}", w)).collect();
teens.push("tíz".to_string());
teens.extend(LOW_BASE.iter().map(|s| s.to_string()));
let mut out: Vec<String> = LOW_BASE.iter().map(|w| format!("huszon{}", w)).collect();
out.push("húsz".to_string());
out.extend(teens);
out.push(ZERO.to_string());
out
}
fn build_cards() -> Cards {
let mut cards = Cards::new();
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<String> = vec!["cent".to_string()];
high.extend(gen_high_numwords(&units, &tens, &lows));
debug_assert_eq!(high.len(), 100);
let cap: i64 = 3 + 6 * high.len() as i64;
let mut n = cap;
for word in high.iter() {
if n <= 3 {
break; }
cards.insert(pow10(n as u32), format!("{}{}", word, GIGA_SUFFIX));
cards.insert(pow10((n - 3) as u32), format!("{}{}", word, MEGA_SUFFIX));
n -= 6;
}
set_mid_numwords(&mut cards, &MID);
let low_owned = build_low_numwords();
let low_refs: Vec<&str> = low_owned.iter().map(|s| s.as_str()).collect();
set_low_numwords(&mut cards, &low_refs);
cards
}
fn build_currency_forms() -> HashMap<&'static str, CurrencyForms> {
let mut m = HashMap::new();
let mut add = |code: &'static str, unit: &[&str], subunit: &[&str]| {
m.insert(code, CurrencyForms::new(unit, subunit));
};
add("AED", &["dirham", "dirhams"], &["fils", "fils"]);
add("BHD", &["dinar", "dinars"], &["fils", "fils"]);
add("BRL", &["real", "reais"], &["cent", "cents"]);
add("CHF", &["franc", "francs"], &["rappen", "rappen"]);
add("CNY", &["yuan", "yuan"], &["fen", "fen"]);
add("HKD", &["dollar", "dollars"], &["cent", "cents"]);
add("IQD", &["dinar", "dinars"], &["fils", "fils"]);
add("JOD", &["dinar", "dinars"], &["fils", "fils"]);
add("KWD", &["dinar", "dinars"], &["fils", "fils"]);
add("LYD", &["dinar", "dinars"], &["dirham", "dirhams"]);
add("NGN", &["naira", "naira"], &["kobo", "kobo"]);
add("NZD", &["dollar", "dollars"], &["cent", "cents"]);
add("OMR", &["rial", "rials"], &["baisa", "baisa"]);
add("QAR", &["riyal", "riyals"], &["dirham", "dirhams"]);
add("SGD", &["dollar", "dollars"], &["cent", "cents"]);
add("TND", &["dinar", "dinars"], &["millime", "millimes"]);
add("ZAR", &["rand", "rand"], &["cent", "cents"]);
add("AUD", &["dollar", "dollars"], &["cent", "cents"]); add("CAD", &["dollar", "dollars"], &["cent", "cents"]); add("EUR", &["euro", "euros"], &["cent", "cents"]); add("GBP", &["pound", "pounds"], &["penny", "pence"]); add("INR", &["rupee", "rupees"], &["paisa", "paise"]); add("JPY", &["yen", "yen"], &["sen", "sen"]); add("KRW", &["won", "won"], &["jeon", "jeon"]); add("MXN", &["peso", "pesos"], &["cent", "cents"]); add("SAR", &["riyal", "riyals"], &["halalah", "halalas"]); add("USD", &["dollar", "dollars"], &["cent", "cents"]);
add("BYN", &["rouble", "roubles"], &["kopek", "kopeks"]);
add("EEK", &["kroon", "kroons"], &["sent", "senti"]);
add("HUF", &["forint", "forint"], &["fillér", "fillér"]);
add("ISK", &["króna", "krónur"], &["aur", "aurar"]);
add("LTL", &["litas", "litas"], &["cent", "cents"]);
add("LVL", &["lat", "lats"], &["santim", "santims"]);
add("NOK", &["krone", "kroner"], &["øre", "øre"]);
add("PLN", &["zloty", "zlotys", "zlotu"], &["grosz", "groszy"]);
add(
"RON",
&["leu", "lei", "de lei"],
&["ban", "bani", "de bani"],
);
add("RUB", &["rouble", "roubles"], &["kopek", "kopeks"]);
add("SEK", &["krona", "kronor"], &["öre", "öre"]);
add("UZS", &["sum", "sums"], &["tiyin", "tiyins"]);
debug_assert_eq!(m.len(), 39);
m
}
pub struct LangHu {
cards: Cards,
maxval: BigInt,
currency_forms: HashMap<&'static str, CurrencyForms>,
}
impl Default for LangHu {
fn default() -> Self {
Self::new()
}
}
impl LangHu {
pub fn new() -> Self {
let cards = build_cards();
let maxval = cards
.highest()
.map(|h| h * BigInt::from(1000))
.unwrap_or_else(BigInt::zero);
LangHu {
cards,
maxval,
currency_forms: build_currency_forms(),
}
}
fn card(&self, key: &BigInt) -> Result<String> {
self.cards
.get(key)
.map(|s| s.to_string())
.ok_or_else(|| N2WError::Key(format!("{}", key)))
}
fn to_cardinal_z(&self, value: &BigInt, zero: &str) -> Result<String> {
if value.is_negative() {
return Ok(format!(
"{}{}",
NEGWORD,
self.to_cardinal_z(&(-value), ZERO)?
));
}
if value.is_zero() {
return Ok(zero.to_string());
}
if zero.is_empty() && *value == BigInt::from(2) {
return Ok("két".to_string());
}
if *value < BigInt::from(30) {
return self.card(value);
}
if *value < BigInt::from(100) {
return self.tens_to_cardinal(value);
}
if *value < BigInt::from(1000) {
return self.hundreds_to_cardinal(value);
}
if *value < pow10(6) {
return self.thousands_to_cardinal(value);
}
self.big_number_to_cardinal(value)
}
fn tens_to_cardinal(&self, value: &BigInt) -> Result<String> {
if let Some(w) = self.cards.get(value) {
return Ok(w.to_string());
}
let ten = BigInt::from(10);
let base = (value / &ten) * &ten;
let head = self.card(&base)?;
let rest = self.to_cardinal_z(&(value % &ten), ZERO)?;
Ok(format!("{}{}", head, rest))
}
fn hundreds_to_cardinal(&self, value: &BigInt) -> Result<String> {
let hundred = BigInt::from(100);
let hundreds = value / &hundred;
let mut prefix = "száz".to_string();
if !hundreds.is_one() {
prefix = format!("{}{}", self.to_cardinal_z(&hundreds, "")?, prefix);
}
let postfix = self.to_cardinal_z(&(value % &hundred), "")?;
Ok(format!("{}{}", prefix, postfix))
}
fn thousands_to_cardinal(&self, value: &BigInt) -> Result<String> {
let k = BigInt::from(1000);
let thousands = value / &k;
let mut prefix = "ezer".to_string();
if !thousands.is_one() {
prefix = format!("{}{}", self.to_cardinal_z(&thousands, "")?, prefix);
}
let postfix = self.to_cardinal_z(&(value % &k), "")?;
let sep = if *value <= BigInt::from(2000) || postfix.is_empty() {
""
} else {
"-"
};
Ok(format!("{}{}{}", prefix, sep, postfix))
}
fn big_number_to_cardinal(&self, value: &BigInt) -> Result<String> {
let d = value.to_string().len();
let d = if d % 3 != 0 { d } else { d - 2 };
let exp = pow10((d / 3 * 3) as u32);
let rest = self.to_cardinal_z(&(value % &exp), "")?;
let head = self.to_cardinal_z(&(value / &exp), "")?;
let scale = self.card(&exp)?;
let tail = if rest.is_empty() {
String::new()
} else {
format!("-{}", rest)
};
Ok(format!("{}{}{}", head, scale, tail))
}
fn ordinalize(&self, mut out: String) -> String {
for (card_word, ord_word) in PARTIAL_ORDS.iter() {
if out.ends_with(card_word) {
out = format!("{}{}", &out[..out.len() - card_word.len()], ord_word);
break; }
}
format!("{}ik", out)
}
fn hu_cardinal_any(
&self,
value: &FloatValue,
precision_override: Option<u32>,
zero: &str,
) -> Result<String> {
if value.as_whole_int().is_none() {
return self.to_cardinal_float(value, precision_override);
}
match value {
FloatValue::Float { value: f, .. } => self.pyfloat_whole_cardinal(*f, zero),
FloatValue::Decimal { value: d, .. } => {
let (v, scale) = {
let scale = d.as_bigint_and_exponent().1;
(d.with_scale(0).as_bigint_and_exponent().0, scale)
};
self.pydec_whole_cardinal(&v, scale, zero)
}
}
}
fn pyfloat_whole_cardinal(&self, f: f64, zero: &str) -> Result<String> {
if f < 0.0 {
return Ok(format!(
"{}{}",
NEGWORD,
self.pyfloat_whole_cardinal(-f, ZERO)?
));
}
if f == 0.0 {
return Ok(zero.to_string());
}
if zero.is_empty() && f == 2.0 {
return Ok("két".to_string());
}
if f < 30.0 {
return self.card(&BigInt::from(f as i64));
}
if f < 100.0 {
if let Some(w) = self.cards.get(&BigInt::from(f as i64)) {
return Ok(w.to_string());
}
let base = BigInt::from((py_floordiv(f, 10.0) * 10.0) as i64);
let head = self.card(&base)?;
let rest = self.pyfloat_whole_cardinal(f % 10.0, ZERO)?;
return Ok(format!("{}{}", head, rest));
}
if f < 1000.0 {
let hundreds = py_floordiv(f, 100.0);
let mut prefix = "száz".to_string();
if hundreds != 1.0 {
prefix = format!("{}{}", self.pyfloat_whole_cardinal(hundreds, "")?, prefix);
}
let postfix = self.pyfloat_whole_cardinal(f % 100.0, "")?;
return Ok(format!("{}{}", prefix, postfix));
}
if f < 1e6 {
let thousands = py_floordiv(f, 1000.0);
let mut prefix = "ezer".to_string();
if thousands != 1.0 {
prefix = format!("{}{}", self.pyfloat_whole_cardinal(thousands, "")?, prefix);
}
let postfix = self.pyfloat_whole_cardinal(f % 1000.0, "")?;
let sep = if f <= 2000.0 || postfix.is_empty() {
""
} else {
"-"
};
return Ok(format!("{}{}{}", prefix, sep, postfix));
}
let slen = py_float_repr(f).len(); let d = if slen % 3 != 0 { slen } else { slen - 2 };
let exp_pow = (d / 3 * 3) as u32;
let exp_int = pow10(exp_pow);
let exp_f: f64 = exp_int.to_string().parse().expect("10**n parses");
if f < exp_f {
return Err(recursion_error());
}
let m = f % exp_f;
let rest = self.pyfloat_whole_cardinal(m, "")?;
let head = self.pyfloat_whole_cardinal(py_floordiv(f, exp_f), "")?;
let scale = self.card(&exp_int)?;
let tail = if rest.is_empty() {
String::new()
} else {
format!("-{}", rest)
};
Ok(format!("{}{}{}", head, scale, tail))
}
fn pydec_whole_cardinal(&self, v: &BigInt, scale: i64, zero: &str) -> Result<String> {
if v.is_negative() {
return Ok(format!(
"{}{}",
NEGWORD,
self.pydec_whole_cardinal(&(-v), scale, ZERO)?
));
}
if v.is_zero() {
return Ok(zero.to_string());
}
if zero.is_empty() && *v == BigInt::from(2) {
return Ok("két".to_string());
}
if *v < BigInt::from(30) {
return self.card(v);
}
if *v < BigInt::from(100) {
if let Some(w) = self.cards.get(v) {
return Ok(w.to_string());
}
let ten = BigInt::from(10);
let base = (v / &ten) * &ten; let head = self.card(&base)?;
let rest = self.pydec_whole_cardinal(&(v % &ten), scale.max(0), ZERO)?;
return Ok(format!("{}{}", head, rest));
}
if *v < BigInt::from(1000) {
let hundred = BigInt::from(100);
let hundreds = v / &hundred;
let mut prefix = "száz".to_string();
if !hundreds.is_one() {
prefix = format!("{}{}", self.pydec_whole_cardinal(&hundreds, 0, "")?, prefix);
}
let postfix = self.pydec_whole_cardinal(&(v % &hundred), scale.max(0), "")?;
return Ok(format!("{}{}", prefix, postfix));
}
if *v < pow10(6) {
let k = BigInt::from(1000);
let thousands = v / &k;
let mut prefix = "ezer".to_string();
if !thousands.is_one() {
prefix = format!(
"{}{}",
self.pydec_whole_cardinal(&thousands, 0, "")?,
prefix
);
}
let postfix = self.pydec_whole_cardinal(&(v % &k), scale.max(0), "")?;
let sep = if *v <= BigInt::from(2000) || postfix.is_empty() {
""
} else {
"-"
};
return Ok(format!("{}{}{}", prefix, sep, postfix));
}
let slen = python_decimal_str(&dec_repr(v, scale)).len(); let d = if slen % 3 != 0 { slen } else { slen - 2 };
let exp = pow10((d / 3 * 3) as u32);
let q = v / &exp;
let rem = v % &exp;
if q.to_string().len() > 28 || rem.to_string().len() > 28 {
return Err(N2WError::Custom {
module: "decimal",
class: "InvalidOperation",
msg: "[<class 'decimal.DivisionImpossible'>]".into(),
});
}
if q.is_zero() {
return Err(recursion_error());
}
let rest = self.pydec_whole_cardinal(&rem, scale.max(0), "")?;
let head = self.pydec_whole_cardinal(&q, 0, "")?;
let scale_word = self.card(&exp)?;
let tail = if rest.is_empty() {
String::new()
} else {
format!("-{}", rest)
};
Ok(format!("{}{}{}", head, scale_word, tail))
}
}
impl Lang for LangHu {
fn default_currency(&self) -> &str {
"HUF"
}
fn default_separator(&self) -> &str {
","
}
fn cards(&self) -> &Cards {
&self.cards
}
fn maxval(&self) -> &BigInt {
&self.maxval
}
fn negword(&self) -> &str {
NEGWORD
}
fn pointword(&self) -> &str {
"egész"
}
fn to_cardinal(&self, value: &BigInt) -> Result<String> {
self.to_cardinal_z(value, ZERO)
}
fn to_ordinal(&self, value: &BigInt) -> Result<String> {
if value.is_negative() {
return Ok(format!("{}{}", NEGWORD, self.to_ordinal(&(-value))?));
}
if value.is_one() {
return Ok("első".to_string());
}
if *value == BigInt::from(2) {
return Ok("második".to_string());
}
let out = self.to_cardinal(value)?;
Ok(self.ordinalize(out))
}
fn to_ordinal_num(&self, value: &BigInt) -> Result<String> {
if value.is_negative() {
return Err(N2WError::Type(format!(
"Cannot treat negative num {} as ordinal.",
value
)));
}
Ok(format!("{}.", value))
}
fn to_year(&self, value: &BigInt) -> Result<String> {
let mut prefix = "";
let mut val = value.clone();
if val.is_negative() {
val = val.abs();
prefix = "i. e. ";
}
Ok(format!("{}{}", prefix, self.to_cardinal(&val)?))
}
fn cardinal_float_entry(
&self,
value: &FloatValue,
precision_override: Option<u32>,
) -> Result<String> {
self.hu_cardinal_any(value, precision_override, ZERO)
}
fn ordinal_float_entry(&self, value: &FloatValue) -> Result<String> {
if fv_lt_zero(value) {
return Ok(format!(
"{}{}",
NEGWORD,
self.ordinal_float_entry(&fv_neg(value))?
));
}
if let Some(i) = value.as_whole_int() {
if i.is_one() {
return Ok("első".to_string());
}
if i == BigInt::from(2) {
return Ok("második".to_string());
}
}
let out = self.cardinal_float_entry(value, None)?;
Ok(self.ordinalize(out))
}
fn ordinal_num_float_entry(&self, value: &FloatValue, repr_str: &str) -> Result<String> {
if value.as_whole_int().is_none() {
return Err(N2WError::Type(format!(
"Cannot treat float {} as ordinal.",
repr_str
)));
}
if fv_lt_zero(value) {
return Err(N2WError::Type(format!(
"Cannot treat negative num {} as ordinal.",
repr_str
)));
}
Ok(format!("{}.", repr_str))
}
fn year_float_entry(&self, value: &FloatValue) -> Result<String> {
if fv_lt_zero(value) {
return Ok(format!(
"i. e. {}",
self.cardinal_float_entry(&fv_neg(value), None)?
));
}
self.cardinal_float_entry(value, None)
}
fn to_cardinal_kw(&self, value: &BigInt, kw: &Kwargs) -> Result<String> {
if !kw.only(&["zero"]) {
return Err(N2WError::Fallback("kwargs".into()));
}
let zero = match kw.get("zero") {
None => ZERO,
Some(KwVal::Str(s)) => s.as_str(),
Some(KwVal::None) => {
if value.is_zero() {
return Err(N2WError::ReturnsNone);
}
ZERO
}
Some(_) => return Err(N2WError::Fallback("kwargs".into())),
};
self.to_cardinal_z(value, zero)
}
fn to_cardinal_float_kw(
&self,
value: &FloatValue,
precision_override: Option<u32>,
kw: &Kwargs,
) -> Result<String> {
if !kw.only(&["zero"]) {
return Err(N2WError::Fallback("kwargs".into()));
}
let zero = match kw.get("zero") {
None => ZERO,
Some(KwVal::Str(s)) => s.as_str(),
Some(KwVal::None) => {
if value.as_whole_int().map_or(false, |i| i.is_zero()) {
return Err(N2WError::ReturnsNone);
}
ZERO
}
Some(_) => return Err(N2WError::Fallback("kwargs".into())),
};
self.hu_cardinal_any(value, precision_override, zero)
}
fn to_year_kw(&self, value: &BigInt, kw: &Kwargs) -> Result<String> {
if !kw.only(&["suffix", "longval"]) {
return Err(N2WError::Fallback("kwargs".into()));
}
let suffix = match kw.get("suffix") {
None | Some(KwVal::None) => None,
Some(KwVal::Str(s)) => Some(s.as_str()),
Some(_) => return Err(N2WError::Fallback("kwargs".into())),
};
let mut val = value.clone();
let mut prefix = String::new();
if val.is_negative() || suffix.is_some() {
val = val.abs();
prefix = match suffix {
Some(s) => format!("{} ", s),
None => "i. e. ".to_string(),
};
}
Ok(format!("{}{}", prefix, self.to_cardinal(&val)?))
}
fn lang_name(&self) -> &str {
"Num2Word_HU"
}
fn currency_forms(&self, code: &str) -> Option<&CurrencyForms> {
self.currency_forms.get(code)
}
fn currency_adjective(&self, code: &str) -> Option<&str> {
Some(match code {
"AUD" => "Australian",
"BYN" => "Belarusian",
"CAD" => "Canadian",
"EEK" => "Estonian",
"HUF" => "Hungarian",
"INR" => "Indian",
"ISK" => "íslenskar",
"JPY" => "Japanese",
"KRW" => "Korean",
"MXN" => "Mexican",
"NOK" => "Norwegian",
"RON" => "Romanian",
"RUB" => "Russian",
"SAR" => "Saudi",
"USD" => "US",
"UZS" => "Uzbekistan",
_ => return None,
})
}
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()))
}
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 v = match val {
CurrencyValue::Int(v) => v,
CurrencyValue::Decimal { value: _, .. } => {
return crate::currency::default_to_currency(
self, val, currency, cents, separator, adjective,
)
}
};
let cr1 = match self.currency_forms.get(currency) {
Some(forms) => &forms.unit,
None => {
return crate::currency::default_to_currency(
self, val, currency, cents, separator, adjective,
)
}
};
let minus_str = if v.is_negative() { NEGWORD } else { "" };
let abs_val = v.abs();
let money_str = self.to_cardinal(&abs_val)?;
let index_err = || N2WError::Index("tuple index out of range".into());
let currency_str: &str = if abs_val.is_one() || cr1.len() <= 1 {
cr1.first().map(String::as_str).ok_or_else(index_err)?
} else {
cr1[1].as_str()
};
Ok(format!("{} {} {}", minus_str, money_str, currency_str)
.trim()
.to_string())
}
fn to_cardinal_float(
&self,
value: &FloatValue,
_precision_override: Option<u32>,
) -> Result<String> {
if fv_lt_zero(value) {
return Ok(format!(
"{}{}",
NEGWORD,
self.to_cardinal_float(&fv_neg(value), None)?
));
}
let (left_int, right_int, right_len): (BigInt, BigInt, usize) = match value {
FloatValue::Float { value: f, .. } => {
let s = py_float_repr(*f);
let (left, right) = s.split_once('.').ok_or_else(|| {
N2WError::Value("not enough values to unpack (expected 2, got 1)".into())
})?;
let left_int = left.parse::<BigInt>().map_err(|_| {
N2WError::Value(format!(
"invalid literal for int() with base 10: '{}'",
left
))
})?;
let right_int = right.parse::<BigInt>().map_err(|_| {
N2WError::Value(format!(
"invalid literal for int() with base 10: '{}'",
right
))
})?;
(left_int, right_int, right.chars().count())
}
FloatValue::Decimal { precision, .. } => {
let (pre, post) = float2tuple(value);
(pre, post, *precision as usize)
}
};
let card_word = self.card(&pow10(right_len as u32))?; let ord_word = PARTIAL_ORDS
.iter()
.find(|&&(k, _)| k == card_word.as_str())
.map(|&(_, v)| v)
.ok_or_else(|| N2WError::Key(format!("'{}'", card_word)))?;
Ok(format!(
"{} egész {} {}",
self.to_cardinal(&left_int)?,
self.to_cardinal(&right_int)?,
ord_word
))
}
}
pub fn new() -> LangHu {
LangHu::new()
}