pub trait Abs {
fn abs(&self) -> Self;
}
pub trait Zero {
fn zero() -> Self;
fn is_zero(&self) -> bool;
}
pub trait One {
fn one() -> Self;
fn is_one(&self) -> bool;
}
pub trait NumCast: Sized {
fn from(n: usize) -> Option<Self>;
}
pub trait Const {
fn tau() -> Self;
}
pub trait Inv {
fn inv(&self) -> Self;
}
pub trait Ln {
fn ln(&self) -> Self;
}
pub trait Cos {
fn cos(&self) -> Self;
}
pub trait Sin {
fn sin(&self) -> Self;
}
pub trait Pow {
fn powf(&self, exp: Self) -> Self;
fn powi(&self, exp: i32) -> Self;
}
pub trait Sign {
fn signum(&self) -> Self;
fn is_sign_negative(&self) -> bool;
}
pub trait Sqrt {
fn sqrt(&self) -> Self;
}
pub trait Max {
fn max(self, other: Self) -> Self;
}
pub trait Epsilon {
fn epsilon() -> Self;
}
macro_rules! impl_trait_float {
($t:ty, $id:ident, $z:expr, $o:expr) => {
impl Abs for $t {
fn abs(&self) -> Self {
<$t>::abs(*self)
}
}
impl Cos for $t {
fn cos(&self) -> Self {
<$t>::cos(*self)
}
}
impl Inv for $t {
fn inv(&self) -> Self {
<$t>::recip(*self)
}
}
impl Ln for $t {
fn ln(&self) -> Self {
<$t>::ln(*self)
}
}
impl Max for $t {
fn max(self, other: Self) -> Self {
<$t>::max(self, other)
}
}
impl Pow for $t {
fn powf(&self, exp: Self) -> Self {
<$t>::powf(*self, exp)
}
fn powi(&self, exp: i32) -> Self {
<$t>::powi(*self, exp)
}
}
impl Sign for $t {
fn signum(&self) -> Self {
<$t>::signum(*self)
}
fn is_sign_negative(&self) -> bool {
<$t>::is_sign_negative(*self)
}
}
impl Sin for $t {
fn sin(&self) -> Self {
<$t>::sin(*self)
}
}
impl Sqrt for $t {
fn sqrt(&self) -> Self {
<$t>::sqrt(*self)
}
}
impl Zero for $t {
fn zero() -> Self {
$z
}
fn is_zero(&self) -> bool {
*self == $z
}
}
impl One for $t {
fn one() -> Self {
$o
}
#[allow(clippy::float_cmp)]
fn is_one(&self) -> bool {
*self == $o
}
}
impl Epsilon for $t {
fn epsilon() -> Self {
std::$id::EPSILON
}
}
impl Const for $t {
fn tau() -> Self {
std::$id::consts::TAU
}
}
impl NumCast for $t {
fn from(n: usize) -> Option<Self> {
if n == 0 {
return Some(0.);
}
let size: u32 = std::mem::size_of::<usize>() as u32 * 8;
let lz = n.leading_zeros();
let tz = n.trailing_zeros();
if size - (lz + tz) > <$t>::MANTISSA_DIGITS + 1 {
None
} else {
Some(n as $t)
}
}
}
};
}
impl_trait_float!(f32, f32, 0.0, 1.0);
impl_trait_float!(f64, f64, 0.0, 1.0);
macro_rules! impl_trait_signed {
($t:ty, $z:expr, $o:expr) => {
impl Abs for $t {
fn abs(&self) -> Self {
<$t>::abs(*self)
}
}
impl Max for $t {
fn max(self, other: Self) -> Self {
Ord::max(self, other)
}
}
impl Zero for $t {
fn zero() -> Self {
$z
}
fn is_zero(&self) -> bool {
*self == $z
}
}
impl One for $t {
fn one() -> Self {
$o
}
fn is_one(&self) -> bool {
*self == $o
}
}
impl NumCast for $t {
fn from(n: usize) -> Option<Self> {
<$t>::try_from(n).ok()
}
}
};
}
impl_trait_signed!(i8, 0, 1);
impl_trait_signed!(i16, 0, 1);
impl_trait_signed!(i32, 0, 1);
impl_trait_signed!(i64, 0, 1);
impl_trait_signed!(i128, 0, 1);
impl_trait_signed!(isize, 0, 1);
macro_rules! impl_trait_unsigned {
($t:ty, $z:expr, $o:expr) => {
impl Abs for $t {
fn abs(&self) -> Self {
*self
}
}
impl Max for $t {
fn max(self, other: Self) -> Self {
Ord::max(self, other)
}
}
impl Zero for $t {
fn zero() -> Self {
$z
}
fn is_zero(&self) -> bool {
*self == $z
}
}
impl One for $t {
fn one() -> Self {
$o
}
fn is_one(&self) -> bool {
*self == $o
}
}
impl NumCast for $t {
fn from(n: usize) -> Option<Self> {
<$t>::try_from(n).ok()
}
}
};
}
impl_trait_unsigned!(u8, 0, 1);
impl_trait_unsigned!(u16, 0, 1);
impl_trait_unsigned!(u32, 0, 1);
impl_trait_unsigned!(u64, 0, 1);
impl_trait_unsigned!(u128, 0, 1);
impl_trait_unsigned!(usize, 0, 1);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn float_one() {
assert!(f32::one().is_one());
}
#[test]
fn numeric_numcast() {
let a: f32 = NumCast::from(12).unwrap();
assert_eq!(12., a);
let a: f64 = NumCast::from(123_432).unwrap();
assert_eq!(123_432., a);
let a: Option<f32> = NumCast::from(8_000_000_001);
assert_eq!(None, a);
}
#[test]
fn numeric_numcast_f32_limit() {
let a: f32 = NumCast::from(0b1000_0000_0000_0000_0000_0000_1).unwrap();
assert_eq!(1. + 24.0_f32.exp2(), a);
let a: Option<f32> = NumCast::from(0b1000_0000_0000_0000_0000_0000_01);
assert_eq!(None, a);
}
#[test]
fn numeric_numcast_f64_limit() {
let a: f64 =
NumCast::from(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_01)
.unwrap();
assert_eq!(1. + 53.0_f64.exp2(), a);
let a: Option<f64> =
NumCast::from(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_001);
assert_eq!(None, a);
}
#[test]
fn signed_max() {
assert_eq!(12, Max::max(-4i32, 12));
}
#[test]
fn signed_one() {
assert!(i128::one().is_one());
}
#[test]
fn signed_numcast() {
let a: i8 = NumCast::from(17).unwrap();
assert_eq!(17, a);
let b: i16 = NumCast::from(17).unwrap();
assert_eq!(17, b);
let c: i32 = NumCast::from(17).unwrap();
assert_eq!(17, c);
let d: i64 = NumCast::from(17).unwrap();
assert_eq!(17, d);
let e: i128 = NumCast::from(17).unwrap();
assert_eq!(17, e);
let f: isize = NumCast::from(17).unwrap();
assert_eq!(17, f);
}
#[test]
fn unsigned_abs() {
assert_eq!(4, 4u32.abs());
}
#[test]
fn unsigned_max() {
assert_eq!(12, Max::max(4u32, 12u32));
}
#[test]
fn unsigned_zero() {
assert!(usize::zero().is_zero());
}
#[test]
fn unsigned_one() {
assert!(u128::one().is_one());
}
#[test]
fn unsigned_numcast() {
let a: u8 = NumCast::from(17).unwrap();
assert_eq!(17, a);
let b: u16 = NumCast::from(17).unwrap();
assert_eq!(17, b);
let c: u32 = NumCast::from(17).unwrap();
assert_eq!(17, c);
let d: u64 = NumCast::from(17).unwrap();
assert_eq!(17, d);
let e: u128 = NumCast::from(17).unwrap();
assert_eq!(17, e);
let f: usize = NumCast::from(17).unwrap();
assert_eq!(17, f);
}
}