2024-09-02 16:15:17 -07:00
|
|
|
#[derive(Clone,Copy,Debug,Hash)]
|
|
|
|
pub struct Ratio<Num,Den>{
|
2024-09-10 13:04:43 -07:00
|
|
|
pub num:Num,
|
|
|
|
pub den:Den,
|
2024-09-02 16:15:17 -07:00
|
|
|
}
|
|
|
|
impl<Num,Den> Ratio<Num,Den>{
|
2024-09-10 13:04:43 -07:00
|
|
|
#[inline(always)]
|
2024-09-02 16:15:17 -07:00
|
|
|
pub const fn new(num:Num,den:Den)->Self{
|
|
|
|
Self{num,den}
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 13:04:43 -07:00
|
|
|
|
2024-09-11 12:59:33 -07:00
|
|
|
/// The actual divide implementation, Div is replaced with a Ratio constructor
|
|
|
|
pub trait Divide<Rhs=Self>{
|
|
|
|
type Output;
|
|
|
|
fn divide(self,rhs:Rhs)->Self::Output;
|
|
|
|
}
|
|
|
|
|
2024-09-10 13:04:43 -07:00
|
|
|
impl<Num,Den> Ratio<Num,Den>
|
|
|
|
where
|
2024-09-11 12:59:33 -07:00
|
|
|
Num:Divide<Den>,
|
2024-09-10 13:04:43 -07:00
|
|
|
{
|
|
|
|
#[inline]
|
2024-09-11 12:59:33 -07:00
|
|
|
pub fn divide(self)-><Num as Divide<Den>>::Output{
|
|
|
|
self.num.divide(self.den)
|
2024-09-10 13:04:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//take care to use the ratio methods to avoid nested ratios
|
|
|
|
|
|
|
|
impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
|
|
|
|
#[inline]
|
|
|
|
pub fn mul_ratio<RhsNum,RhsDen>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsNum as core::ops::Mul<RhsNum>>::Output,<LhsDen as core::ops::Mul<RhsDen>>::Output>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::Mul<RhsNum>,
|
|
|
|
LhsDen:core::ops::Mul<RhsDen>,
|
|
|
|
{
|
|
|
|
Ratio::new(self.num*rhs.num,self.den*rhs.den)
|
|
|
|
}
|
|
|
|
#[inline]
|
|
|
|
pub fn div_ratio<RhsNum,RhsDen>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsNum as core::ops::Mul<RhsDen>>::Output,<LhsDen as core::ops::Mul<RhsNum>>::Output>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::Mul<RhsDen>,
|
|
|
|
LhsDen:core::ops::Mul<RhsNum>,
|
|
|
|
{
|
|
|
|
Ratio::new(self.num*rhs.den,self.den*rhs.num)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
macro_rules! impl_ratio_method {
|
|
|
|
($trait:ident, $method:ident, $ratio_method:ident) => {
|
|
|
|
impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
|
|
|
|
#[inline]
|
|
|
|
pub fn $ratio_method<RhsNum,RhsDen,LhsCrossMul,RhsCrossMul>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsCrossMul as core::ops::$trait<RhsCrossMul>>::Output,<LhsDen as core::ops::Mul<RhsDen>>::Output>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::Mul<RhsDen,Output=LhsCrossMul>,
|
|
|
|
LhsDen:core::ops::Mul<RhsNum,Output=RhsCrossMul>,
|
|
|
|
LhsDen:core::ops::Mul<RhsDen>,
|
|
|
|
LhsDen:Copy,
|
|
|
|
RhsDen:Copy,
|
|
|
|
LhsCrossMul:core::ops::$trait<RhsCrossMul>,
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2024-09-23 11:20:11 -07:00
|
|
|
/// 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<LhsNum,LhsDen:Parity> Ratio<LhsNum,LhsDen>{
|
2024-09-16 11:46:05 -07:00
|
|
|
#[inline]
|
2024-09-23 11:20:11 -07:00
|
|
|
pub fn $ratio_method<RhsNum,RhsDen:Parity,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
|
2024-09-16 11:46:05 -07:00
|
|
|
where
|
|
|
|
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
|
|
|
LhsDen:core::ops::Mul<RhsNum,Output=T>,
|
|
|
|
T:Ord,
|
|
|
|
{
|
2024-09-23 11:20:11 -07:00
|
|
|
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)),
|
|
|
|
}
|
2024-09-16 11:46:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//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<core::cmp::Ordering>);
|
|
|
|
//Ord
|
|
|
|
impl_ratio_ord_method!(cmp,cmp_ratio,core::cmp::Ordering);
|
|
|
|
|
2024-09-10 13:04:43 -07:00
|
|
|
/* generic rhs mul is not possible!
|
|
|
|
impl<Lhs,RhsNum,RhsDen> core::ops::Mul<Ratio<RhsNum,RhsDen>> for Lhs
|
|
|
|
where
|
|
|
|
Lhs:core::ops::Mul<RhsNum>,
|
|
|
|
{
|
|
|
|
type Output=Ratio<<Lhs as core::ops::Mul<RhsNum>>::Output,RhsDen>;
|
|
|
|
#[inline]
|
|
|
|
fn mul(self,rhs:Ratio<RhsNum,RhsDen>)->Self::Output{
|
|
|
|
Ratio::new(self*rhs.num,rhs.den)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
//operators
|
|
|
|
|
|
|
|
impl<LhsNum,LhsDen> core::ops::Neg for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::Neg,
|
|
|
|
{
|
|
|
|
type Output=Ratio<<LhsNum as core::ops::Neg>::Output,LhsDen>;
|
|
|
|
#[inline]
|
|
|
|
fn neg(self)->Self::Output{
|
|
|
|
Ratio::new(-self.num,self.den)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<LhsNum,LhsDen,Rhs> core::ops::Mul<Rhs> for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::Mul<Rhs>,
|
|
|
|
{
|
|
|
|
type Output=Ratio<<LhsNum as core::ops::Mul<Rhs>>::Output,LhsDen>;
|
|
|
|
#[inline]
|
|
|
|
fn mul(self,rhs:Rhs)->Self::Output{
|
|
|
|
Ratio::new(self.num*rhs,self.den)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<LhsNum,LhsDen,Rhs> core::ops::Div<Rhs> for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsDen:core::ops::Mul<Rhs>,
|
|
|
|
{
|
|
|
|
type Output=Ratio<LhsNum,<LhsDen as core::ops::Mul<Rhs>>::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<LhsNum,LhsDen,Rhs,Intermediate> core::ops::$trait<Rhs> for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::$trait<Intermediate>,
|
|
|
|
LhsDen:Copy,
|
|
|
|
Rhs:core::ops::Mul<LhsDen,Output=Intermediate>,
|
|
|
|
{
|
|
|
|
type Output=Ratio<<LhsNum as core::ops::$trait<Intermediate>>::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<LhsNum,LhsDen,Rhs> core::ops::MulAssign<Rhs> for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::MulAssign<Rhs>,
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn mul_assign(&mut self,rhs:Rhs){
|
|
|
|
self.num*=rhs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<LhsNum,LhsDen,Rhs> core::ops::DivAssign<Rhs> for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsDen:core::ops::MulAssign<Rhs>,
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn div_assign(&mut self,rhs:Rhs){
|
|
|
|
self.den*=rhs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_ratio_assign_operator {
|
|
|
|
($trait:ident, $method:ident) => {
|
|
|
|
impl<LhsNum,LhsDen,Rhs> core::ops::$trait<Rhs> for Ratio<LhsNum,LhsDen>
|
|
|
|
where
|
|
|
|
LhsNum:core::ops::$trait,
|
|
|
|
LhsDen:Copy,
|
|
|
|
Rhs:core::ops::Mul<LhsDen,Output=LhsNum>,
|
|
|
|
{
|
|
|
|
#[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);
|
2024-09-13 14:00:13 -07:00
|
|
|
|
|
|
|
// Only implement PartialEq<Self>
|
|
|
|
// Rust's operators aren't actually that good
|
|
|
|
|
2024-10-03 12:25:39 -07:00
|
|
|
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
|
2024-09-13 14:00:13 -07:00
|
|
|
where
|
2024-10-03 12:25:39 -07:00
|
|
|
LhsNum:Copy,
|
|
|
|
LhsDen:Copy,
|
|
|
|
RhsNum:Copy,
|
|
|
|
RhsDen:Copy,
|
|
|
|
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
|
|
|
RhsNum:core::ops::Mul<LhsDen,Output=U>,
|
|
|
|
T:PartialEq<U>,
|
2024-09-13 14:00:13 -07:00
|
|
|
{
|
|
|
|
#[inline]
|
2024-10-03 12:25:39 -07:00
|
|
|
fn eq(&self,other:&Ratio<RhsNum,RhsDen>)->bool{
|
2024-09-13 14:00:13 -07:00
|
|
|
(self.num*other.den).eq(&(other.num*self.den))
|
|
|
|
}
|
|
|
|
}
|
2024-10-03 12:31:41 -07:00
|
|
|
impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}
|
2024-09-13 14:00:13 -07:00
|
|
|
|
2024-10-03 12:25:39 -07:00
|
|
|
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
|
2024-09-13 14:00:13 -07:00
|
|
|
where
|
2024-10-03 12:25:39 -07:00
|
|
|
LhsNum:Copy,
|
|
|
|
LhsDen:Copy,
|
|
|
|
RhsNum:Copy,
|
|
|
|
RhsDen:Copy,
|
|
|
|
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
|
|
|
RhsNum:core::ops::Mul<LhsDen,Output=U>,
|
|
|
|
T:PartialOrd<U>,
|
2024-09-13 14:00:13 -07:00
|
|
|
{
|
|
|
|
#[inline]
|
2024-10-03 12:25:39 -07:00
|
|
|
fn partial_cmp(&self,other:&Ratio<RhsNum,RhsDen>)->Option<core::cmp::Ordering>{
|
2024-09-13 14:00:13 -07:00
|
|
|
(self.num*other.den).partial_cmp(&(other.num*self.den))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<Num,Den,T> Ord for Ratio<Num,Den>
|
|
|
|
where
|
|
|
|
Num:Copy,
|
|
|
|
Den:Copy,
|
|
|
|
Num:core::ops::Mul<Den,Output=T>,
|
|
|
|
T:Ord,
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn cmp(&self,other:&Self)->std::cmp::Ordering{
|
|
|
|
(self.num*other.den).cmp(&(other.num*self.den))
|
|
|
|
}
|
|
|
|
}
|