physical-units 0.1.0

A library for working with values that have physical units in a simple sound way.
Documentation
use crate::{base, derived, identities::BASIC_IDENTITIES};

impl From<base::BaseUnit> for derived::DerivedUnit {
    fn from(value: base::BaseUnit) -> Self {
        value.to_derived()
    }
}

impl base::BaseUnit {
    pub const fn to_derived(self) -> derived::DerivedUnit {
        derived::DerivedUnit {
            base: self,
            ..derived::UNITLESS
        }
    }
}

impl From<derived::DerivedUnit> for base::BaseUnit {
    fn from(value: derived::DerivedUnit) -> Self {
        value.to_base()
    }
}

impl derived::DerivedUnit {
    pub fn to_base(self) -> base::BaseUnit {
        let mut output = self.base;
        let derived_units = [
            (self.hertz, base::HERTZ),
            (self.newton, base::NEWTON),
            (self.pascal, base::PASCAL),
            (self.joule, base::JOULE),
            (self.watt, base::WATT),
            (self.coulomb, base::COULOMB),
            (self.volt, base::VOLT),
            (self.farad, base::FARAD),
            (self.ohm, base::OHM),
            (self.siemens, base::SIEMENS),
            (self.weber, base::WEBER),
            (self.tesla, base::TESLA),
            (self.henry, base::HENRY),
            (self.lux, base::LUX),
            (self.becquerel, base::BECQUEREL),
            (self.gray, base::GRAY),
            (self.sievert, base::SIEVERT),
            (self.katal, base::KATAL),
        ];
        for (n, base) in derived_units {
            if n > 0 {
                for _ in 0..n {
                    output = output.multiply(base);
                }
            } else {
                for _ in 0..-n {
                    output = output.divide(base);
                }
            }
        }
        output
    }
}

impl<Number> From<base::BaseValue<Number>> for derived::DerivedValue<Number> {
    fn from(value: base::BaseValue<Number>) -> Self {
        value.to_derived()
    }
}

impl<Number> base::BaseValue<Number> {
    pub fn to_derived(self) -> derived::DerivedValue<Number> {
        derived::DerivedValue {
            unit: self.unit.to_derived(),
            number: self.number,
        }
    }
}

impl<Number> From<derived::DerivedValue<Number>> for base::BaseValue<Number> {
    fn from(value: derived::DerivedValue<Number>) -> Self {
        value.to_base()
    }
}

impl<Number> derived::DerivedValue<Number> {
    pub fn to_base(self) -> base::BaseValue<Number> {
        base::BaseValue {
            unit: self.unit.to_base(),
            number: self.number,
        }
    }
}

impl derived::DerivedUnit {
    pub fn simplify(self) -> Self {
        let mut output = self;

        for identity in BASIC_IDENTITIES {
            let after_mul = output.multiply(identity);
            let after_div = output.divide(identity);
            if after_mul.magnitude() < output.magnitude() {
                let mut after_mul = after_mul;
                let mut after_mul_mag = after_mul.magnitude();
                loop {
                    let next = after_mul.multiply(identity);
                    let next_mag = next.magnitude();
                    if next_mag >= after_mul_mag {
                        break;
                    }
                    after_mul = next;
                    after_mul_mag = next_mag;
                }
                output = after_mul;
            } else if after_div.magnitude() < output.magnitude() {
                let mut after_div = after_div;
                let mut after_div_mag = after_div.magnitude();
                loop {
                    let next = after_div.divide(identity);
                    let next_mag = next.magnitude();
                    if next_mag >= after_div_mag {
                        break;
                    }
                    after_div = next;
                    after_div_mag = next_mag;
                }
                output = after_div;
            }
        }

        output
    }
}

#[cfg(test)]
mod tests {
    use crate::{base, derived};

    #[test]
    fn test_base_derived_conversions() {
        assert_eq!(base::HERTZ, derived::HERTZ.into());
        assert_eq!(base::NEWTON, derived::NEWTON.into());
        assert_eq!(base::PASCAL, derived::PASCAL.into());
        assert_eq!(base::JOULE, derived::JOULE.into());
        assert_eq!(base::WATT, derived::WATT.into());
        assert_eq!(base::COULOMB, derived::COULOMB.into());
        assert_eq!(base::VOLT, derived::VOLT.into());
        assert_eq!(base::FARAD, derived::FARAD.into());
        assert_eq!(base::OHM, derived::OHM.into());
        assert_eq!(base::SIEMENS, derived::SIEMENS.into());
        assert_eq!(base::WEBER, derived::WEBER.into());
        assert_eq!(base::TESLA, derived::TESLA.into());
        assert_eq!(base::HENRY, derived::HENRY.into());
        assert_eq!(base::LUX, derived::LUX.into());
        assert_eq!(base::BECQUEREL, derived::BECQUEREL.into());
        assert_eq!(base::GRAY, derived::GRAY.into());
        assert_eq!(base::SIEVERT, derived::SIEVERT.into());
        assert_eq!(base::KATAL, derived::KATAL.into());
    }

    #[test]
    fn test_derive() {
        let base_joule =
            base::KILOGRAM * (base::METER * base::METER) / (base::SECOND * base::SECOND);
        let derived_joule = base_joule.to_derived().simplify();
        assert_eq!(
            derived::JOULE,
            derived_joule,
            "{} != {}",
            derived::JOULE,
            derived_joule
        );

        let base_joule =
            base::KILOGRAM * (base::METER * base::METER * base::METER) / (base::SECOND * base::SECOND);
        let actual = base_joule.to_derived().simplify();
        let expected = derived::JOULE * derived::METER;
        assert_eq!(
            expected,
            actual,
            "{} != {}",
            expected,
            actual
        );
    }
}