#[derive(Clone,Copy,Debug,Hash)]
pub struct Ratio<Num,Den>{
	pub num:Num,
	pub den:Den,
}
impl<Num,Den> Ratio<Num,Den>{
	#[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<Rhs=Self>{
	type Output;
	fn divide(self,rhs:Rhs)->Self::Output;
}

impl<Num,Den> Ratio<Num,Den>
	where
		Num:Divide<Den>,
{
	#[inline]
	pub fn divide(self)-><Num as Divide<Den>>::Output{
		self.num.divide(self.den)
	}
}

//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);

/// 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>{
			#[inline]
			pub fn $ratio_method<RhsNum,RhsDen:Parity,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
				where
					LhsNum:core::ops::Mul<RhsDen,Output=T>,
					LhsDen:core::ops::Mul<RhsNum,Output=T>,
					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<core::cmp::Ordering>);
//Ord
impl_ratio_ord_method!(cmp,cmp_ratio,core::cmp::Ordering);

/* 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);

// Only implement PartialEq<Self>
// Rust's operators aren't actually that good

impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
	where
		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>,
{
	#[inline]
	fn eq(&self,other:&Ratio<RhsNum,RhsDen>)->bool{
		(self.num*other.den).eq(&(other.num*self.den))
	}
}
impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}

impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
	where
		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>,
{
	#[inline]
	fn partial_cmp(&self,other:&Ratio<RhsNum,RhsDen>)->Option<core::cmp::Ordering>{
		(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))
	}
}
