unitforge 0.3.20

A library for unit and quantity consistent computations in Rust
Documentation
#![cfg(test)]
mod tests {
    use crate::*;
    use num_traits::Zero;

    #[test]
    fn test_new() {
        let input = 1000;
        let output = Distance::new(input.into(), DistanceUnit::mm);
        assert_eq!(output.get_tuple(), (1.0, 0));
        assert_eq!(output.to(DistanceUnit::mm), 1000.0);
    }

    #[test]
    fn optimize_on_add() {
        let f_1 = Force::new(10.0, ForceUnit::N);
        let f_2 = Force::new(1.0E12, ForceUnit::N);
        let f = f_1 + f_2;
        assert!(f.get_multiplier().log10().round() == 0.);
    }

    #[test]
    fn optimize_large() {
        let mut x = Force {
            multiplier: 1E10,
            power: 0,
        };
        x.optimize();
        assert!(x.get_multiplier().log10().round() == 0.);
        assert_eq!(x.to(ForceUnit::N), 1E10);
    }

    #[test]
    fn optimize_small() {
        let mut x = Force {
            multiplier: 1E-10,
            power: 0,
        };
        x.optimize();
        assert!(x.get_multiplier().log10().round() == 0.);
        assert_eq!(x.to(ForceUnit::N), 1E-10);
    }

    #[test]
    fn from_exponential_preserves_small_multiplier_with_large_positive_power() {
        let force = Force::from_exponential(1.0e-20, 20);
        assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
    }

    #[test]
    fn as_f64_preserves_denormalized_representation() {
        let force = Force {
            multiplier: 1.0e-20,
            power: 20,
        };
        assert!((force.as_f64() - 1.0).abs() < 1.0e-12);
        assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
    }

    #[test]
    fn optimize_normalizes_small_multiplier_instead_of_zeroing() {
        let mut force = Force {
            multiplier: 1.0e-20,
            power: 20,
        };
        force.optimize();
        assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
        assert!(!force.is_zero());
    }

    #[test]
    fn optimize_zero() {
        let mut f_1 = Force::zero();
        f_1.optimize();
        assert_eq!(f_1, Force::zero());
    }

    #[test]
    fn optimize_infinity_is_a_no_op() {
        let mut pos = Force::INFINITY;
        let mut neg = Force::NEG_INFINITY;
        pos.optimize();
        neg.optimize();
        assert!(pos.get_multiplier().is_infinite());
        assert!(neg.get_multiplier().is_infinite());
        assert!(pos.get_multiplier().is_sign_positive());
        assert!(neg.get_multiplier().is_sign_negative());
        assert_eq!(pos.get_power(), 0);
        assert_eq!(neg.get_power(), 0);
    }

    #[test]
    fn optimize_saturates_to_infinity_when_upper_bound_is_reached() {
        let mut force = Force {
            multiplier: 1.0,
            power: MAX_ABS_QUANTITY_POWER,
        };
        force.optimize();
        assert!(force.get_multiplier().is_infinite());
        assert!(force.get_multiplier().is_sign_positive());
        assert_eq!(force.get_power(), 0);
    }

    #[test]
    fn optimize_saturates_to_zero_when_lower_bound_is_reached() {
        let mut force = Force {
            multiplier: 1.0,
            power: -MAX_ABS_QUANTITY_POWER,
        };
        force.optimize();
        assert!(force.is_zero());
        assert_eq!(force.get_power(), 0);
    }

    #[test]
    fn scalar_mul_saturates_when_upper_bound_is_reached() {
        let force = Force::from_exponential(1.0, MAX_ABS_QUANTITY_POWER - 1) * 10.0;
        assert!(force.get_multiplier().is_infinite());
        assert!(force.get_multiplier().is_sign_positive());
        assert_eq!(force.get_power(), 0);
    }

    #[test]
    fn scalar_div_saturates_to_zero_when_lower_bound_is_reached() {
        let force = Force::from_exponential(1.0, -(MAX_ABS_QUANTITY_POWER - 1)) / 10.0;
        assert!(force.is_zero());
        assert_eq!(force.get_power(), 0);
    }

    #[test]
    fn add_and_sub_keep_normalized_power_within_bound() {
        let lhs = Force::from_exponential(1.0e-20, 20);
        let rhs = Force::from_exponential(5.0e-21, 20);

        let sum = lhs + rhs;
        assert!(sum.get_power().abs() < MAX_ABS_QUANTITY_POWER);
        assert!((sum.to(ForceUnit::N) - 1.5).abs() < 1.0e-12);

        let diff = lhs - rhs;
        assert!(diff.get_power().abs() < MAX_ABS_QUANTITY_POWER);
        assert!((diff.to(ForceUnit::N) - 0.5).abs() < 1.0e-12);
    }

    #[test]
    fn mul_with_self() {
        let a = Quantity::AreaQuantity(Area::new(10.0, AreaUnit::msq));
        let b = (a * a).unwrap();
        assert_eq!(
            b.to(Unit::AreaOfMomentUnit(AreaOfMomentUnit::mhc)).unwrap(),
            100.0
        );
    }

    #[test]
    fn sqrt() {
        let a = Quantity::AreaOfMomentQuantity(AreaOfMoment::new(25.0, AreaOfMomentUnit::mhc));
        let b = a.sqrt().unwrap();
        assert!((b.to(Unit::AreaUnit(AreaUnit::msq)).unwrap() - 5.0).abs() < 1E-10);
    }

    #[test]
    fn inverse_stress_from_stress_division() {
        let stress = Stress::new(2.0, StressUnit::Pa);
        let inverse = 1.0 / stress;
        assert!((inverse.to(InverseStressUnit::_Pa) - 0.5).abs() < 1E-12);
    }

    #[test]
    fn stress_from_inverse_stress_division() {
        let inverse = InverseStress::new(0.5, InverseStressUnit::_Pa);
        let stress = 1.0 / inverse;
        assert!((stress.to(StressUnit::Pa) - 2.0).abs() < 1E-12);
    }

    #[test]
    fn stress_squared_from_force_stress_over_area() {
        let force = Force::new(2.0, ForceUnit::N);
        let stress = Stress::new(3.0, StressUnit::Pa);
        let area = Area::new(6.0, AreaUnit::msq);
        let result = (force * stress) / area;
        assert!((result.to(StressSquaredUnit::Nsq_mmhc) - 1.0).abs() < 1E-12);
    }

    #[test]
    fn force_stress_from_stress_squared_times_area() {
        let stress_squared = StressSquared::new(2.0, StressSquaredUnit::Nsq_mmhc);
        let area = Area::new(3.0, AreaUnit::msq);
        let result = stress_squared * area;
        assert!((result.to(ForceStressUnit::Nsq_msq) - 6.0).abs() < 1E-12);
    }

    #[test]
    fn test_optimal_unit_with_selection() {
        let area = Area::new(0.0001, AreaUnit::msq);
        let optimal_unit = area
            .optimal_unit(
                vec![AreaUnit::kmsq, AreaUnit::cmsq, AreaUnit::nmsq]
                    .into_iter()
                    .collect::<std::collections::HashSet<_>>(),
            )
            .unwrap();
        assert_eq!(optimal_unit, AreaUnit::cmsq)
    }
}