unitforge 0.4.0

A library for unit and quantity consistent computations in Rust
Documentation
use num_traits::Zero;
use unitforge::quantities::{Distance, DistanceUnit, Force, ForceUnit};
use unitforge::{MAX_ABS_QUANTITY_POWER, PhysicsQuantity};

#[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.0);
}

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

#[test]
fn optimize_small() {
    let mut x = Force::from_exponential(1e-10, 0);
    x.optimize();
    assert!(x.get_multiplier().log10().round() == 0.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::from_exponential(1.0e-20, 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::from_exponential(1.0e-20, 20);
    force.optimize();
    assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
    assert!(!force.is_zero());
}

#[test]
fn optimize_zero() {
    let mut force = Force::zero();
    force.optimize();
    assert_eq!(force, 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::from_exponential(1.0, 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_negative_infinity_when_upper_bound_is_reached() {
    let mut force = Force::from_exponential(-1.0, MAX_ABS_QUANTITY_POWER);
    force.optimize();
    assert!(force.get_multiplier().is_infinite());
    assert!(force.get_multiplier().is_sign_negative());
    assert_eq!(force.get_power(), 0);
}

#[test]
fn optimize_saturates_to_zero_when_lower_bound_is_reached() {
    let mut force = Force::from_exponential(1.0, -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 subtraction_with_large_powers_does_not_overflow() {
    let lhs = Distance::from_exponential(1.0, i32::MAX);
    let rhs = Distance::from_exponential(1.0, i32::MAX);
    let diff = lhs - rhs;

    assert_eq!(diff.to(DistanceUnit::m), 0.0);
}