unit-intervals 0.1.0

Constrained f32/f64 wrapper types for normalized [0, 1] and [-1, 1] values.
Documentation
#![cfg(feature = "rand_distr")]

use rand::{RngExt, SeedableRng, rngs::StdRng};
use rand_distr::{Beta, Distribution, Open01, OpenClosed01, StandardUniform};
use unit_intervals::{
    SignedUnitInterval, UnitInterval,
    random::{
        CheckedSignedUnitIntervalDistribution, CheckedUnitIntervalDistribution,
        SaturatingSignedUnitIntervalDistribution, SaturatingUnitIntervalDistribution,
    },
};

struct Fixed<T>(T);

impl<T: Copy> Distribution<T> for Fixed<T> {
    fn sample<R: rand::Rng + ?Sized>(&self, _rng: &mut R) -> T {
        self.0
    }
}

#[test]
fn standard_uniform_samples_unit_interval_values() {
    let mut rng = StdRng::seed_from_u64(42);

    for _ in 0..1024 {
        let value: UnitInterval<f32> = StandardUniform.sample(&mut rng);

        assert!(UnitInterval::<f32>::contains(value.get()));
    }
}

#[test]
fn bounded_float_distributions_sample_unit_interval_values() {
    let mut rng = StdRng::seed_from_u64(42);
    let beta = Beta::new(2.0_f64, 5.0).unwrap();

    for _ in 0..1024 {
        let open: UnitInterval<f32> = Open01.sample(&mut rng);
        let open_closed: UnitInterval<f64> = OpenClosed01.sample(&mut rng);
        let beta_value: UnitInterval<f64> = beta.sample(&mut rng);

        assert!(UnitInterval::<f32>::contains(open.get()));
        assert!(UnitInterval::<f64>::contains(open_closed.get()));
        assert!(UnitInterval::<f64>::contains(beta_value.get()));
    }
}

#[test]
fn standard_uniform_samples_signed_unit_interval_values() {
    let mut rng = StdRng::seed_from_u64(42);

    for _ in 0..1024 {
        let value: SignedUnitInterval<f64> = StandardUniform.sample(&mut rng);

        assert!(SignedUnitInterval::<f64>::contains(value.get()));
    }
}

#[test]
fn rng_random_uses_standard_uniform_impls() {
    let mut rng = StdRng::seed_from_u64(42);

    let unit: UnitInterval<f64> = rng.random();
    let signed: SignedUnitInterval<f32> = rng.random();

    assert!(UnitInterval::<f64>::contains(unit.get()));
    assert!(SignedUnitInterval::<f32>::contains(signed.get()));
}

#[test]
fn checked_adapters_defer_to_inner_distribution_and_reject_invalid_samples() {
    let mut rng = StdRng::seed_from_u64(42);
    let valid_unit = CheckedUnitIntervalDistribution::new(Fixed(0.25_f32));
    let invalid_unit = CheckedUnitIntervalDistribution::new(Fixed(1.25_f32));
    let valid_signed = CheckedSignedUnitIntervalDistribution::new(Fixed(-0.25_f64));
    let invalid_signed = CheckedSignedUnitIntervalDistribution::new(Fixed(-1.25_f64));

    assert_eq!(
        valid_unit.sample(&mut rng),
        Some(UnitInterval::new(0.25).unwrap())
    );
    assert_eq!(invalid_unit.sample(&mut rng), None);
    assert_eq!(
        valid_signed.sample(&mut rng),
        Some(SignedUnitInterval::new(-0.25).unwrap())
    );
    assert_eq!(invalid_signed.sample(&mut rng), None);
}

#[test]
fn saturating_adapters_defer_to_inner_distribution_and_clamp_invalid_samples() {
    let mut rng = StdRng::seed_from_u64(42);
    let low_unit = SaturatingUnitIntervalDistribution::new(Fixed(-0.25_f32));
    let high_unit = SaturatingUnitIntervalDistribution::new(Fixed(1.25_f32));
    let low_signed = SaturatingSignedUnitIntervalDistribution::new(Fixed(-1.25_f64));
    let high_signed = SaturatingSignedUnitIntervalDistribution::new(Fixed(1.25_f64));

    assert_eq!(low_unit.sample(&mut rng), UnitInterval::ZERO);
    assert_eq!(high_unit.sample(&mut rng), UnitInterval::ONE);
    assert_eq!(low_signed.sample(&mut rng), SignedUnitInterval::NEG_ONE);
    assert_eq!(high_signed.sample(&mut rng), SignedUnitInterval::ONE);
}