asklyphe/unit_converter/src/length_units.rs

848 lines
26 KiB
Rust

/*
* unit_converter length_units.rs
* - definitions of length units
*
* Copyright (C) 2025 Real Microsoft, LLC
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::unit_defs::{BestUnit, ConvertTo, FromUnitName, MetricPrefix, UnitName};
use astro_float::{BigFloat, RoundingMode};
use once_cell::sync::Lazy;
use std::collections::BTreeMap;
pub const PRECISION: usize = 2048;
// length unit -> value in meters
pub static LENGTH_STORE: Lazy<BTreeMap<LengthUnit, BigFloat>> = Lazy::new(|| {
let mut store = BTreeMap::new();
store.insert(
LengthUnit::Meter(MetricPrefix::Pico),
BigFloat::from_f64(0.000000000001, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Nano),
BigFloat::from_f64(0.000000001, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Micro),
BigFloat::from_f64(0.000001, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Milli),
BigFloat::from_f64(0.001, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Centi),
BigFloat::from_f64(0.01, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Deci),
BigFloat::from_f64(0.1, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::None),
BigFloat::from_f64(1.0, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Deca),
BigFloat::from_f64(10.0, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Hecto),
BigFloat::from_f64(100.0, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Kilo),
BigFloat::from_f64(1000.0, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Mega),
BigFloat::from_f64(1_000_000.0, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Giga),
BigFloat::from_f64(1_000_000_000.0, PRECISION),
);
store.insert(
LengthUnit::Meter(MetricPrefix::Tera),
BigFloat::from_f64(1_000_000_000_000.0, PRECISION),
);
// inch is 2.54 cm
store.insert(
LengthUnit::Inch,
store
.get(&LengthUnit::Meter(MetricPrefix::Centi))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(2.54, PRECISION)),
);
// thou is 1/1000 inches
store.insert(
LengthUnit::Thou,
store
.get(&LengthUnit::Inch)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(1.0 / 1000.0, PRECISION)),
);
// foot is 12 inches
store.insert(
LengthUnit::Foot,
store
.get(&LengthUnit::Inch)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(12.0, PRECISION)),
);
// yard is 3 feet
store.insert(
LengthUnit::Yard,
store
.get(&LengthUnit::Foot)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(3.0, PRECISION)),
);
// mile is 5280 feet
store.insert(
LengthUnit::Mile,
store
.get(&LengthUnit::Foot)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(5280.0, PRECISION)),
);
// league is 3 miles
store.insert(
LengthUnit::League,
store
.get(&LengthUnit::Mile)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(3.0, PRECISION)),
);
// fathom is 2 yards
store.insert(
LengthUnit::Fathom,
store
.get(&LengthUnit::Yard)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(2.0, PRECISION)),
);
// nautical mile is 1852 m
store.insert(
LengthUnit::NauticalMile,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(1852.0, PRECISION)),
);
// chain is 22 yards
store.insert(
LengthUnit::Chain,
store
.get(&LengthUnit::Yard)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(22.0, PRECISION)),
);
// rod is 5 / 12 yards
store.insert(
LengthUnit::Rod,
store
.get(&LengthUnit::Yard)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(5.5, PRECISION)),
);
// earth radius is 6371 km
store.insert(
LengthUnit::EarthRadius,
store
.get(&LengthUnit::Meter(MetricPrefix::Kilo))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(6371.0, PRECISION)),
);
// lunar distance is 384402 km
store.insert(
LengthUnit::LunarDistance,
store
.get(&LengthUnit::Meter(MetricPrefix::Kilo))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(384402.0, PRECISION)),
);
// astronomical unit is 149597870700 m
store.insert(
LengthUnit::AstronomicalUnit,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(149597870700.0, PRECISION)),
);
// lightyear is 9460730472580.8 km
store.insert(
LengthUnit::LightYear,
store
.get(&LengthUnit::Meter(MetricPrefix::Kilo))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(9460730472580.8, PRECISION)),
);
// parsec is 30856775814671.9 km
store.insert(
LengthUnit::Parsec,
store
.get(&LengthUnit::Meter(MetricPrefix::Kilo))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(30856775814671.9, PRECISION)),
);
// hubble length is 14400000000 light years
store.insert(
LengthUnit::HubbleLength,
store
.get(&LengthUnit::LightYear)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(14400000000.0, PRECISION)),
);
// planck length is 0.000000000000000000000000000000000016163 meters
store.insert(
LengthUnit::PlanckLength,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(
0.000000000000000000000000000000000016163,
PRECISION,
)),
);
// cana is 1.57 meters
store.insert(
LengthUnit::Cana,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(1.57, PRECISION)),
);
// cubit is 52.92 cm
store.insert(
LengthUnit::Cubit,
store
.get(&LengthUnit::Meter(MetricPrefix::Centi))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(52.92, PRECISION)),
);
// rope is 6.096 meters
store.insert(
LengthUnit::Rope,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(6.096, PRECISION)),
);
// li is 500 meters
store.insert(
LengthUnit::Li,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(500.0, PRECISION)),
);
// pace is 0.75 meters
store.insert(
LengthUnit::Pace,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(0.75, PRECISION)),
);
// verst is 1.0668 km
store.insert(
LengthUnit::Verst,
store
.get(&LengthUnit::Meter(MetricPrefix::Kilo))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(1.0668, PRECISION)),
);
// double decker bus is 10 meters
store.insert(
LengthUnit::DoubleDeckerBus,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(10.0, PRECISION)),
);
// football field is 100 yards
store.insert(
LengthUnit::FootballField,
store
.get(&LengthUnit::Yard)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(100.0, PRECISION)),
);
// human hair thickness is 0.08 mm
store.insert(
LengthUnit::HumanHairThickness,
store
.get(&LengthUnit::Meter(MetricPrefix::Milli))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(0.08, PRECISION)),
);
// furlong is 1/8 mile
store.insert(
LengthUnit::Furlong,
store
.get(&LengthUnit::Mile)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(1.0 / 8.0, PRECISION)),
);
// horse is 2.4 meters
store.insert(
LengthUnit::HorseLength,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(2.4, PRECISION)),
);
// horizontal pitch is 5.08 mm
store.insert(
LengthUnit::HorizontalPitch,
store
.get(&LengthUnit::Meter(MetricPrefix::Milli))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(5.08, PRECISION)),
);
// hammer unit is 19.05 mm
store.insert(
LengthUnit::HammerUnit,
store
.get(&LengthUnit::Meter(MetricPrefix::Milli))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(19.05, PRECISION)),
);
// rack unit is 1.75 inches
store.insert(
LengthUnit::RackUnit,
store
.get(&LengthUnit::Inch)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(1.75, PRECISION)),
);
// hand is 4 inches
store.insert(
LengthUnit::Hand,
store
.get(&LengthUnit::Inch)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(4.0, PRECISION)),
);
// light nanosecond is 29.9792458 cm
store.insert(
LengthUnit::LightNanosecond,
store
.get(&LengthUnit::Meter(MetricPrefix::Centi))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(29.9792458, PRECISION)),
);
// metric foot is 300 mm
store.insert(
LengthUnit::MetricFoot,
store
.get(&LengthUnit::Meter(MetricPrefix::Milli))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(300.0, PRECISION)),
);
// boat length is 19 meters
store.insert(
LengthUnit::BoatLength,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(19.0, PRECISION)),
);
// block is 150 meters
store.insert(
LengthUnit::Block,
store
.get(&LengthUnit::Meter(MetricPrefix::None))
.unwrap()
.mul_full_prec(&BigFloat::from_f64(150.0, PRECISION)),
);
// siriometer is 15.8 light years
store.insert(
LengthUnit::Siriometer,
store
.get(&LengthUnit::LightYear)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(15.8, PRECISION)),
);
// banana is 8 inches
store.insert(
LengthUnit::Banana,
store
.get(&LengthUnit::Inch)
.unwrap()
.mul_full_prec(&BigFloat::from_f64(8.0, PRECISION)),
);
store
});
// length unit -> Vec<(name, plural)>
pub static LENGTH_NAMES: Lazy<BTreeMap<LengthUnit, Vec<(String, String)>>> = Lazy::new(|| {
let mut store = BTreeMap::new();
store.insert(
LengthUnit::Meter(MetricPrefix::Pico),
vec![("picometer", "picometers")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Nano),
vec![("nanometer", "nanometers")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Micro),
vec![("micrometer", "micrometers")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Milli),
vec![("millimeter", "millimeters"), ("mm", "mm")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Centi),
vec![("centimeter", "centimeters"), ("cm", "cm")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Deci),
vec![("decimeter", "decimeters")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::None),
vec![
("meter", "meters"),
("m", "m"),
("minecraft block", "minecraft blocks"),
],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Deca),
vec![("decameter", "decameters")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Hecto),
vec![("hectometer", "hectometers")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Kilo),
vec![("kilometer", "kilometers"), ("km", "km")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Mega),
vec![("megameter", "megameters")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Giga),
vec![("gigameter", "gigameters")],
);
store.insert(
LengthUnit::Meter(MetricPrefix::Tera),
vec![("terameter", "terameters")],
);
// inch is 25.4 m
store.insert(
LengthUnit::Inch,
vec![("inch", "inches"), ("in", "in"), ("\"", "\"")],
);
// thou is 1/1000 inches
store.insert(LengthUnit::Thou, vec![("thou", "thous")]);
// foot is 12 inches
store.insert(
LengthUnit::Foot,
vec![("foot", "feet"), ("ft", "ft"), ("'", "'ft'")],
);
// yard is 3 feet
store.insert(LengthUnit::Yard, vec![("yard", "yards"), ("yd", "yd")]);
// mile is 5280 feet
store.insert(LengthUnit::Mile, vec![("mile", "miles")]);
// league is 3 miles
store.insert(LengthUnit::League, vec![("league", "leagues")]);
// fathom is 2 yards
store.insert(LengthUnit::Fathom, vec![("fathom", "fathoms")]);
// nautical mile is 1852 m
store.insert(
LengthUnit::NauticalMile,
vec![("nautical mile", "nautical miles")],
);
// chain is 22 yards
store.insert(LengthUnit::Chain, vec![("chain", "chains")]);
// rod is 5 / 12 yards
store.insert(LengthUnit::Rod, vec![("rod", "rods")]);
// earth radius is 6371 km
store.insert(
LengthUnit::EarthRadius,
vec![("earth radius", "earth radii")],
);
// lunar distance is 384402 km
store.insert(
LengthUnit::LunarDistance,
vec![("lunar distance", "lunar distances")],
);
// astronomical unit is 149597870700 m
store.insert(
LengthUnit::AstronomicalUnit,
vec![("astronomical unit", "astronomical units")],
);
// lightyear is 9460730472580.8 km
store.insert(LengthUnit::LightYear, vec![("lightyear", "lightyears")]);
// parsec is 30856775814671.9 km
store.insert(LengthUnit::Parsec, vec![("parsec", "parsecs")]);
// hubble length is 14400000000 light years
store.insert(
LengthUnit::HubbleLength,
vec![("hubble length", "hubble lengths")],
);
// planck length is 0.000000000000000000000000000000000016163 meters
store.insert(
LengthUnit::PlanckLength,
vec![("planck length", "planck lengths")],
);
// cana is 1.57 meters
store.insert(LengthUnit::Cana, vec![("cana", "canas")]);
// cubit is 52.92 cm
store.insert(LengthUnit::Cubit, vec![("cubit", "cubits")]);
// rope is 6.096 meters
store.insert(LengthUnit::Rope, vec![("rope", "ropes")]);
// li is 500 meters
store.insert(LengthUnit::Li, vec![("li", "lis")]);
// pace is 0.75 meters
store.insert(LengthUnit::Pace, vec![("pace", "paces")]);
// verst is 1.0668 km
store.insert(LengthUnit::Verst, vec![("verst", "versts")]);
// double decker bus is 10 meters
store.insert(
LengthUnit::DoubleDeckerBus,
vec![("double decker bus", "double decker busses")],
);
// football field is 100 yards
store.insert(
LengthUnit::FootballField,
vec![("football field", "football fields")],
);
// human hair thickness is 0.08 mm
store.insert(
LengthUnit::HumanHairThickness,
vec![("human hair thickness", "human hair thicknesses")],
);
// furlong is 1/8 mile
store.insert(LengthUnit::Furlong, vec![("furlong", "furlongs")]);
// horse is 2.4 meters
store.insert(
LengthUnit::HorseLength,
vec![("horse", "horses"), ("horse length", "horse lengths")],
);
// horizontal pitch is 5.08 mm
store.insert(
LengthUnit::HorizontalPitch,
vec![("horizontal pitch", "horizontal pitches")],
);
// hammer unit is 19.05 mm
store.insert(
LengthUnit::HammerUnit,
vec![("hammer unit", "hammer units")],
);
// rack unit is 1.75 inches
store.insert(LengthUnit::RackUnit, vec![("rack unit", "rack units")]);
// hand is 4 inches
store.insert(LengthUnit::Hand, vec![("hand", "hands")]);
// light nanosecond is 29.9792458 cm
store.insert(
LengthUnit::LightNanosecond,
vec![("light nanosecond", "light nanoseconds")],
);
// metric foot is 300 mm
store.insert(LengthUnit::MetricFoot, vec![("metric foot", "metric feet")]);
// boat length is 19 meters
store.insert(
LengthUnit::BoatLength,
vec![("boat length", "boat lengths")],
);
// block is 150 meters
store.insert(LengthUnit::Block, vec![("block", "blocks")]);
// siriometer is 15.8 light years
store.insert(LengthUnit::Siriometer, vec![("siriometer", "siriometers")]);
// banana is 8 inches
store.insert(LengthUnit::Banana, vec![("banana", "bananas")]);
let mut store_string = BTreeMap::new();
for (k, v) in store {
store_string.insert(
k,
v.into_iter()
.map(|(s1, s2)| (s1.to_string(), s2.to_string()))
.collect(),
);
}
store_string
});
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug)]
pub enum LengthUnit {
Meter(MetricPrefix),
// 1/1000 inch
Thou,
// 25.4 mm
Inch,
// 12 inches
Foot,
// 3 feet
Yard,
// 5280 feet
Mile,
// 3 miles
League,
// 2 yards
Fathom,
// 1852 m
NauticalMile,
// 22 yards
Chain,
// 5 1/2 yards
Rod,
// 6371 km
EarthRadius,
// 384402 km
LunarDistance,
// 149597870700 m
AstronomicalUnit,
// 9460730472580.8 km
LightYear,
// 30856775814671.9 km
Parsec,
// 14400000000 light years
HubbleLength,
// 0.000000000000000000000000000000000016163 meters
PlanckLength,
// 1.57 meters
Cana,
// 52.92 cm
Cubit,
// 6.096 meters
Rope,
// 500 meters
Li,
// 0.75 meters
Pace,
// 1.0668 kilometers
Verst,
// 10 meters
DoubleDeckerBus,
// 100 yards
FootballField,
// 0.08 mm
HumanHairThickness,
// 1/8 mile
Furlong,
// 2.4 meters
HorseLength,
// 5.08 mm
HorizontalPitch,
// 19.05 mm
HammerUnit,
// 1.75 inches
RackUnit,
// 4 inches
Hand,
// 29.9792458 cm
LightNanosecond,
// 300 mm
MetricFoot,
// 19 meters
BoatLength,
// 150 meters
Block,
// 15.8 light years
Siriometer,
// 8 inches
Banana,
}
impl ConvertTo<LengthUnit> for LengthUnit {
// convert to meters:
// 1. find the desired type in the length store
// 2. multiply value by the type's meter value
fn convert_to(self, value: BigFloat, into: LengthUnit) -> BigFloat {
match self {
LengthUnit::Meter(prefix) => {
// unprefix
let denom = LENGTH_STORE.get(&LengthUnit::Meter(prefix)).unwrap();
let store_value = LENGTH_STORE.get(&into).unwrap();
value
.mul_full_prec(denom)
.div(store_value, PRECISION, RoundingMode::None)
}
_ => {
match into {
LengthUnit::Meter(prefix) => {
// unprefix
let denom = LENGTH_STORE.get(&LengthUnit::Meter(prefix)).unwrap();
let store_value = LENGTH_STORE.get(&self).unwrap();
value
.mul_full_prec(store_value)
.div(denom, PRECISION, RoundingMode::None)
}
_ => {
// convert to meters, then to desired value
let meters = self.convert_to(value, LengthUnit::Meter(MetricPrefix::None));
LengthUnit::Meter(MetricPrefix::None).convert_to(meters, into)
}
}
}
}
}
}
impl BestUnit for LengthUnit {
fn best_unit(self, value: BigFloat) -> Self {
let mut lowest_remainder = BigFloat::from_f64(f64::MAX, PRECISION);
let mut lowest_value = BigFloat::from_f64(f64::MAX, PRECISION);
let mut best_unit = LengthUnit::Meter(MetricPrefix::None);
for unit in LENGTH_STORE.keys() {
if *unit == self {
continue;
}
match self {
LengthUnit::Meter(prefix) => {
// convert to meters, then to desired value
let meters = self
.convert_to(value.clone(), LengthUnit::Meter(MetricPrefix::None));
let store_value = LENGTH_STORE.get(unit).unwrap();
let rem = meters.rem(store_value);
let div = meters.div(store_value, PRECISION, RoundingMode::None);
if rem.le(&lowest_remainder) && div.le(&lowest_value) && div.ge(&BigFloat::from_f64(1.0, PRECISION)) {
lowest_remainder = rem;
lowest_value = div;
best_unit = *unit;
}
}
_ => {
match unit {
LengthUnit::Meter(prefix) => {
// unprefix
let denom = LENGTH_STORE.get(&LengthUnit::Meter(*prefix)).unwrap();
let store_value = LENGTH_STORE.get(&self).unwrap();
let rem = value.mul_full_prec(store_value).rem(denom);
let div = value.mul_full_prec(store_value).div(
denom,
PRECISION,
RoundingMode::None,
);
if rem.le(&lowest_remainder) && div.le(&lowest_value) && div.ge(&BigFloat::from_f64(1.0, PRECISION)) {
lowest_remainder = rem;
lowest_value = div;
best_unit = *unit;
}
}
_ => {
// convert to meters, then to desired value
let meters = self
.convert_to(value.clone(), LengthUnit::Meter(MetricPrefix::None));
let store_value = LENGTH_STORE.get(unit).unwrap();
let rem = meters.rem(store_value);
let div = meters.div(store_value, PRECISION, RoundingMode::None);
if rem.le(&lowest_remainder) && div.le(&lowest_value) && div.ge(&BigFloat::from_f64(1.0, PRECISION)) {
lowest_remainder = rem;
lowest_value = div;
best_unit = *unit;
}
}
}
}
}
}
best_unit
}
}
impl FromUnitName<LengthUnit> for LengthUnit {
fn parse(value: String) -> Option<(LengthUnit, String)> {
let mut sorted_checklist: Vec<(String, String, LengthUnit)> = vec![];
for (unit, names) in LENGTH_NAMES.iter() {
for (single, plural) in names {
sorted_checklist.push((single.clone(), plural.clone(), *unit));
}
}
sorted_checklist.sort_by(|b, a| a.1.len().cmp(&b.1.len()));
let input = value.trim_start();
let diff = value.len() - input.len();
for (name, plural, unit) in sorted_checklist {
if input.to_lowercase().starts_with(&name) || input.to_lowercase().starts_with(&plural)
{
let len = if plural.len() > name.len() {
if input.to_lowercase().starts_with(&plural) {
plural.len()
} else {
name.len()
}
} else if input.to_lowercase().starts_with(&name) {
name.len()
} else {
plural.len()
};
return Some((unit, value[diff + len..].to_string()));
}
}
None
}
}
impl UnitName for LengthUnit {
fn unit_name(self, value: BigFloat) -> String {
if value == BigFloat::from_f64(1.0, PRECISION) {
// singular
LENGTH_NAMES.get(&self).unwrap().first().unwrap().0.clone()
} else {
// plural
LENGTH_NAMES.get(&self).unwrap().first().unwrap().1.clone()
}
}
}