diff --git a/Cargo.toml b/Cargo.toml index 13aa9ef..2132a06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,23 @@ license = "MIT OR Apache-2.0" name = "operations" harness = false +[features] +default = ["num-traits"] + +nalgebra-v021 = ["num-traits", "nalgebra_v021", "simba_v01", "approx_v03"] +nalgebra-v029 = ["num-traits", "nalgebra_v029", "simba_v06", "approx_v05"] + [dependencies] num-traits = { version = "0.2", optional = true } +approx_v03 = { package = "approx", version = "0.3", optional = true } +nalgebra_v021 = { package = "nalgebra", version = "0.21", optional = true } +simba_v01 = { package = "simba", version = "0.1", optional = true } + +approx_v05 = { package = "approx", version = "0.5", optional = true } +nalgebra_v029 = { package = "nalgebra", version = "0.29", optional = true } +simba_v06 = { package = "simba", version = "0.6", optional = true } + [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } rand = "0.8" diff --git a/src/lib.rs b/src/lib.rs index 9a76cca..8e8be61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![feature(core_intrinsics)] // intrinsics for the fast math #![feature(asm)] // asm used to emulate freeze +#![feature(doc_cfg)] use core::{ cmp, fmt, intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}, @@ -9,6 +10,39 @@ use core::{ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, }; +macro_rules! forward_freeze_self { + ($fast_ty:ident, $base_ty:ident + $( + $(#[$attr:meta])* + $vis:vis fn $fn_name:ident (self $(, $arg:ident : Self)* ) -> Self ; + )*) => { + $( + $(#[$attr])* + #[inline] + $vis fn $fn_name(self $(, $arg : Self)*) -> Self { + <$fast_ty>::new(<$base_ty>::$fn_name(self.freeze_raw() $(, $arg.freeze_raw())* )) + } + )* + }; + + ($fast_ty:ident, $base_ty:ident + $( + $(#[$attr:meta])* + $vis:vis fn $fn_name:ident (&self $(, $arg:ident : &Self)* ) -> Self ; + )*) => { + $( + $(#[$attr])* + #[inline] + $vis fn $fn_name(&self $(, $arg : &Self)*) -> Self { + <$fast_ty>::new(<$base_ty>::$fn_name(self.freeze_raw() $(, $arg.freeze_raw())* )) + } + )* + }; +} + +mod nalgebra; +mod num_traits; + mod poison; use poison::MaybePoison; @@ -266,259 +300,6 @@ macro_rules! impl_fmt { } } -#[cfg(feature = "num-traits")] -macro_rules! impl_num_traits { - ($fast_ty:ident, $base_ty:ident) => { - impl num_traits::One for $fast_ty { - #[inline(always)] - fn one() -> Self { - Self::ONE - } - - #[inline] - fn is_one(&self) -> bool { - self.freeze_raw() == 1.0 - } - } - - impl num_traits::Zero for $fast_ty { - #[inline(always)] - fn zero() -> Self { - Self::ZERO - } - - #[inline] - fn is_zero(&self) -> bool { - self.freeze_raw() == 0.0 - } - } - - impl num_traits::Num for $fast_ty { - type FromStrRadixErr = <$base_ty as num_traits::Num>::FromStrRadixErr; - - fn from_str_radix(str: &str, radix: u32) -> Result { - Ok(<$fast_ty>::new( - <$base_ty as num_traits::Num>::from_str_radix(str, radix)?, - )) - } - } - - impl num_traits::ToPrimitive for $fast_ty { - forward_freeze_ty! { - $fast_ty, $base_ty - fn to_isize(&self) -> Option ; - fn to_i8(&self) -> Option ; - fn to_i16(&self) -> Option ; - fn to_i32(&self) -> Option ; - fn to_i64(&self) -> Option ; - fn to_i128(&self) -> Option ; - - fn to_usize(&self) -> Option ; - fn to_u8(&self) -> Option ; - fn to_u16(&self) -> Option ; - fn to_u32(&self) -> Option ; - fn to_u64(&self) -> Option ; - fn to_u128(&self) -> Option ; - - fn to_f32(&self) -> Option ; - fn to_f64(&self) -> Option ; - } - } - - impl num_traits::NumCast for $fast_ty { - #[inline] - fn from(n: N) -> Option { - Some(<$fast_ty>::new(<$base_ty as num_traits::NumCast>::from(n)?)) - } - } - - /// Because inf and nan are prohibited, the `fast_fp` types correspond more to the `Real` - /// trait than the `Float` trait. However in practice some libs require a Float bound when - /// they could really use a Real, which would restrict using the `fast_fp` types. - impl num_traits::Float for $fast_ty { - /// Panics because NaN values are not supported - #[inline] - fn nan() -> Self { - panic!(concat!( - stringify!($fast_ty), - " does not support NaN values" - )); - } - - /// Panics because infinite values are not supported - /// - /// Consider using [`max_value`](num_traits::Float::max_value) as appropriate instead - #[inline] - fn infinity() -> Self { - panic!(concat!( - stringify!($fast_ty), - " does not support infinite values. Consider using `max_value` for comparisons" - )); - } - - /// Panics because infinite values are not supported - /// - /// Consider using [`min_value`](num_traits::Float::min_value) as appropriate instead - #[inline] - fn neg_infinity() -> Self { - panic!(concat!( - stringify!($fast_ty), - " does not support infinite values. Consider using `min_value` for comparisons" - )); - } - - #[inline] - fn neg_zero() -> Self { - -Self::ZERO - } - - #[inline] - fn min_value() -> Self { - $fast_ty::MIN - } - - #[inline] - fn min_positive_value() -> Self { - $fast_ty::MIN_POSITIVE - } - - #[inline] - fn max_value() -> Self { - $fast_ty::MAX - } - - #[inline] - fn epsilon() -> Self { - <$fast_ty>::new($base_ty::EPSILON) - } - - #[inline] - fn is_nan(self) -> bool { - false - } - - #[inline] - fn is_infinite(self) -> bool { - false - } - - #[inline] - fn is_finite(self) -> bool { - true - } - - forward_self! { - $fast_ty, $base_ty - fn is_normal(self) -> bool; - fn classify(self) -> FpCategory; - fn floor(self) -> Self; - fn ceil(self) -> Self; - fn round(self) -> Self; - fn trunc(self) -> Self; - fn fract(self) -> Self; - fn abs(self) -> Self; - fn signum(self) -> Self; - fn is_sign_positive(self) -> bool; - fn is_sign_negative(self) -> bool; - fn mul_add(self, a: Self, b: Self) -> Self; - fn recip(self) -> Self; - fn powi(self, n: i32) -> Self; - fn powf(self, n: Self) -> Self; - fn sqrt(self) -> Self; - fn exp(self) -> Self; - fn exp2(self) -> Self; - fn ln(self) -> Self; - fn log(self, base: Self) -> Self; - fn log2(self) -> Self; - fn log10(self) -> Self; - fn max(self, other: Self) -> Self; - fn min(self, other: Self) -> Self; - fn cbrt(self) -> Self; - fn hypot(self, other: Self) -> Self; - fn sin(self) -> Self; - fn cos(self) -> Self; - fn tan(self) -> Self; - fn asin(self) -> Self; - fn acos(self) -> Self; - fn atan(self) -> Self; - fn atan2(self, other: Self) -> Self; - fn sin_cos(self) -> (Self, Self); - fn exp_m1(self) -> Self; - fn ln_1p(self) -> Self; - fn sinh(self) -> Self; - fn cosh(self) -> Self; - fn tanh(self) -> Self; - fn asinh(self) -> Self; - fn acosh(self) -> Self; - fn atanh(self) -> Self; - fn to_degrees(self) -> Self; - fn to_radians(self) -> Self; - } - - forward_freeze_self! { - $fast_ty, $base_ty - #[allow(deprecated)] - fn abs_sub(self, other: Self) -> Self; - } - - #[inline] - fn integer_decode(self) -> (u64, i16, i8) { - <$base_ty as num_traits::Float>::integer_decode(self.freeze_raw()) - } - } - }; -} - -macro_rules! forward_freeze_self { - ($fast_ty:ident, $base_ty:ident - $( - $(#[$attr:meta])* - $vis:vis fn $fn_name:ident (self $(, $arg:ident : Self)* ) -> Self ; - )*) => { - $( - $(#[$attr])* - #[inline] - $vis fn $fn_name(self $(, $arg : Self)*) -> Self { - <$fast_ty>::new(<$base_ty>::$fn_name(self.freeze_raw() $(, $arg.freeze_raw())* )) - } - )* - }; -} - -#[cfg(feature = "num-traits")] -macro_rules! forward_freeze_ty { - ($fast_ty:ident, $base_ty:ident - $( - $(#[$attr:meta])* - $vis:vis fn $fn_name:ident (&self) -> $ret_ty:ty ; - )*) => { - $( - $(#[$attr])* - #[inline] - $vis fn $fn_name(&self) -> $ret_ty { - <$base_ty>::$fn_name(&self.freeze_raw()) - } - )* - } -} - -#[cfg(feature = "num-traits")] -macro_rules! forward_self { - ($fast_ty:ident, $base_ty:ident - $( - $(#[$attr:meta])* - $vis:vis fn $fn_name:ident (self $(, $arg:ident : $arg_ty:ty)* ) -> $ret_ty:ty ; - )*) => { - $( - $(#[$attr])* - #[inline] - $vis fn $fn_name(self $(, $arg : $arg_ty)*) -> $ret_ty { - <$fast_ty>::$fn_name(self $(, $arg)* ) - } - )* - }; -} - macro_rules! impls { ($fast_ty:ident, $base_ty: ident) => { impl $fast_ty { @@ -839,9 +620,6 @@ macro_rules! impls { <$fast_ty>::new(from) } } - - #[cfg(feature = "num-traits")] - impl_num_traits! { $fast_ty, $base_ty } }; } diff --git a/src/nalgebra.rs b/src/nalgebra.rs new file mode 100644 index 0000000..998f0b2 --- /dev/null +++ b/src/nalgebra.rs @@ -0,0 +1,422 @@ +#![cfg(any(feature = "nalgebra-v021"))] +macro_rules! forward_fn_const { + ($fast_ty:ident, $base_ty:ident + $(fn $fn_name:ident () -> Self ;)*) => { + $( + fn $fn_name () -> $fast_ty { + <$fast_ty>::new(<$base_ty>::$fn_name()) + } + )* + } +} + +macro_rules! impl_nalgebra { + ($($fast_ty:ident, $base_ty:ident),* ; + $nalgebra_version:path, $simba_version:path, $approx_version:path ; + @RealField: $real_field_callback:ident + ) => { + use $crate::{FF32, FF64, num_traits::forward_self}; + use $nalgebra_version as na; + use $simba_version as simba; + use $approx_version as approx; + + $( + impl simba::simd::SimdValue for $fast_ty { + type Element = Self; + type SimdBool = bool; + + #[inline] + fn lanes() -> usize { + 1 + } + + #[inline] + fn splat(val: Self::Element) -> Self { + val + } + + #[inline] + fn extract(&self, _:usize) -> Self::Element { + *self + } + + #[inline] + unsafe fn extract_unchecked(&self, _:usize) -> Self::Element { + *self + } + + #[inline] + fn replace(&mut self, _:usize, val: Self::Element) { + *self = val + } + + #[inline] + unsafe fn replace_unchecked(&mut self, _:usize, val: Self::Element) { + *self = val + } + + #[inline] + fn select(self, cond:Self::SimdBool, other: Self) -> Self { + if cond { + self + } else { + other + } + } + } + + impl simba::scalar::SubsetOf for $fast_ty { + #[inline] + fn to_superset(&self) -> f32 { + self.freeze_raw() as f32 + } + + #[inline] + fn from_superset_unchecked(element: &f32) -> Self { + <$fast_ty>::new(*element as $base_ty) + } + + #[inline] + fn is_in_subset(_: &f32) -> bool { + true + } + } + + impl simba::scalar::SubsetOf for $fast_ty { + #[inline] + fn to_superset(&self) -> f64 { + self.freeze_raw() as f64 + } + + #[inline] + fn from_superset_unchecked(element: &f64) -> Self { + <$fast_ty>::new(*element as $base_ty) + } + + #[inline] + fn is_in_subset(_: &f64) -> bool { + true + } + } + + impl simba::scalar::SubsetOf for $fast_ty { + #[inline] + fn to_superset(&self) -> FF32 { + FF32::new(self.freeze_raw() as f32) + } + + #[inline] + fn from_superset_unchecked(element: &FF32) -> Self { + <$fast_ty>::new(element.freeze_raw() as $base_ty) + } + + #[inline] + fn is_in_subset(_: &FF32) -> bool { + true + } + } + + impl simba::scalar::SubsetOf for $fast_ty { + #[inline] + fn to_superset(&self) -> FF64 { + FF64::new(self.freeze_raw() as f64) + } + + #[inline] + fn from_superset_unchecked(element: &FF64) -> Self { + <$fast_ty>::new(element.freeze_raw() as $base_ty) + } + + #[inline] + fn is_in_subset(_: &FF64) -> bool { + true + } + } + + impl simba::scalar::SubsetOf<$fast_ty> for f32 { + #[inline] + fn to_superset(&self) -> $fast_ty { + <$fast_ty>::new(*self as $base_ty) + } + + #[inline] + fn from_superset_unchecked(element: &$fast_ty) -> Self { + element.freeze_raw() as f32 + } + + #[inline] + fn is_in_subset(_: &$fast_ty) -> bool { + true + } + } + + impl simba::scalar::SubsetOf<$fast_ty> for f64 { + #[inline] + fn to_superset(&self) -> $fast_ty { + <$fast_ty>::new(*self as $base_ty) + } + + #[inline] + fn from_superset_unchecked(element: &$fast_ty) -> Self { + element.freeze_raw() as f64 + } + + #[inline] + fn is_in_subset(_: &$fast_ty) -> bool { + true + } + } + + impl approx::AbsDiffEq for $fast_ty { + type Epsilon = Self; + + #[inline] + fn default_epsilon() -> Self { + <$fast_ty>::new($base_ty::EPSILON) + } + + #[inline] + fn abs_diff_eq(&self, other: &Self, epsilon: Self) -> bool { + <$fast_ty>::abs(self - other) <= epsilon + } + } + + impl approx::UlpsEq for $fast_ty { + #[inline] + fn default_max_ulps() -> u32 { + <$base_ty>::default_max_ulps() + } + + #[inline] + fn ulps_eq(&self, other: &Self, epsilon: Self, max_ulps: u32) -> bool { + <$base_ty>::ulps_eq( + &self.freeze_raw(), + &other.freeze_raw(), + epsilon.freeze_raw(), + max_ulps + ) + } + } + + impl approx::RelativeEq for $fast_ty { + #[inline] + fn default_max_relative() -> Self::Epsilon { + <$fast_ty as approx::AbsDiffEq>::default_epsilon() + } + + #[inline] + fn relative_eq(&self, other: &Self, epsilon: Self, max_relative: Self) -> bool { + <$base_ty>::relative_eq( + &self.freeze_raw(), + &other.freeze_raw(), + epsilon.freeze_raw(), + max_relative.freeze_raw(), + ) + } + } + + impl na::Field for $fast_ty {} + + impl na::RealField for $fast_ty { + forward_self! { + $fast_ty, $base_ty + fn atan2(self, other: Self) -> Self; + fn clamp(self, min: Self, max: Self) -> Self; + fn max(self, other: Self) -> Self; + fn min(self, other: Self) -> Self; + } + + forward_fn_const! { + $fast_ty, $base_ty + fn pi() -> Self ; + fn two_pi() -> Self ; + fn frac_pi_2() -> Self ; + fn frac_pi_3() -> Self ; + fn frac_pi_4() -> Self ; + fn frac_pi_6() -> Self ; + fn frac_pi_8() -> Self ; + fn frac_1_pi() -> Self ; + fn frac_2_pi() -> Self ; + fn frac_2_sqrt_pi() -> Self ; + fn e() -> Self ; + fn log2_e() -> Self ; + fn log10_e() -> Self ; + fn ln_2() -> Self ; + fn ln_10() -> Self ; + } + + $real_field_callback! { $fast_ty, $base_ty } + } + + impl na::ComplexField for $fast_ty { + type RealField = Self; + + forward_self! { + $fast_ty, $base_ty + fn floor(self) -> Self; + fn ceil(self) -> Self; + fn round(self) -> Self; + fn trunc(self) -> Self; + fn fract(self) -> Self; + fn abs(self) -> Self; + fn signum(self) -> Self; + fn mul_add(self, a: Self, b: Self) -> Self; + fn recip(self) -> Self; + fn powi(self, n: i32) -> Self; + fn powf(self, n: Self) -> Self; + fn sqrt(self) -> Self; + fn exp(self) -> Self; + fn exp2(self) -> Self; + fn ln(self) -> Self; + fn log(self, base: Self) -> Self; + fn log2(self) -> Self; + fn log10(self) -> Self; + fn cbrt(self) -> Self; + fn hypot(self, other: Self) -> Self; + fn sin(self) -> Self; + fn cos(self) -> Self; + fn tan(self) -> Self; + fn asin(self) -> Self; + fn acos(self) -> Self; + fn atan(self) -> Self; + fn sin_cos(self) -> (Self, Self); + fn exp_m1(self) -> Self; + fn ln_1p(self) -> Self; + fn sinh(self) -> Self; + fn cosh(self) -> Self; + fn tanh(self) -> Self; + fn asinh(self) -> Self; + fn acosh(self) -> Self; + fn atanh(self) -> Self; + } + + #[inline] + fn from_real(re: Self::RealField) -> Self { + re + } + + #[inline] + fn real(self) -> Self::RealField { + self + } + + #[inline] + fn imaginary(self) -> Self::RealField { + Self::ZERO + } + + #[inline] + fn norm1(self) -> Self::RealField { + self.abs() + } + + #[inline] + fn modulus(self) -> Self::RealField { + self.abs() + } + + #[inline] + fn modulus_squared(self) -> Self::RealField { + self * self + } + + #[inline] + fn argument(self) -> Self::RealField { + if self >= Self::ZERO { + Self::ZERO + } else { + <$fast_ty>::new(core::$base_ty::consts::PI) + } + } + + #[inline] + fn scale(self, factor: Self::RealField) -> Self { + self * factor + } + + #[inline] + fn unscale(self, factor: Self::RealField) -> Self { + self / factor + } + + #[inline] + fn conjugate(self) -> Self { + self + } + + #[inline] + fn powc(self, n: Self) -> Self { + self.powf(n) + } + + #[inline] + fn is_finite(&self) -> bool { + true + } + + #[inline] + fn try_sqrt(self) -> Option { + let x = self.freeze_raw(); + if x > 0.0 { + Some(<$fast_ty>::new(x.sqrt())) + } else { + None + } + } + } + )* + }; +} + +#[cfg(feature = "nalgebra-v021")] +#[doc(cfg(feature = "nalgebra-v021"))] +mod nalgebra_v021 { + macro_rules! real_field { + ($fast_ty:ident, $base_ty:ident) => { + #[inline] + fn is_sign_positive(self) -> bool { + self > Self::ZERO + } + + #[inline] + fn is_sign_negative(self) -> bool { + self < Self::ZERO + } + }; + } + + impl_nalgebra! { + FF32, f32, FF64, f64 ; + ::nalgebra_v021, ::simba_v01, ::approx_v03 ; + @RealField: real_field + } +} + +#[cfg(feature = "nalgebra-v029")] +#[doc(cfg(feature = "nalgebra-v029"))] +mod nalgebra_v029 { + macro_rules! real_field { + ($fast_ty:ident, $base_ty:ident) => { + #[inline] + fn is_sign_positive(&self) -> bool { + *self > Self::ZERO + } + + #[inline] + fn is_sign_negative(&self) -> bool { + *self < Self::ZERO + } + + forward_self! { + $fast_ty, $base_ty + fn copysign(self, other: Self) -> Self; + } + }; + } + + impl_nalgebra! { + FF32, f32, FF64, f64 ; + ::nalgebra_v029, ::simba_v06, ::approx_v05 ; + @RealField: real_field + } +} diff --git a/src/num_traits.rs b/src/num_traits.rs new file mode 100644 index 0000000..796dd82 --- /dev/null +++ b/src/num_traits.rs @@ -0,0 +1,330 @@ +#![cfg(feature = "num-traits")] +#![doc(cfg(feature = "num-traits"))] +use crate::{FF32, FF64}; +use core::num::FpCategory; + +macro_rules! forward_freeze_ty { + ($fast_ty:ident, $base_ty:ident + $( + $(#[$attr:meta])* + $vis:vis fn $fn_name:ident (&self) -> $ret_ty:ty ; + )*) => { + $( + $(#[$attr])* + #[inline] + $vis fn $fn_name(&self) -> $ret_ty { + <$base_ty>::$fn_name(&self.freeze_raw()) + } + )* + } +} + +macro_rules! forward_self { + ($fast_ty:ident, $base_ty:ident + $( + $(#[$attr:meta])* + $vis:vis fn $fn_name:ident (self $(, $arg:ident : $arg_ty:ty)* ) -> $ret_ty:ty ; + )*) => { + $( + $(#[$attr])* + #[inline] + $vis fn $fn_name(self $(, $arg : $arg_ty)*) -> $ret_ty { + <$fast_ty>::$fn_name(self $(, $arg)* ) + } + )* + }; + + ($fast_ty:ident, $base_ty:ident + $( + $(#[$attr:meta])* + $vis:vis fn $fn_name:ident (&self $(, $arg:ident : $arg_ty:ty)* ) -> $ret_ty:ty ; + )*) => { + $( + $(#[$attr])* + #[inline] + $vis fn $fn_name(&self $(, $arg : $arg_ty)*) -> $ret_ty { + <$fast_ty>::$fn_name(*self $(, $arg)* ) + } + )* + }; +} + +pub(crate) use forward_self; + +macro_rules! opt_from_base { + ($fast_ty:ident, $base_ty:ident + $( + $(#[$attr:meta])* + $vis:vis fn $fn_name:ident ($($arg:ident : $arg_ty:ty),*) -> Option ; + )*) => { + $( + $(#[$attr])* + #[inline] + $vis fn $fn_name($($arg : $arg_ty),*) -> Option { + Some(<$fast_ty>::new(<$base_ty>::$fn_name($($arg),*)?)) + } + )* + } +} + +macro_rules! impl_num_traits { + ($fast_ty:ident, $base_ty:ident) => { + impl num_traits::One for $fast_ty { + #[inline(always)] + fn one() -> Self { + Self::ONE + } + + #[inline] + fn is_one(&self) -> bool { + self.freeze_raw() == 1.0 + } + } + + impl num_traits::Zero for $fast_ty { + #[inline(always)] + fn zero() -> Self { + Self::ZERO + } + + #[inline] + fn is_zero(&self) -> bool { + self.freeze_raw() == 0.0 + } + } + + impl num_traits::Bounded for $fast_ty { + #[inline] + fn max_value() -> Self { + Self::MAX + } + #[inline] + fn min_value() -> Self { + Self::MIN + } + } + + impl num_traits::Signed for $fast_ty { + forward_self! { + $fast_ty, $base_ty + fn abs(&self) -> Self ; + fn signum(&self) -> Self ; + } + + forward_freeze_self! { + $fast_ty, $base_ty + #[allow(deprecated)] + fn abs_sub(&self, other: &Self) -> Self; + } + + #[inline] + fn is_positive(&self) -> bool { + self.freeze_raw() > 0.0 + } + + #[inline] + fn is_negative(&self) -> bool { + self.freeze_raw() < 0.0 + } + } + + impl num_traits::Num for $fast_ty { + type FromStrRadixErr = <$base_ty as num_traits::Num>::FromStrRadixErr; + + fn from_str_radix(str: &str, radix: u32) -> Result { + Ok(<$fast_ty>::new( + <$base_ty as num_traits::Num>::from_str_radix(str, radix)?, + )) + } + } + + impl num_traits::ToPrimitive for $fast_ty { + forward_freeze_ty! { + $fast_ty, $base_ty + fn to_isize(&self) -> Option ; + fn to_i8(&self) -> Option ; + fn to_i16(&self) -> Option ; + fn to_i32(&self) -> Option ; + fn to_i64(&self) -> Option ; + fn to_i128(&self) -> Option ; + + fn to_usize(&self) -> Option ; + fn to_u8(&self) -> Option ; + fn to_u16(&self) -> Option ; + fn to_u32(&self) -> Option ; + fn to_u64(&self) -> Option ; + fn to_u128(&self) -> Option ; + + fn to_f32(&self) -> Option ; + fn to_f64(&self) -> Option ; + } + } + + impl num_traits::FromPrimitive for $fast_ty { + opt_from_base! { + $fast_ty, $base_ty + fn from_isize(n: isize) -> Option ; + fn from_i8(n: i8) -> Option ; + fn from_i16(n: i16) -> Option ; + fn from_i32(n: i32) -> Option ; + fn from_i64(n: i64) -> Option ; + fn from_i128(n: i128) -> Option ; + + fn from_usize(n: usize) -> Option ; + fn from_u8(n: u8) -> Option ; + fn from_u16(n: u16) -> Option ; + fn from_u32(n: u32) -> Option ; + fn from_u64(n: u64) -> Option ; + fn from_u128(n: u128) -> Option ; + + fn from_f32(n: f32) -> Option ; + fn from_f64(n: f64) -> Option ; + } + } + + impl num_traits::NumCast for $fast_ty { + #[inline] + fn from(n: N) -> Option { + Some(<$fast_ty>::new(<$base_ty as num_traits::NumCast>::from(n)?)) + } + } + + /// Because inf and nan are prohibited, the `fast_fp` types correspond more to the `Real` + /// trait than the `Float` trait. However in practice some libs require a Float bound when + /// they could really use a Real, which would restrict using the `fast_fp` types. + impl num_traits::Float for $fast_ty { + /// Panics because NaN values are not supported + #[inline] + fn nan() -> Self { + panic!(concat!( + stringify!($fast_ty), + " does not support NaN values" + )); + } + + /// Panics because infinite values are not supported + /// + /// Consider using [`max_value`](num_traits::Float::max_value) as appropriate instead + #[inline] + fn infinity() -> Self { + panic!(concat!( + stringify!($fast_ty), + " does not support infinite values. Consider using `max_value` for comparisons" + )); + } + + /// Panics because infinite values are not supported + /// + /// Consider using [`min_value`](num_traits::Float::min_value) as appropriate instead + #[inline] + fn neg_infinity() -> Self { + panic!(concat!( + stringify!($fast_ty), + " does not support infinite values. Consider using `min_value` for comparisons" + )); + } + + #[inline] + fn neg_zero() -> Self { + -Self::ZERO + } + + #[inline] + fn min_value() -> Self { + $fast_ty::MIN + } + + #[inline] + fn min_positive_value() -> Self { + $fast_ty::MIN_POSITIVE + } + + #[inline] + fn max_value() -> Self { + $fast_ty::MAX + } + + #[inline] + fn epsilon() -> Self { + <$fast_ty>::new($base_ty::EPSILON) + } + + #[inline] + fn is_nan(self) -> bool { + false + } + + #[inline] + fn is_infinite(self) -> bool { + false + } + + #[inline] + fn is_finite(self) -> bool { + true + } + + forward_self! { + $fast_ty, $base_ty + fn is_normal(self) -> bool; + fn classify(self) -> FpCategory; + fn floor(self) -> Self; + fn ceil(self) -> Self; + fn round(self) -> Self; + fn trunc(self) -> Self; + fn fract(self) -> Self; + fn abs(self) -> Self; + fn signum(self) -> Self; + fn is_sign_positive(self) -> bool; + fn is_sign_negative(self) -> bool; + fn mul_add(self, a: Self, b: Self) -> Self; + fn recip(self) -> Self; + fn powi(self, n: i32) -> Self; + fn powf(self, n: Self) -> Self; + fn sqrt(self) -> Self; + fn exp(self) -> Self; + fn exp2(self) -> Self; + fn ln(self) -> Self; + fn log(self, base: Self) -> Self; + fn log2(self) -> Self; + fn log10(self) -> Self; + fn max(self, other: Self) -> Self; + fn min(self, other: Self) -> Self; + fn cbrt(self) -> Self; + fn hypot(self, other: Self) -> Self; + fn sin(self) -> Self; + fn cos(self) -> Self; + fn tan(self) -> Self; + fn asin(self) -> Self; + fn acos(self) -> Self; + fn atan(self) -> Self; + fn atan2(self, other: Self) -> Self; + fn sin_cos(self) -> (Self, Self); + fn exp_m1(self) -> Self; + fn ln_1p(self) -> Self; + fn sinh(self) -> Self; + fn cosh(self) -> Self; + fn tanh(self) -> Self; + fn asinh(self) -> Self; + fn acosh(self) -> Self; + fn atanh(self) -> Self; + fn to_degrees(self) -> Self; + fn to_radians(self) -> Self; + } + + forward_freeze_self! { + $fast_ty, $base_ty + #[allow(deprecated)] + fn abs_sub(self, other: Self) -> Self; + } + + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + <$base_ty as num_traits::Float>::integer_decode(self.freeze_raw()) + } + } + }; +} + +impl_num_traits! { FF32, f32 } +impl_num_traits! { FF64, f64 }