use super::{provider::single_unit::SingleUnit, single_unit_vec::SingleUnitVec};
use alloc::string::String;
use core::fmt::Write;
#[derive(Debug, Eq, Clone)]
pub struct MeasureUnit {
pub id: Option<&'static str>,
pub(crate) single_units: SingleUnitVec,
pub(crate) constant_denominator: u64,
}
impl PartialEq for MeasureUnit {
fn eq(&self, other: &Self) -> bool {
self.single_units == other.single_units
&& self.constant_denominator == other.constant_denominator
}
}
impl MeasureUnit {
pub fn single_units(&self) -> &[SingleUnit] {
self.single_units.as_slice()
}
pub fn constant_denominator(&self) -> u64 {
self.constant_denominator
}
pub fn generate_short_representation(&self) -> String {
fn decompose_number_and_trailing_zeros(mut n: u64) -> (u64, u8) {
let mut zeros_count = 0;
for (divisor, zeros) in [(100_000_000, 8), (10_000, 4), (100, 2), (10, 1)] {
while n % divisor == 0 {
n /= divisor;
zeros_count += zeros;
}
}
(n, zeros_count)
}
fn append_power_of_10_to_scientific(input: u64, buff: &mut String) {
if input < 1000 {
let _infallible = write!(buff, "{input}");
return;
}
let (significant_digits, zeros_count) = decompose_number_and_trailing_zeros(input);
if zeros_count > 3 {
let _infallible = write!(buff, "{significant_digits}E{zeros_count}");
return;
}
let _infallible = write!(buff, "{input}");
}
let mut short_representation = String::new();
if self.constant_denominator > 0 {
short_representation.push('C');
append_power_of_10_to_scientific(self.constant_denominator, &mut short_representation);
}
self.single_units.as_slice().iter().for_each(|single_unit| {
single_unit.append_short_representation(&mut short_representation)
});
short_representation
}
}
#[cfg(test)]
mod tests {
use crate::measure::measureunit::MeasureUnit;
#[test]
fn test_generate_short_representation() {
let test_cases = vec![
("meter", "I85"),
("foot", "I50"),
("inch", "I66"),
("square-meter", "P2I85"),
("square-millimeter", "P2D-3I85"),
("micrometer", "D-6I85"),
("meter-per-second", "I85P-1I127"),
("liter-per-100-kilometer", "C100I82P-1D3I85"),
("portion-per-1e9", "C1E9I113"),
("per-10000000000-portion", "C1E10P-1I113"),
("liter-per-240000000000-kilometer", "C24E10I82P-1D3I85"),
("millimeter-per-square-microsecond", "D-3I85P-2D-6I127"),
];
for (full_unit, expected_short) in test_cases {
let measure_unit = MeasureUnit::try_from_str(full_unit).unwrap();
let short_representation = measure_unit.generate_short_representation();
assert_eq!(short_representation, expected_short, "{full_unit}");
}
}
}