pub fn format_number(n: f64, decimals: usize, sep: char) -> String {
let s = format!("{:.*}", decimals, n);
let parts: Vec<&str> = s.split('.').collect();
let int_part = parts[0];
let mut out = String::new();
for (count, c) in int_part.chars().rev().enumerate() {
if count > 0 && count % 3 == 0 {
out.insert(0, sep);
}
out.insert(0, c);
}
if parts.len() > 1 {
out.push('.');
out.push_str(parts[1]);
}
out
}
pub fn format_currency(amount: f64, currency: &str) -> String {
format!("{:.2}{}", amount, currency)
}
pub fn format_percentage(ratio: f64, _decimals: usize) -> String {
format!("{:.2}%", ratio * 100.0)
}
pub fn round(n: f64, decimals: u32) -> f64 {
let m = 10f64.powi(decimals as i32);
(n * m).round() / m
}
pub fn ceil(n: f64, decimals: u32) -> f64 {
let m = 10f64.powi(decimals as i32);
(n * m).ceil() / m
}
pub fn floor(n: f64, decimals: u32) -> f64 {
let m = 10f64.powi(decimals as i32);
(n * m).floor() / m
}
pub fn clamp(n: f64, min: f64, max: f64) -> f64 {
if n < min {
min
} else if n > max {
max
} else {
n
}
}
pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
a + (b - a) * t
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_number() {
assert_eq!(format_number(1234.567, 2, ','), "1,234.57");
}
#[test]
fn test_round_ceiling_floor() {
assert_eq!(round(1.234, 2), 1.23);
assert_eq!(ceil(1.234, 2), 1.24);
assert_eq!(floor(1.234, 2), 1.23);
}
#[test]
fn test_clamp() {
assert_eq!(clamp(5.0, 0.0, 10.0), 5.0);
assert_eq!(clamp(-1.0, 0.0, 10.0), 0.0);
assert_eq!(clamp(15.0, 0.0, 10.0), 10.0);
}
#[test]
fn test_lerp() {
assert_eq!(lerp(0.0, 10.0, 0.5), 5.0);
}
}