use core::fmt;
use crate::common::numeric::NumericValue;
use super::NumberOptions;
pub fn format_number<L: crate::locale::Locale>(
f: &mut fmt::Formatter<'_>,
value: NumericValue,
options: &NumberOptions<L>,
) -> fmt::Result {
let raw = to_f64(value);
let negative = raw.is_sign_negative();
let abs = raw.abs();
let (scaled, idx) = normalize_scaled(abs, options.precision_value());
let rendered = render_scaled(
scaled,
options.precision_value(),
options.separators_value(),
);
if negative {
write!(f, "-")?;
}
write!(f, "{rendered}")?;
let suffix = options
.locale_ref()
.compact_suffix(idx, options.long_units_value());
write!(f, "{suffix}")
}
fn to_f64(value: NumericValue) -> f64 {
match value {
NumericValue::Int(v) => v as f64,
NumericValue::UInt(v) => v as f64,
NumericValue::Float(v) => v,
}
}
fn normalize_scaled(value: f64, precision: u8) -> (f64, usize) {
let mut scaled = value;
let mut idx = 0;
while scaled >= 1_000.0 && idx < 4 {
scaled /= 1_000.0;
idx += 1;
}
scaled = round_to(scaled, precision);
if scaled >= 1_000.0 && idx < 4 {
scaled /= 1_000.0;
idx += 1;
}
(scaled, idx)
}
fn round_to(value: f64, precision: u8) -> f64 {
let factor = 10_f64.powi(precision as i32);
(value * factor).round() / factor
}
fn render_scaled(value: f64, precision: u8, separators: bool) -> alloc::string::String {
let mut out = if value.fract() == 0.0 {
alloc::format!("{:.0}", value)
} else {
alloc::format!("{:.*}", precision as usize, value)
};
trim_trailing_zeroes(&mut out);
if separators {
out = add_separators(&out);
}
out
}
fn trim_trailing_zeroes(s: &mut alloc::string::String) {
if !s.contains('.') {
return;
}
while s.ends_with('0') {
s.pop();
}
if s.ends_with('.') {
s.pop();
}
}
fn add_separators(input: &str) -> alloc::string::String {
let mut split = input.split('.');
let int_part = split.next().unwrap_or("");
let frac_part = split.next();
let mut out = alloc::string::String::new();
let chars: alloc::vec::Vec<char> = int_part.chars().rev().collect();
for (i, ch) in chars.iter().enumerate() {
if i != 0 && i % 3 == 0 {
out.push(',');
}
out.push(*ch);
}
let mut int_done: alloc::string::String = out.chars().rev().collect();
if let Some(frac) = frac_part {
int_done.push('.');
int_done.push_str(frac);
}
int_done
}