use num::traits::Zero;
pub trait Category {
type D;
fn category(&self) -> Self::D;
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum CatFloat {
IntegerLike(f64),
FractionLike(f64),
IntegerAndFractionalPart(f64, f64),
Nan,
Infinity,
}
impl CatFloat {
pub fn is_integer_like(&self) -> bool {
matches!(self, Self::IntegerLike(..))
}
pub fn is_fraction_like(&self) -> bool {
matches!(self, Self::FractionLike(..))
}
pub fn is_integer_and_fractional_part(&self) -> bool {
matches!(self, Self::IntegerAndFractionalPart(..))
}
pub fn is_infinity(&self) -> bool {
matches!(self, Self::Infinity)
}
pub fn is_nan(&self) -> bool {
matches!(self, Self::Nan)
}
}
impl Category for f64 {
type D = CatFloat;
fn category(&self) -> Self::D {
if self.is_infinite() {
return CatFloat::Infinity;
}
if self.is_nan() {
return CatFloat::Nan;
}
let int_part: f64 = self.trunc();
let fract_part: f64 = self.fract();
if fract_part.is_zero() {
CatFloat::IntegerLike(int_part)
} else if int_part.is_zero() {
CatFloat::FractionLike(fract_part)
} else {
CatFloat::IntegerAndFractionalPart(int_part, fract_part)
}
}
}
#[test]
fn trait_works() {
use crate::*;
let f: f64 = 1.5;
assert_eq!(f.category(), CatFloat::IntegerAndFractionalPart(1.0, 0.5));
let f: f64 = 1.0;
assert_eq!(f.category(), CatFloat::IntegerLike(1.0));
let f: f64 = 0.2;
assert_eq!(f.category(), CatFloat::FractionLike(0.2));
let f: f64 = f64::INFINITY;
assert_eq!(f.category(), CatFloat::Infinity,);
let f: f64 = f64::NAN;
assert_eq!(f.category(), CatFloat::Nan);
}