unit-intervals 0.1.0

Constrained f32/f64 wrapper types for normalized [0, 1] and [-1, 1] values.
Documentation
use unit_intervals::{SignedUnitInterval, UnitInterval};

#[test]
fn constructors_accept_signed_unit_interval() {
    let default_width: SignedUnitInterval = SignedUnitInterval::new(-0.5).unwrap();

    assert_eq!(default_width.get(), -0.5);
    assert_eq!(
        SignedUnitInterval::<f32>::new(-1.0).map(|u| u.get()),
        Some(-1.0)
    );
    assert_eq!(
        SignedUnitInterval::<f32>::new(0.0).map(|u| u.get()),
        Some(0.0)
    );
    assert_eq!(
        SignedUnitInterval::<f32>::new(1.0).map(|u| u.get()),
        Some(1.0)
    );
    assert_eq!(SignedUnitInterval::<f32>::new(-1.1), None);
    assert_eq!(SignedUnitInterval::<f32>::new(1.1), None);
    assert_eq!(SignedUnitInterval::<f32>::new(f32::NAN), None);
}

#[test]
fn constants_conversions_and_helpers_work() {
    let unit = UnitInterval::new(0.25).unwrap();
    let signed = SignedUnitInterval::from(unit);
    let back_to_unit = UnitInterval::try_from(signed).unwrap();
    let negative = SignedUnitInterval::new(-0.25).unwrap();

    assert_eq!(
        SignedUnitInterval::<f32>::default(),
        SignedUnitInterval::ZERO
    );
    assert!(SignedUnitInterval::<f32>::NEG_ONE.is_neg_one());
    assert!(SignedUnitInterval::<f32>::ZERO.is_zero());
    assert!(SignedUnitInterval::<f32>::ONE.is_one());
    assert_eq!(SignedUnitInterval::<f32>::saturating(-1.25).get(), -1.0);
    assert_eq!(SignedUnitInterval::<f32>::saturating(1.25).get(), 1.0);
    assert_eq!(SignedUnitInterval::<f32>::saturating(f32::NAN).get(), 0.0);
    assert_eq!(signed.get(), 0.25);
    assert_eq!(back_to_unit, unit);
    assert!(UnitInterval::try_from(negative).is_err());
}

#[test]
fn checked_and_saturating_arithmetic_accept_unit_interval() {
    let negative = SignedUnitInterval::new(-0.75).unwrap();
    let positive = SignedUnitInterval::new(0.75).unwrap();
    let unit = UnitInterval::new(0.5).unwrap();

    assert_eq!(negative.min(unit), negative);
    assert_eq!(negative.max(unit).get(), 0.5);
    assert_eq!(negative.midpoint(unit).get(), -0.125);
    assert_eq!(negative.distance_to(unit), 1.25);
    assert_eq!(negative.checked_add(unit).unwrap().get(), -0.25);
    assert_eq!(positive.checked_add(unit), None);
    assert_eq!(positive.saturating_add(unit).get(), 1.0);
    assert_eq!(negative.checked_sub(unit), None);
    assert_eq!(negative.saturating_sub(unit).get(), -1.0);
    assert_eq!(positive.checked_div(unit), None);
    assert_eq!(positive.saturating_div(unit).get(), 1.0);
}

#[test]
fn constrained_results_return_constrained_types() {
    let negative = SignedUnitInterval::new(-0.5).unwrap();
    let positive = SignedUnitInterval::new(0.5).unwrap();
    let unit = UnitInterval::new(0.5).unwrap();

    let negated: SignedUnitInterval = -negative;
    let signed_product: SignedUnitInterval = negative * positive;
    let mixed_product: SignedUnitInterval = negative * unit;
    let reverse_mixed_product: SignedUnitInterval = unit * negative;

    assert_eq!(negated.get(), 0.5);
    assert_eq!(signed_product.get(), -0.25);
    assert_eq!(mixed_product.get(), -0.25);
    assert_eq!(reverse_mixed_product.get(), -0.25);
}

#[test]
fn unconstrained_operations_return_backing_float() {
    let negative = SignedUnitInterval::<f32>::new(-0.75).unwrap();
    let positive = SignedUnitInterval::<f32>::new(0.75).unwrap();
    let unit = UnitInterval::new(0.5).unwrap();

    let sum: f32 = positive + positive;
    let mixed_sum: f32 = positive + unit;
    let quotient: f32 = positive / negative;
    let distance: f32 = negative.distance_to(positive);

    assert_eq!(sum, 1.5);
    assert_eq!(mixed_sum, 1.25);
    assert_eq!(quotient, -1.0);
    assert_eq!(distance, 1.5);
}