848 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			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()
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |