#[derive(Clone,Copy,Debug,Hash)] pub struct Ratio{ pub num:Num, pub den:Den, } impl Ratio{ #[inline(always)] pub const fn new(num:Num,den:Den)->Self{ Self{num,den} } } /// The actual divide implementation, Div is replaced with a Ratio constructor pub trait Divide{ type Output; fn divide(self,rhs:Rhs)->Self::Output; } impl Ratio where Num:Divide, { #[inline] pub fn divide(self)->>::Output{ self.num.divide(self.den) } } //take care to use the ratio methods to avoid nested ratios impl Ratio{ #[inline] pub fn mul_ratio(self,rhs:Ratio)->Ratio<>::Output,>::Output> where LhsNum:core::ops::Mul, LhsDen:core::ops::Mul, { Ratio::new(self.num*rhs.num,self.den*rhs.den) } #[inline] pub fn div_ratio(self,rhs:Ratio)->Ratio<>::Output,>::Output> where LhsNum:core::ops::Mul, LhsDen:core::ops::Mul, { Ratio::new(self.num*rhs.den,self.den*rhs.num) } } macro_rules! impl_ratio_method { ($trait:ident, $method:ident, $ratio_method:ident) => { impl Ratio{ #[inline] pub fn $ratio_method(self,rhs:Ratio)->Ratio<>::Output,>::Output> where LhsNum:core::ops::Mul, LhsDen:core::ops::Mul, LhsDen:core::ops::Mul, LhsDen:Copy, RhsDen:Copy, LhsCrossMul:core::ops::$trait, { Ratio::new((self.num*rhs.den).$method(self.den*rhs.num),self.den*rhs.den) } } }; } impl_ratio_method!(Add,add,add_ratio); impl_ratio_method!(Sub,sub,sub_ratio); impl_ratio_method!(Rem,rem,rem_ratio); /// Comparing two ratios needs to know the parity of the denominators /// For signed integers this can be implemented with is_negative() pub trait Parity{ fn parity(&self)->bool; } macro_rules! impl_parity_unsigned{ ($($type:ty),*)=>{ $( impl Parity for $type{ fn parity(&self)->bool{ false } } )* }; } macro_rules! impl_parity_signed{ ($($type:ty),*)=>{ $( impl Parity for $type{ fn parity(&self)->bool{ self.is_negative() } } )* }; } macro_rules! impl_parity_float{ ($($type:ty),*)=>{ $( impl Parity for $type{ fn parity(&self)->bool{ self.is_sign_negative() } } )* }; } impl_parity_unsigned!(u8,u16,u32,u64,u128,usize); impl_parity_signed!(i8,i16,i32,i64,i128,isize); impl_parity_float!(f32,f64); macro_rules! impl_ratio_ord_method{ ($method:ident, $ratio_method:ident, $output:ty)=>{ impl Ratio{ #[inline] pub fn $ratio_method(self,rhs:Ratio)->$output where LhsNum:core::ops::Mul, LhsDen:core::ops::Mul, T:Ord, { match self.den.parity()^rhs.den.parity(){ true=>(self.den*rhs.num).$method(&(self.num*rhs.den)), false=>(self.num*rhs.den).$method(&(self.den*rhs.num)), } } } } } //PartialEq impl_ratio_ord_method!(eq,eq_ratio,bool); //PartialOrd impl_ratio_ord_method!(lt,lt_ratio,bool); impl_ratio_ord_method!(gt,gt_ratio,bool); impl_ratio_ord_method!(le,le_ratio,bool); impl_ratio_ord_method!(ge,ge_ratio,bool); impl_ratio_ord_method!(partial_cmp,partial_cmp_ratio,Option); //Ord impl_ratio_ord_method!(cmp,cmp_ratio,core::cmp::Ordering); /* generic rhs mul is not possible! impl core::ops::Mul> for Lhs where Lhs:core::ops::Mul, { type Output=Ratio<>::Output,RhsDen>; #[inline] fn mul(self,rhs:Ratio)->Self::Output{ Ratio::new(self*rhs.num,rhs.den) } } */ //operators impl core::ops::Neg for Ratio where LhsNum:core::ops::Neg, { type Output=Ratio<::Output,LhsDen>; #[inline] fn neg(self)->Self::Output{ Ratio::new(-self.num,self.den) } } impl core::ops::Mul for Ratio where LhsNum:core::ops::Mul, { type Output=Ratio<>::Output,LhsDen>; #[inline] fn mul(self,rhs:Rhs)->Self::Output{ Ratio::new(self.num*rhs,self.den) } } impl core::ops::Div for Ratio where LhsDen:core::ops::Mul, { type Output=Ratio>::Output>; #[inline] fn div(self,rhs:Rhs)->Self::Output{ Ratio::new(self.num,self.den*rhs) } } macro_rules! impl_ratio_operator { ($trait:ident, $method:ident) => { impl core::ops::$trait for Ratio where LhsNum:core::ops::$trait, LhsDen:Copy, Rhs:core::ops::Mul, { type Output=Ratio<>::Output,LhsDen>; #[inline] fn $method(self,rhs:Rhs)->Self::Output{ Ratio::new(self.num.$method(rhs*self.den),self.den) } } }; } impl_ratio_operator!(Add,add); impl_ratio_operator!(Sub,sub); impl_ratio_operator!(Rem,rem); //assign operators impl core::ops::MulAssign for Ratio where LhsNum:core::ops::MulAssign, { #[inline] fn mul_assign(&mut self,rhs:Rhs){ self.num*=rhs; } } impl core::ops::DivAssign for Ratio where LhsDen:core::ops::MulAssign, { #[inline] fn div_assign(&mut self,rhs:Rhs){ self.den*=rhs; } } macro_rules! impl_ratio_assign_operator { ($trait:ident, $method:ident) => { impl core::ops::$trait for Ratio where LhsNum:core::ops::$trait, LhsDen:Copy, Rhs:core::ops::Mul, { #[inline] fn $method(&mut self,rhs:Rhs){ self.num.$method(rhs*self.den) } } }; } impl_ratio_assign_operator!(AddAssign,add_assign); impl_ratio_assign_operator!(SubAssign,sub_assign); impl_ratio_assign_operator!(RemAssign,rem_assign); // Only implement PartialEq // Rust's operators aren't actually that good impl PartialEq> for Ratio where LhsNum:Copy, LhsDen:Copy, RhsNum:Copy, RhsDen:Copy, LhsNum:core::ops::Mul, RhsNum:core::ops::Mul, T:PartialEq, { #[inline] fn eq(&self,other:&Ratio)->bool{ (self.num*other.den).eq(&(other.num*self.den)) } } impl Eq for Ratio where Self:PartialEq{} impl PartialOrd> for Ratio where LhsNum:Copy, LhsDen:Copy, RhsNum:Copy, RhsDen:Copy, LhsNum:core::ops::Mul, RhsNum:core::ops::Mul, T:PartialOrd, { #[inline] fn partial_cmp(&self,other:&Ratio)->Option{ (self.num*other.den).partial_cmp(&(other.num*self.den)) } } impl Ord for Ratio where Num:Copy, Den:Copy, Num:core::ops::Mul, T:Ord, { #[inline] fn cmp(&self,other:&Self)->std::cmp::Ordering{ (self.num*other.den).cmp(&(other.num*self.den)) } }