use crate::ConversionError;
use core::ops;
use num::{rational::Ratio, CheckedDiv, CheckedMul, Zero};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Fraction(Ratio<u32>);
impl Fraction {
pub const fn new(numerator: u32, denominator: u32) -> Self {
Self(Ratio::new_raw(numerator, denominator))
}
pub const fn numerator(&self) -> &u32 {
self.0.numer()
}
pub const fn denominator(&self) -> &u32 {
self.0.denom()
}
}
impl Fraction {
pub fn new_reduce(numerator: u32, denominator: u32) -> Result<Self, ConversionError> {
if !denominator.is_zero() {
Ok(Self(Ratio::new(numerator, denominator)))
} else {
Err(ConversionError::DivByZero)
}
}
pub fn to_integer(&self) -> u32 {
self.0.to_integer()
}
pub fn from_integer(value: u32) -> Self {
Self(Ratio::from_integer(value))
}
pub fn recip(self) -> Self {
Self(self.0.recip())
}
pub fn checked_mul(&self, v: &Self) -> Option<Self> {
self.0.checked_mul(&v.0).map(Self)
}
pub fn checked_div(&self, v: &Self) -> Option<Self> {
self.0.checked_div(&v.0).map(Self)
}
}
impl ops::Mul<Fraction> for u32 {
type Output = Self;
fn mul(self, rhs: Fraction) -> Self::Output {
(rhs.0 * self).to_integer()
}
}
impl ops::Div<Fraction> for u32 {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Fraction) -> Self::Output {
(rhs.0.recip() * self).to_integer()
}
}
impl ops::Mul<Fraction> for u64 {
type Output = Self;
fn mul(self, rhs: Fraction) -> Self::Output {
(Ratio::new_raw((*rhs.numerator()).into(), (*rhs.denominator()).into()) * self).to_integer()
}
}
impl ops::Div<Fraction> for u64 {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Fraction) -> Self::Output {
(Ratio::new_raw((*rhs.denominator()).into(), (*rhs.numerator()).into()) * self).to_integer()
}
}
impl ops::Mul for Fraction {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl ops::Div for Fraction {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl Default for Fraction {
fn default() -> Self {
Self::new(1, 1)
}
}
#[cfg(test)]
mod tests {}