scalars 0.3.1

Minimal numeric traits: Zero, One, Inv, Sqrt, Exp, Logarithm, Trigonometry, Real, Integer
Documentation
use crate::{Clamp, Exp, Inv, InverseTrigonometry, Logarithm, One, Sqrt, Trigonometry, Zero};

macro_rules! impl_float {
    ($type:ty, $sqrt:path, $exp:path, $exp2:path, $ln:path, $log2:path, $pow:path, $sin:path, $cos:path, $tan:path,
     $asin:path, $acos:path, $atan:path, $atan2:path) => {
        impl Zero for $type {
            fn zero() -> Self {
                0.0
            }
            fn is_zero(&self) -> bool {
                *self == 0.0
            }
        }

        impl One for $type {
            fn one() -> Self {
                1.0
            }
            fn is_one(&self) -> bool {
                *self == 1.0
            }
        }

        impl Inv for $type {
            type Output = Self;
            fn inv(self) -> Self {
                1.0 / self
            }
        }

        impl Sqrt for $type {
            fn sqrt(self) -> Self {
                $sqrt(self)
            }
        }

        impl Exp for $type {
            type Output = Self;
            fn exp(self) -> Self {
                $exp(self)
            }
            fn exp2(self) -> Self {
                $exp2(self)
            }
        }

        impl Logarithm for $type {
            fn ln(self) -> Self {
                $ln(self)
            }
            fn log2(self) -> Self {
                $log2(self)
            }
            fn powf(self, exponent: Self) -> Self {
                $pow(self, exponent)
            }
        }

        impl Trigonometry for $type {
            fn sin_cos(self) -> [Self; 2] {
                [$sin(self), $cos(self)]
            }

            fn tan(self) -> Self {
                $tan(self)
            }
        }

        impl InverseTrigonometry for $type {
            fn asin(self) -> Self {
                $asin(self)
            }
            fn acos(self) -> Self {
                $acos(self)
            }
            fn atan(self) -> Self {
                $atan(self)
            }
            fn atan2(self, other: Self) -> Self {
                $atan2(self, other)
            }
        }

        impl Clamp for $type {
            fn min(self, other: Self) -> Self {
                if self < other { self } else { other }
            }

            fn max(self, other: Self) -> Self {
                if self > other { self } else { other }
            }
        }
    };
}

impl_float!(
    f32,
    libm::sqrtf,
    libm::expf,
    libm::exp2f,
    libm::logf,
    libm::log2f,
    libm::powf,
    libm::sinf,
    libm::cosf,
    libm::tanf,
    libm::asinf,
    libm::acosf,
    libm::atanf,
    libm::atan2f
);

impl_float!(
    f64,
    libm::sqrt,
    libm::exp,
    libm::exp2,
    libm::log,
    libm::log2,
    libm::pow,
    libm::sin,
    libm::cos,
    libm::tan,
    libm::asin,
    libm::acos,
    libm::atan,
    libm::atan2
);

#[cfg(feature = "std")]
mod from_str_radix {
    use crate::FromStrRadix;

    fn digit(c: char, radix: u32) -> Option<u32> {
        let d = match c {
            '0'..='9' => c as u32 - '0' as u32,
            'a'..='z' => c as u32 - 'a' as u32 + 10,
            'A'..='Z' => c as u32 - 'A' as u32 + 10,
            _ => return None,
        };
        if d < radix { Some(d) } else { None }
    }

    fn parse_float<F: From<f32> + From<f64>>(source: &str, radix: u32) -> Result<F, core::num::ParseFloatError> {
        if radix == 10 {
            return source.parse::<f64>().map(F::from);
        }

        let s = source.strip_prefix('-').unwrap_or(source);
        let negative = s.len() < source.len();

        let (int_part, frac_part) = match s.split_once('.') {
            Some((i, f)) => (i, Some(f)),
            None => (s, None),
        };

        let mut value: f64 = 0.0;

        for c in int_part.chars() {
            let d = digit(c, radix).ok_or_else(|| "".parse::<f64>().unwrap_err())?;
            value = value * radix as f64 + d as f64;
        }

        if let Some(frac) = frac_part {
            let mut divisor = radix as f64;
            for c in frac.chars() {
                let d = digit(c, radix).ok_or_else(|| "".parse::<f64>().unwrap_err())?;
                value += d as f64 / divisor;
                divisor *= radix as f64;
            }
        }

        if negative {
            value = -value;
        }

        Ok(F::from(value))
    }

    impl FromStrRadix for f32 {
        type Error = core::num::ParseFloatError;

        fn from_str_radix(source: &str, radix: u32) -> Result<Self, Self::Error> {
            parse_float(source, radix)
        }
    }

    impl FromStrRadix for f64 {
        type Error = core::num::ParseFloatError;

        fn from_str_radix(source: &str, radix: u32) -> Result<Self, Self::Error> {
            parse_float(source, radix)
        }
    }
}