crying 0.0.1

linear algebra utility library with generic matrix and vector types
Documentation
use num_traits::{Float, Num, NumCast, identities};
use rand::{RngCore, distr::Distribution};
use std::ops;

pub trait Scalar:
    Copy
    + Num
    + NumCast
    + PartialOrd
    + ops::AddAssign
    + ops::SubAssign
    + ops::DivAssign
    + ops::MulAssign
{
    fn zero() -> Self {
        identities::zero()
    }
    fn one() -> Self {
        identities::one()
    }

    const MIN: Self;
    const MAX: Self;
}

impl Scalar for f32 {
    const MIN: Self = Self::NEG_INFINITY;
    const MAX: Self = Self::INFINITY;
}
impl Scalar for f64 {
    const MIN: Self = Self::NEG_INFINITY;
    const MAX: Self = Self::INFINITY;
}

impl Scalar for u8 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for i8 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for u16 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for i16 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for u32 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for i32 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for u64 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for i64 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for u128 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for i128 {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for usize {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}
impl Scalar for isize {
    const MIN: Self = Self::MIN;
    const MAX: Self = Self::MAX;
}

pub trait ScalarFloat: Scalar + Float {}
impl<T: Scalar + Float> ScalarFloat for T {}

pub struct Interval<S: Scalar>(pub S, pub S);
impl<S: Scalar> Interval<S> {
    pub fn empty() -> Self {
        Self(S::MAX, S::MIN)
    }
    pub fn maximal() -> Self {
        Self(S::MIN, S::MAX)
    }
    pub fn unit() -> Self {
        Self(<S as Scalar>::zero(), <S as Scalar>::one())
    }

    // functions to check whether a value is within the interval
    pub fn contains(&self, value: S) -> bool {
        self.0 <= value && value < self.1
    }
    pub fn open_contains(&self, value: S) -> bool {
        self.0 < value && value < self.1
    }
    pub fn closed_contains(&self, value: S) -> bool {
        self.0 <= value && value <= self.1
    }
    pub fn openclosed_contains(&self, value: S) -> bool {
        self.0 < value && value <= self.1
    }
}
impl<S: RandFloat> Interval<S> {
    pub fn random<R: RngCore + ?Sized>(&self, r: &mut R) -> S {
        S::sample_ab(r, self.0, self.1)
    }
    pub fn random_open<R: RngCore + ?Sized>(&self, r: &mut R) -> S {
        S::sample_ab_open(r, self.0, self.1)
    }
    pub fn random_closed<R: RngCore + ?Sized>(&self, r: &mut R) -> S {
        S::sample_ab_closed(r, self.0, self.1)
    }
    pub fn random_openclosed<R: RngCore + ?Sized>(&self, r: &mut R) -> S {
        S::sample_ab_openclosed(r, self.0, self.1)
    }
}

/// Floating-point scalars which can be randomly generated
pub trait RandFloat: ScalarFloat {
    fn sample_from<R: RngCore + ?Sized>(r: &mut R, d: &impl Distribution<Self>) -> Self {
        Self::from(d.sample(r)).unwrap()
    }

    /// Sample uniformly from [0,1)
    fn sample01<R: RngCore + ?Sized>(r: &mut R) -> Self;
    /// Sample uniformly from (0, 1)
    fn sample01_open<R: RngCore + ?Sized>(r: &mut R) -> Self;
    /// Sample uniformly from [0, 1]
    fn sample01_closed<R: RngCore + ?Sized>(r: &mut R) -> Self;
    /// Sample uniformly from (0,1]
    fn sample01_openclosed<R: RngCore + ?Sized>(r: &mut R) -> Self {
        <Self as Scalar>::one() - Self::sample01(r)
    }

    /// Sample uniformly from [a, b)
    fn sample_ab<R: RngCore + ?Sized>(r: &mut R, a: Self, b: Self) -> Self {
        (b - a) * (a + Self::sample01(r))
    }
    /// Sample uniformly from (a, b)
    fn sample_ab_open<R: RngCore + ?Sized>(r: &mut R, a: Self, b: Self) -> Self {
        (b - a) * (a + Self::sample01_open(r))
    }
    /// Sample uniformly from [a, b]
    fn sample_ab_closed<R: RngCore + ?Sized>(r: &mut R, a: Self, b: Self) -> Self {
        (b - a) * (a + Self::sample01_closed(r))
    }
    /// Sample uniformly from (a, b]
    fn sample_ab_openclosed<R: RngCore + ?Sized>(r: &mut R, a: Self, b: Self) -> Self {
        (b - a) * (a + Self::sample01_openclosed(r))
    }
}
impl RandFloat for f32 {
    fn sample_from<R: RngCore + ?Sized>(r: &mut R, d: &impl Distribution<Self>) -> Self {
        d.sample(r) as Self
    }
    fn sample01<R: RngCore + ?Sized>(r: &mut R) -> Self {
        Self::sample_from(r, &rand::distr::StandardUniform {})
    }
    fn sample01_open<R: RngCore + ?Sized>(r: &mut R) -> Self {
        Self::sample_from(r, &rand::distr::Open01 {})
    }
    fn sample01_closed<R: RngCore + ?Sized>(r: &mut R) -> Self {
        let d =
            rand::distr::Uniform::new_inclusive(<Self as Scalar>::zero(), <Self as Scalar>::one())
                .unwrap();
        Self::sample_from(r, &d)
    }
}
impl RandFloat for f64 {
    fn sample_from<R: RngCore + ?Sized>(r: &mut R, d: &impl Distribution<Self>) -> Self {
        d.sample(r) as Self
    }
    fn sample01<R: RngCore + ?Sized>(r: &mut R) -> Self {
        Self::sample_from(r, &rand::distr::StandardUniform {})
    }
    fn sample01_open<R: RngCore + ?Sized>(r: &mut R) -> Self {
        Self::sample_from(r, &rand::distr::Open01 {})
    }
    fn sample01_closed<R: RngCore + ?Sized>(r: &mut R) -> Self {
        let d =
            rand::distr::Uniform::new_inclusive(<Self as Scalar>::zero(), <Self as Scalar>::one())
                .unwrap();
        Self::sample_from(r, &d)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    pub fn mock_rng() -> rand::rngs::mock::StepRng {
        rand::rngs::mock::StepRng::new(1, 1)
    }

    // this one just tests that NumCast does what I expect when converting from
    // f64 to f32
    #[test]
    pub fn convert_f64_to_f32() {
        let max_f = std::f64::MAX;
        <f32 as NumCast>::from(max_f).unwrap();
        let small_f = std::f64::EPSILON;
        <f32 as NumCast>::from(small_f).unwrap();
    }

    #[test]
    pub fn test_uniform_01_sampling_f64() {
        let mut r = mock_rng();
        for _ in 0..500 {
            let value = f64::sample01(&mut r);
            assert!(0.0 <= value && value < 1.0);

            let value = f64::sample01_open(&mut r);
            assert!(0.0 < value && value < 1.0);

            let value = f64::sample01_closed(&mut r);
            assert!(0.0 <= value && value <= 1.0);

            let value = f64::sample01_openclosed(&mut r);
            assert!(0.0 < value && value <= 1.0);
        }
    }
}