nexus-stats-core 1.2.0

Core types and utilities shared across nexus-stats subcrates
Documentation
/// Validates that a float value is finite (not NaN, not Inf).
/// Returns `Err(DataError)` with the appropriate variant if invalid.
/// Used at the top of every update method that accepts float input.
macro_rules! check_finite {
    ($val:expr) => {
        if !$val.is_finite() {
            return Err(if $val.is_nan() {
                crate::DataError::NotANumber
            } else {
                crate::DataError::Infinite
            });
        }
    };
}

/// Validates that a float value is finite (not NaN, not Inf).
///
/// Returns `Err(DataError::NotANumber)` for NaN, `Err(DataError::Infinite)`
/// for infinity, `Ok(())` for finite values.
#[doc(hidden)]
#[inline]
pub fn check_finite(val: f64) -> Result<(), crate::DataError> {
    if !val.is_finite() {
        return Err(if val.is_nan() {
            crate::DataError::NotANumber
        } else {
            crate::DataError::Infinite
        });
    }
    Ok(())
}

/// f32 variant of [`check_finite`].
#[doc(hidden)]
#[inline]
pub fn check_finite_f32(val: f32) -> Result<(), crate::DataError> {
    if !val.is_finite() {
        return Err(if val.is_nan() {
            crate::DataError::NotANumber
        } else {
            crate::DataError::Infinite
        });
    }
    Ok(())
}

/// Square root.
///
/// Requires `std` or `libm` feature. Types using this (`std_dev()`,
/// `ShiryaevRoberts`) won't compile without one of these features.
#[doc(hidden)]
#[cfg(any(feature = "std", feature = "libm"))]
#[inline]
pub fn sqrt(x: f64) -> f64 {
    #[cfg(feature = "std")]
    {
        x.sqrt()
    }
    #[cfg(all(not(feature = "std"), feature = "libm"))]
    {
        libm::sqrt(x)
    }
}

/// Exponential function.
///
/// Requires `std` or `libm` feature. Types using this (`ShiryaevRoberts`,
/// `halflife()` constructors) won't compile without one of these features.
#[doc(hidden)]
#[cfg(any(feature = "std", feature = "libm"))]
#[inline]
pub fn exp(x: f64) -> f64 {
    #[cfg(feature = "std")]
    {
        x.exp()
    }
    #[cfg(all(not(feature = "std"), feature = "libm"))]
    {
        libm::exp(x)
    }
}

/// Natural logarithm.
///
/// Requires `std` or `libm` feature. Types using this (`EntropyF64`,
/// `TransferEntropyF64`) won't compile without one of these features.
#[doc(hidden)]
#[cfg(any(feature = "std", feature = "libm"))]
#[inline]
pub fn ln(x: f64) -> f64 {
    #[cfg(feature = "std")]
    {
        x.ln()
    }
    #[cfg(all(not(feature = "std"), feature = "libm"))]
    {
        libm::log(x)
    }
}

/// Trait providing `fma` (fused multiply-add) across all feature configurations.
///
/// With `std`: uses hardware FMA intrinsic.
/// With `libm`: uses `libm::fma` / `libm::fmaf`.
/// Without either: falls back to `a * b + c` (no fusion, but correct).
#[doc(hidden)]
pub trait MulAdd {
    /// Fused multiply-add: `self * b + c`.
    fn fma(self, b: Self, c: Self) -> Self;
}

impl MulAdd for f64 {
    #[inline]
    fn fma(self, b: f64, c: f64) -> f64 {
        #[cfg(feature = "std")]
        {
            self.mul_add(b, c)
        }
        #[cfg(all(not(feature = "std"), feature = "libm"))]
        {
            libm::fma(self, b, c)
        }
        #[cfg(not(any(feature = "std", feature = "libm")))]
        {
            self * b + c
        }
    }
}

impl MulAdd for f32 {
    #[inline]
    fn fma(self, b: f32, c: f32) -> f32 {
        #[cfg(feature = "std")]
        {
            self.mul_add(b, c)
        }
        #[cfg(all(not(feature = "std"), feature = "libm"))]
        {
            libm::fmaf(self, b, c)
        }
        #[cfg(not(any(feature = "std", feature = "libm")))]
        {
            self * b + c
        }
    }
}