use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
pub trait Element:
Copy + Clone + Send + Sync + Debug + Default + PartialEq + PartialOrd + 'static
{
const DTYPE: &'static str;
fn to_f64(self) -> f64;
fn from_f64(value: f64) -> Self;
}
pub trait Num:
Element
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Rem<Output = Self>
{
const ZERO: Self;
const ONE: Self;
const MIN_VALUE: Self;
const MAX_VALUE: Self;
fn abs(self) -> Self;
fn from_usize(value: usize) -> Self;
}
pub trait Float: Num + Neg<Output = Self> + crate::misc::FloatHooks {
const NAN: Self;
const INFINITY: Self;
const NEG_INFINITY: Self;
const EPSILON: Self;
const PI: Self;
const E: Self;
fn exp(self) -> Self;
fn exp2(self) -> Self;
fn exp_m1(self) -> Self;
fn ln(self) -> Self;
fn log2(self) -> Self;
fn log10(self) -> Self;
fn ln_1p(self) -> Self;
fn sqrt(self) -> Self;
fn cbrt(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 sinh(self) -> Self;
fn cosh(self) -> Self;
fn tanh(self) -> Self;
fn floor(self) -> Self;
fn ceil(self) -> Self;
fn round(self) -> Self;
fn trunc(self) -> Self;
fn powf(self, p: Self) -> Self;
fn powi(self, p: i32) -> Self;
fn is_nan(self) -> bool;
fn is_infinite(self) -> bool;
fn is_finite(self) -> bool;
fn atan2(self, o: Self) -> Self;
fn hypot(self, o: Self) -> Self;
fn max(self, o: Self) -> Self;
fn min(self, o: Self) -> Self;
}
macro_rules! element {
($t:ty,$n:literal) => {
impl Element for $t {
const DTYPE: &'static str = $n;
fn to_f64(self) -> f64 {
self as f64
}
fn from_f64(v: f64) -> Self {
v as $t
}
}
};
}
element!(f32, "float32");
element!(f64, "float64");
element!(i32, "int32");
element!(i64, "int64");
element!(u8, "uint8");
element!(usize, "uint64");
impl Element for bool {
const DTYPE: &'static str = "bool";
fn to_f64(self) -> f64 {
if self { 1.0 } else { 0.0 }
}
fn from_f64(v: f64) -> Self {
v != 0.0
}
}
macro_rules! num {
($t:ty,$abs:ident) => {
impl Num for $t {
const ZERO: Self = 0 as $t;
const ONE: Self = 1 as $t;
const MIN_VALUE: Self = <$t>::MIN;
const MAX_VALUE: Self = <$t>::MAX;
fn abs(self) -> Self {
self.$abs()
}
fn from_usize(v: usize) -> Self {
v as $t
}
}
};
}
num!(f32, abs);
num!(f64, abs);
num!(i32, wrapping_abs);
num!(i64, wrapping_abs);
impl Num for u8 {
const ZERO: Self = 0;
const ONE: Self = 1;
const MIN_VALUE: Self = u8::MIN;
const MAX_VALUE: Self = u8::MAX;
fn abs(self) -> Self {
self
}
fn from_usize(v: usize) -> Self {
v as u8
}
}
impl Num for usize {
const ZERO: Self = 0;
const ONE: Self = 1;
const MIN_VALUE: Self = usize::MIN;
const MAX_VALUE: Self = usize::MAX;
fn abs(self) -> Self {
self
}
fn from_usize(v: usize) -> Self {
v
}
}
macro_rules! float {
($t:ty) => {
impl Float for $t {
const NAN: Self = <$t>::NAN;
const INFINITY: Self = <$t>::INFINITY;
const NEG_INFINITY: Self = <$t>::NEG_INFINITY;
const EPSILON: Self = <$t>::EPSILON;
const PI: Self = core::f64::consts::PI as $t;
const E: Self = core::f64::consts::E as $t;
fn exp(self) -> Self {
self.exp()
}
fn exp2(self) -> Self {
self.exp2()
}
fn exp_m1(self) -> Self {
self.exp_m1()
}
fn ln(self) -> Self {
self.ln()
}
fn log2(self) -> Self {
self.log2()
}
fn log10(self) -> Self {
self.log10()
}
fn ln_1p(self) -> Self {
self.ln_1p()
}
fn sqrt(self) -> Self {
self.sqrt()
}
fn cbrt(self) -> Self {
self.cbrt()
}
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 sinh(self) -> Self {
self.sinh()
}
fn cosh(self) -> Self {
self.cosh()
}
fn tanh(self) -> Self {
self.tanh()
}
fn floor(self) -> Self {
self.floor()
}
fn ceil(self) -> Self {
self.ceil()
}
fn round(self) -> Self {
self.round_ties_even()
}
fn trunc(self) -> Self {
self.trunc()
}
fn powf(self, p: Self) -> Self {
self.powf(p)
}
fn powi(self, p: i32) -> Self {
self.powi(p)
}
fn is_nan(self) -> bool {
self.is_nan()
}
fn is_infinite(self) -> bool {
self.is_infinite()
}
fn is_finite(self) -> bool {
self.is_finite()
}
fn atan2(self, o: Self) -> Self {
self.atan2(o)
}
fn hypot(self, o: Self) -> Self {
self.hypot(o)
}
fn max(self, o: Self) -> Self {
if self.is_nan() || o.is_nan() {
Self::NAN
} else {
self.max(o)
}
}
fn min(self, o: Self) -> Self {
if self.is_nan() || o.is_nan() {
Self::NAN
} else {
self.min(o)
}
}
}
};
}
float!(f32);
float!(f64);
pub(crate) fn isnan<T: Element>(x: T) -> bool {
x.partial_cmp(&x).is_none()
}
pub(crate) fn maximum_value<T: Element>(x: T, y: T) -> T {
if isnan(x) || x > y { x } else { y }
}
pub(crate) fn minimum_value<T: Element>(x: T, y: T) -> T {
if isnan(x) || x < y { x } else { y }
}
macro_rules! arithmetic {
($name:ident,$method:ident,$op:tt) => {
pub(crate) fn $name<T:Num>(x:T,y:T)->T {
use std::any::Any;
macro_rules! cast {($t:ty)=> {
if let (Some(a),Some(b))=((&x as &dyn Any).downcast_ref::<$t>(),(&y as &dyn Any).downcast_ref::<$t>()) {
let value=a.$method(*b);
return *(&value as &dyn Any).downcast_ref::<T>().expect("same numeric type");
}
}}
cast!(i32);cast!(i64);cast!(u8);cast!(usize);
x $op y
}
};
}
arithmetic!(add,wrapping_add,+);
arithmetic!(sub,wrapping_sub,-);
arithmetic!(mul,wrapping_mul,*);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dtype_casts() {
assert!(bool::from_f64(-1.0));
assert_eq!(i64::DTYPE, "int64");
assert_eq!(u8::from_f64(2.9), 2);
}
#[test]
fn bankers_rounding() {
assert_eq!(Float::round(2.5f64), 2.0);
assert_eq!(Float::round(3.5f64), 4.0);
}
#[test]
fn nan_extrema() {
assert!(Float::max(f64::NAN, 1.0).is_nan());
}
}