use core::fmt::Debug;
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
pub trait Num:
Copy + PartialEq + Debug + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
{
fn zero() -> Self;
fn one() -> Self;
fn from_i64(v: i64) -> Self;
}
pub trait Signed: Num + Neg<Output = Self> + PartialOrd {
fn abs(self) -> Self;
fn signum(self) -> Self;
fn is_negative(self) -> bool;
}
pub trait Field: Num + Div<Output = Self> + Rem<Output = Self> {
fn div(self, other: Self) -> Self;
fn reciprocal(self) -> Self;
}
pub trait Real: Num + Signed + Div<Output = Self> + Rem<Output = Self> + PartialOrd {
fn from_f64(v: f64) -> Self;
fn to_f64(self) -> f64;
fn epsilon() -> Self;
fn sqrt(self) -> Self;
fn cbrt(self) -> Self;
fn powf(self, e: Self) -> Self;
fn powi(self, e: i32) -> Self;
fn recip(self) -> Self;
fn floor(self) -> Self;
fn ceil(self) -> Self;
fn round(self) -> Self;
fn trunc(self) -> Self;
fn fract(self) -> Self;
fn copysign(self, other: Self) -> Self;
fn abs_sub(self, other: Self) -> Self;
fn min(self, other: Self) -> Self;
fn max(self, other: Self) -> Self;
}
pub trait Transcendental: Real {
fn exp(self) -> Self;
fn exp_m1(self) -> Self;
fn ln(self) -> Self;
fn ln_1p(self) -> Self;
fn log(self, base: Self) -> Self;
fn log10(self) -> Self;
fn log2(self) -> Self;
}
pub trait Trig: Real {
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 hypot(self, other: Self) -> Self;
}
pub trait Hyperbolic: Real {
fn sinh(self) -> Self;
fn cosh(self) -> Self;
fn tanh(self) -> Self;
fn asinh(self) -> Self;
fn acosh(self) -> Self;
fn atanh(self) -> Self;
}
pub trait FloatClass: Real {
fn is_finite(self) -> bool;
fn is_nan(self) -> bool;
fn is_infinite(self) -> bool;
fn is_normal(self) -> bool;
fn is_subnormal(self) -> bool;
fn is_sign_negative(self) -> bool;
fn is_sign_positive(self) -> bool;
}
pub trait RealFull:
Real + Transcendental + Trig + Hyperbolic + FloatClass
{
}
impl<T: Real + Transcendental + Trig + Hyperbolic + FloatClass> RealFull for T {}
pub trait Normed: Signed {
fn norm(self) -> Self;
}
macro_rules! impl_num_int {
($($t:ty),* $(,)?) => {$(
impl Num for $t {
fn zero() -> Self { 0 }
fn one() -> Self { 1 }
fn from_i64(v: i64) -> Self { v as $t }
}
)*};
}
impl_num_int!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
macro_rules! impl_num_float {
($($t:ty),* $(,)?) => {$(
impl Num for $t {
fn zero() -> Self { 0.0 }
fn one() -> Self { 1.0 }
fn from_i64(v: i64) -> Self { v as $t }
}
)*};
}
impl_num_float!(f32, f64);
macro_rules! impl_signed_int {
($($t:ty),* $(,)?) => {$(
impl Signed for $t {
fn abs(self) -> Self { self.abs() }
fn signum(self) -> Self { self.signum() }
fn is_negative(self) -> bool { self < 0 }
}
)*};
}
impl_signed_int!(i8, i16, i32, i64, isize);
macro_rules! impl_signed_float {
($($t:ty),* $(,)?) => {$(
impl Signed for $t {
fn abs(self) -> Self { self.abs() }
fn signum(self) -> Self { self.signum() }
fn is_negative(self) -> bool { self.is_sign_negative() }
}
)*};
}
impl_signed_float!(f32, f64);
macro_rules! impl_field_float {
($($t:ty),* $(,)?) => {$(
impl Field for $t {
fn div(self, other: Self) -> Self { self / other }
fn reciprocal(self) -> Self { 1.0 / self }
}
)*};
}
impl_field_float!(f32, f64);
#[cfg(feature = "std")]
macro_rules! impl_real_core {
($($t:ty),* $(,)?) => {$(
impl Real for $t {
fn from_f64(v: f64) -> Self { v as $t }
fn to_f64(self) -> f64 { f64::from(self) }
fn epsilon() -> Self { Self::from_f64($crate::precision::EPS) }
fn sqrt(self) -> Self { self.sqrt() }
fn cbrt(self) -> Self { self.cbrt() }
fn powf(self, e: Self) -> Self { self.powf(e) }
fn powi(self, e: i32) -> Self { self.powi(e) }
fn recip(self) -> Self { self.recip() }
fn floor(self) -> Self { self.floor() }
fn ceil(self) -> Self { self.ceil() }
fn round(self) -> Self { self.round() }
fn trunc(self) -> Self { self.trunc() }
fn fract(self) -> Self { self.fract() }
fn copysign(self, other: Self) -> Self { self.copysign(other) }
fn abs_sub(self, other: Self) -> Self { (self - other).max(Self::zero()) }
fn min(self, other: Self) -> Self { self.min(other) }
fn max(self, other: Self) -> Self { self.max(other) }
}
)*};
}
#[cfg(feature = "std")]
macro_rules! impl_transcendental {
($($t:ty),* $(,)?) => {$(
impl Transcendental for $t {
fn exp(self) -> Self { self.exp() }
fn exp_m1(self) -> Self { self.exp_m1() }
fn ln(self) -> Self { self.ln() }
fn ln_1p(self) -> Self { self.ln_1p() }
fn log(self, base: Self) -> Self { self.log(base) }
fn log10(self) -> Self { self.log10() }
fn log2(self) -> Self { self.log2() }
}
)*};
}
#[cfg(feature = "std")]
macro_rules! impl_trig {
($($t:ty),* $(,)?) => {$(
impl Trig for $t {
fn sin(self) -> Self { self.sin() }
fn cos(self) -> Self { self.cos() }
fn tan(self) -> Self { self.tan() }
fn asin(self) -> Self { self.asin() }
fn acos(self) -> Self { self.acos() }
fn atan(self) -> Self { self.atan() }
fn atan2(self, other: Self) -> Self { self.atan2(other) }
fn sin_cos(self) -> (Self, Self) { self.sin_cos() }
fn hypot(self, other: Self) -> Self { self.hypot(other) }
}
)*};
}
#[cfg(feature = "std")]
macro_rules! impl_hyperbolic {
($($t:ty),* $(,)?) => {$(
impl Hyperbolic for $t {
fn sinh(self) -> Self { self.sinh() }
fn cosh(self) -> Self { self.cosh() }
fn tanh(self) -> Self { self.tanh() }
fn asinh(self) -> Self { self.asinh() }
fn acosh(self) -> Self { self.acosh() }
fn atanh(self) -> Self { self.atanh() }
}
)*};
}
#[cfg(feature = "std")]
macro_rules! impl_float_class {
($($t:ty),* $(,)?) => {$(
impl FloatClass for $t {
fn is_finite(self) -> bool { self.is_finite() }
fn is_nan(self) -> bool { self.is_nan() }
fn is_infinite(self) -> bool { self.is_infinite() }
fn is_normal(self) -> bool { self.is_normal() }
fn is_subnormal(self) -> bool { self.is_subnormal() }
fn is_sign_negative(self) -> bool { self.is_sign_negative() }
fn is_sign_positive(self) -> bool { self.is_sign_positive() }
}
)*};
}
#[cfg(feature = "std")]
mod real_impls {
use super::{Num, Real, Transcendental, Trig, Hyperbolic, FloatClass};
impl_real_core!(f32, f64);
impl_transcendental!(f32, f64);
impl_trig!(f32, f64);
impl_hyperbolic!(f32, f64);
impl_float_class!(f32, f64);
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
mod real_impls {
use super::{Num, Real, Transcendental, Trig, Hyperbolic, FloatClass};
macro_rules! impl_real_core_libm {
($($t:ty),* $(,)?) => {$(
impl Real for $t {
fn from_f64(v: f64) -> Self { v as $t }
fn to_f64(self) -> f64 { f64::from(self) }
fn epsilon() -> Self { Self::from_f64($crate::precision::EPS) }
fn sqrt(self) -> Self { crate::libm_fallback::sqrt(self as f64) as $t }
fn cbrt(self) -> Self { crate::libm_fallback::cbrt(self as f64) as $t }
fn powf(self, e: Self) -> Self { crate::libm_fallback::powf(self as f64, e as f64) as $t }
fn powi(self, e: i32) -> Self { crate::libm_fallback::powi(self as f64, e) as $t }
fn recip(self) -> Self { 1.0 / self }
fn floor(self) -> Self { crate::libm_fallback::floor(self as f64) as $t }
fn ceil(self) -> Self { crate::libm_fallback::ceil(self as f64) as $t }
fn round(self) -> Self { crate::libm_fallback::round(self as f64) as $t }
fn trunc(self) -> Self { crate::libm_fallback::trunc(self as f64) as $t }
fn fract(self) -> Self { self - crate::libm_fallback::floor(self as f64) as $t }
fn copysign(self, other: Self) -> Self {
if other.is_sign_negative() { -self.abs() } else { self.abs() }
}
fn abs_sub(self, other: Self) -> Self { (self - other).max(Self::zero()) }
fn min(self, other: Self) -> Self { if self < other { self } else { other } }
fn max(self, other: Self) -> Self { if self > other { self } else { other } }
}
)*};
}
macro_rules! impl_transcendental_libm {
($($t:ty),* $(,)?) => {$(
impl Transcendental for $t {
fn exp(self) -> Self { crate::libm_fallback::exp(self as f64) as $t }
fn exp_m1(self) -> Self { crate::libm_fallback::exp_m1(self as f64) as $t }
fn ln(self) -> Self { crate::libm_fallback::ln(self as f64) as $t }
fn ln_1p(self) -> Self { crate::libm_fallback::ln_1p(self as f64) as $t }
fn log(self, base: Self) -> Self { crate::libm_fallback::log(self as f64, base as f64) as $t }
fn log10(self) -> Self { crate::libm_fallback::log10(self as f64) as $t }
fn log2(self) -> Self { crate::libm_fallback::log2(self as f64) as $t }
}
)*};
}
macro_rules! impl_trig_libm {
($($t:ty),* $(,)?) => {$(
impl Trig for $t {
fn sin(self) -> Self { crate::libm_fallback::sin(self as f64) as $t }
fn cos(self) -> Self { crate::libm_fallback::cos(self as f64) as $t }
fn tan(self) -> Self { crate::libm_fallback::tan(self as f64) as $t }
fn asin(self) -> Self { crate::libm_fallback::asin(self as f64) as $t }
fn acos(self) -> Self { crate::libm_fallback::acos(self as f64) as $t }
fn atan(self) -> Self { crate::libm_fallback::atan(self as f64) as $t }
fn atan2(self, other: Self) -> Self { crate::libm_fallback::atan2(self as f64, other as f64) as $t }
fn sin_cos(self) -> (Self, Self) {
let (s, c) = crate::libm_fallback::sin_cos(self as f64);
(s as $t, c as $t)
}
fn hypot(self, other: Self) -> Self { crate::libm_fallback::hypot(self as f64, other as f64) as $t }
}
)*};
}
macro_rules! impl_hyperbolic_libm {
($($t:ty),* $(,)?) => {$(
impl Hyperbolic for $t {
fn sinh(self) -> Self { crate::libm_fallback::sinh(self as f64) as $t }
fn cosh(self) -> Self { crate::libm_fallback::cosh(self as f64) as $t }
fn tanh(self) -> Self { crate::libm_fallback::tanh(self as f64) as $t }
fn asinh(self) -> Self { crate::libm_fallback::asinh(self as f64) as $t }
fn acosh(self) -> Self { crate::libm_fallback::acosh(self as f64) as $t }
fn atanh(self) -> Self { crate::libm_fallback::atanh(self as f64) as $t }
}
)*};
}
macro_rules! impl_float_class_libm {
($($t:ty),* $(,)?) => {$(
impl FloatClass for $t {
fn is_finite(self) -> bool { self.is_finite() }
fn is_nan(self) -> bool { self.is_nan() }
fn is_infinite(self) -> bool { self.is_infinite() }
fn is_normal(self) -> bool { self.is_normal() }
fn is_subnormal(self) -> bool { self.is_finite() && self != 0.0 && self.abs() < Self::MIN_POSITIVE }
fn is_sign_negative(self) -> bool { self.is_sign_negative() }
fn is_sign_positive(self) -> bool { self.is_sign_positive() }
}
)*};
}
impl_real_core_libm!(f32, f64);
impl_transcendental_libm!(f32, f64);
impl_trig_libm!(f32, f64);
impl_hyperbolic_libm!(f32, f64);
impl_float_class_libm!(f32, f64);
}
impl Normed for f32 {
fn norm(self) -> Self {
self.abs()
}
}
impl Normed for f64 {
fn norm(self) -> Self {
self.abs()
}
}
impl Normed for i8 {
fn norm(self) -> Self {
self.abs()
}
}
impl Normed for i16 {
fn norm(self) -> Self {
self.abs()
}
}
impl Normed for i32 {
fn norm(self) -> Self {
self.abs()
}
}
impl Normed for i64 {
fn norm(self) -> Self {
self.abs()
}
}
impl Normed for isize {
fn norm(self) -> Self {
self.abs()
}
}
#[cfg(test)]
#[allow(clippy::suboptimal_flops, clippy::imprecise_flops)]
mod tests {
use super::{Field, Num, Real};
#[test]
fn identity_laws() {
for a in 0..100i64 {
assert_eq!(a + <i64 as Num>::zero(), a);
assert_eq!(a * <i64 as Num>::one(), a);
}
}
#[test]
fn signed_abs() {
assert_eq!((-5i32).abs(), 5);
assert_eq!((-2.5f64).abs(), 2.5);
assert_eq!((-2.5f64).signum(), -1.0);
}
#[test]
fn real_ops() {
let x = 9.0f64;
assert_eq!(x.sqrt(), 3.0);
assert_eq!(x.cbrt(), 2.080083823051904);
assert_eq!(x.powi(2), 81.0);
assert_eq!(x.powf(0.5), 3.0);
assert_eq!(x.ln(), 2.0 * 3.0f64.ln());
assert!((1.0f64.sin().powi(2) + 1.0f64.cos().powi(2) - 1.0).abs() < 1e-12);
assert!((0.5f64.asin() - 0.5f64.asin()).abs() < 1e-12);
assert!((0.5f64.acos() - 0.5f64.acos()).abs() < 1e-12);
assert!((1.0f64.atan() - core::f64::consts::FRAC_PI_4).abs() < 1e-12);
assert!((1.0f64.atan2(1.0) - core::f64::consts::FRAC_PI_4).abs() < 1e-12);
assert!((1.0f64.cosh().powi(2) - 1.0f64.sinh().powi(2) - 1.0).abs() < 1e-12);
assert!((0.5f64.tanh() - 0.5f64.tanh()).abs() < 1e-12);
assert!((1.0f64.asinh() - 1.0f64.asinh()).abs() < 1e-12);
assert!((0.001f64.exp_m1() - (0.001f64.exp() - 1.0)).abs() < 1e-15);
assert!((0.001f64.ln_1p() - 0.001f64.ln_1p()).abs() < 1e-15);
let (s, c) = 0.0f64.sin_cos();
assert!((s - 0.0f64.sin()).abs() < 1e-15);
assert!((c - 0.0f64.cos()).abs() < 1e-15);
assert!((3.0f64.hypot(4.0) - 5.0).abs() < 1e-12);
assert_eq!(3.0f64.copysign(-1.0), -3.0);
assert_eq!(Real::abs_sub(5.0f64, 3.0), 2.0);
assert_eq!(Real::abs_sub(4.0f64, 5.0), 0.0);
assert_eq!(4.0f64.recip(), 0.25);
assert!((8.0f64.log2() - 3.0).abs() < 1e-12);
assert_eq!(2.7f64.trunc(), 2.0);
assert!((2.7f64.fract() - 0.7).abs() < 1e-12);
assert_eq!((-2.5f64).signum(), -1.0);
assert!(1.0f64.is_normal());
assert!(!0.0f64.is_normal());
assert!(1.0f64.is_sign_positive());
assert!((-0.0f64).is_sign_negative());
}
#[test]
fn field_reciprocal() {
assert_eq!(4.0f64.reciprocal(), 0.25);
assert_eq!(Field::div(10.0f32, 4.0), 2.5);
}
}