unitforge 0.4.0

A library for unit and quantity consistent computations in Rust
Documentation
use crate::common::approx_eq;
use unitforge::{
    Distance, DistanceUnit, Force, ForceDistanceUnit, ForceUnit, Mass, MassUnit, Quantity,
    PhysicsQuantity, StiffnessUnit, Unit,
};

#[test]
fn add() {
    let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
    let b = Quantity::MassQuantity(Mass::new(10.0, MassUnit::kg));
    let c = (a + b).unwrap();

    assert!(approx_eq(
        c.to(Unit::MassUnit(MassUnit::kg)).unwrap(),
        22.0,
        1e-10
    ));
}

#[test]
fn add_fail() {
    let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
    let b = Quantity::FloatQuantity(3.0);
    assert!((a + b).is_err());
}

#[test]
fn sub() {
    let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
    let b = Quantity::MassQuantity(Mass::new(10.0, MassUnit::kg));
    let c = (a - b).unwrap();

    assert!(approx_eq(
        c.to(Unit::MassUnit(MassUnit::kg)).unwrap(),
        2.0,
        1e-10
    ));
}

#[test]
fn sub_fail() {
    let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
    let b = Quantity::FloatQuantity(3.0);
    assert!((a - b).is_err());
}

#[test]
fn mul() {
    let a = Quantity::ForceQuantity(Force::new(3.0, ForceUnit::N));
    let b = Quantity::DistanceQuantity(Distance::new(4.0, DistanceUnit::m));
    let c = (a * b).unwrap();

    assert!(approx_eq(
        c.to(Unit::ForceDistanceUnit(ForceDistanceUnit::Nm))
            .unwrap(),
        12.0,
        1e-10
    ));
}

#[test]
fn mul_with_float_quantity_is_commutative() {
    let scalar = Quantity::FloatQuantity(3.0);
    let distance = Quantity::DistanceQuantity(Distance::new(4.0, DistanceUnit::m));

    let left = (scalar * distance).unwrap();
    let right = (distance * scalar).unwrap();

    assert!(approx_eq(
        left.to(Unit::DistanceUnit(DistanceUnit::m)).unwrap(),
        12.0,
        1e-10
    ));
    assert_eq!(left, right);
}

#[test]
fn div() {
    let a = Quantity::ForceQuantity(Force::new(3.0, ForceUnit::N));
    let b = Quantity::DistanceQuantity(Distance::new(4.0, DistanceUnit::m));
    let c = (a / b).unwrap();

    assert!(approx_eq(
        c.to(Unit::StiffnessUnit(StiffnessUnit::N_m)).unwrap(),
        3.0 / 4.0,
        1e-10
    ));
}

#[test]
fn div_fail_for_unsupported_runtime_pair() {
    let mass = Quantity::MassQuantity(Mass::new(3.0, MassUnit::kg));
    let force = Quantity::ForceQuantity(Force::new(4.0, ForceUnit::N));
    assert!((mass / force).is_err());
}

#[test]
fn abs() {
    let value = Quantity::DistanceQuantity(Distance::new(-4.0, DistanceUnit::m));

    assert!(approx_eq(
        value.abs().to(Unit::DistanceUnit(DistanceUnit::m)).unwrap(),
        4.0,
        1e-10
    ));
}

#[test]
fn unit_name() {
    let unit = Unit::DistanceUnit(DistanceUnit::m);
    assert_eq!(unit.get_name(), "m");
}

#[test]
fn unit_to_quantity_wraps_runtime_value() {
    let quantity = Unit::DistanceUnit(DistanceUnit::m).to_quantity(2.5);

    assert!(approx_eq(
        quantity.to(Unit::DistanceUnit(DistanceUnit::m)).unwrap(),
        2.5,
        1e-10
    ));
}

#[test]
fn float_quantity_round_trips_through_no_unit() {
    let quantity = Unit::NoUnit.to_quantity(2.5);

    assert_eq!(quantity, Quantity::FloatQuantity(2.5));
    assert_eq!(quantity.to(Unit::NoUnit).unwrap(), 2.5);
}

#[test]
fn extract_float_only_works_for_float_quantity() {
    assert_eq!(Quantity::FloatQuantity(2.5).extract_float().unwrap(), 2.5);
    assert!(Quantity::DistanceQuantity(Distance::new(1.0, DistanceUnit::m))
        .extract_float()
        .is_err());
}

#[test]
fn to_with_mismatched_unit_returns_error() {
    let quantity = Quantity::MassQuantity(Mass::new(2.0, MassUnit::kg));
    assert!(quantity.to(Unit::DistanceUnit(DistanceUnit::m)).is_err());
}

#[test]
fn is_not_nan() {
    let quantity = Quantity::DistanceQuantity(Distance::new(-4.0, DistanceUnit::m));
    assert!(!quantity.is_nan());
}

#[test]
fn is_nan() {
    let quantity = Quantity::DistanceQuantity(Distance::new(f64::NAN, DistanceUnit::m));
    assert!(quantity.is_nan());
}

#[test]
fn float_quantity_sqrt_works() {
    let quantity = Quantity::FloatQuantity(9.0);
    assert_eq!(quantity.sqrt().unwrap().extract_float().unwrap(), 3.0);
}

#[test]
fn sqrt_rejects_non_square_quantity() {
    let quantity = Quantity::ForceQuantity(Force::new(9.0, ForceUnit::N));
    assert!(quantity.sqrt().is_err());
}

#[test]
fn inf_multiplication() {
    let distance = Quantity::DistanceQuantity(Distance::new(1.0, DistanceUnit::m));
    let inf = Quantity::FloatQuantity(f64::INFINITY);
    let res = (distance * inf).unwrap();
    let value = res.to(Unit::DistanceUnit(DistanceUnit::mm)).unwrap();

    assert!(value.is_infinite());
    assert!(value.is_sign_positive());
}

#[test]
fn neg_inf_multiplication() {
    let distance = Quantity::DistanceQuantity(Distance::new(1.0, DistanceUnit::m));
    let inf = Quantity::FloatQuantity(f64::NEG_INFINITY);
    let res = (distance * inf).unwrap();
    let value = res.to(Unit::DistanceUnit(DistanceUnit::mm)).unwrap();

    assert!(value.is_infinite());
    assert!(value.is_sign_negative());
}