use crate::error::MatrixError;
use crate::number::Number;
use crate::number::One;
use crate::number::Zero;
#[cfg(feature = "serde_mat")]
use serde::{Deserialize, Serialize};
pub trait Integeral
where
Self: Number + std::cmp::PartialOrd + std::ops::Rem<Output = Self>,
{
fn gcd(&self, rhs: &Self) -> Self {
if rhs.to_owned() == Self::zero() {
self.to_owned().abs()
} else {
rhs.gcd(&(self.to_owned().rem(rhs.to_owned())))
}
}
fn lcm(&self, rhs: &Self) -> Self {
match (self.to_owned() * rhs.to_owned()).abs().ndiv(self.gcd(rhs)) {
Ok(division) => division,
Err(_) => -Self::one(),
}
}
}
impl Integeral for i32 {}
impl Integeral for i128 {}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde_mat", derive(Serialize, Deserialize))]
pub struct Rational<T: Integeral> {
numerator: T,
denominator: T,
}
impl<T: Integeral> Rational<T> {
pub fn create(numerator: T, denominator: T) -> Self {
Rational {
numerator,
denominator,
}
}
pub fn refine(&self) -> Result<Self, MatrixError> {
let gcd = self.numerator.gcd(&self.denominator);
let mut numerator = self.numerator.to_owned().ndiv(gcd.to_owned())?;
let mut denominator = self.denominator.to_owned().ndiv(gcd)?;
if (numerator < T::zero() && denominator < T::zero())
|| (numerator > T::zero() && denominator < T::zero())
{
numerator = -numerator;
denominator = -denominator;
} else if self.numerator.is_zero() {
numerator = T::zero();
denominator = T::one();
}
Ok(Self::create(numerator, denominator))
}
}
#[macro_export]
macro_rules! rat {
($x:expr, $y:expr) => {
Rational::create($x, $y)
};
}
impl<T: Integeral> std::fmt::Display for Rational<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let refined = match self.refine() {
Ok(v) => v,
Err(_) => Rational::create(T::zero(), T::zero()),
};
write!(f, "{}/{}", refined.numerator, refined.denominator)
}
}
impl<T: Integeral> std::ops::Neg for Rational<T> {
type Output = Self;
fn neg(self) -> Self::Output {
Self::create(-self.numerator, self.denominator)
}
}
impl<T: Integeral> std::ops::Add for Rational<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let numerator = self.numerator * rhs.denominator.to_owned()
+ rhs.numerator * self.denominator.to_owned();
let denominator = self.denominator * rhs.denominator;
Self::create(numerator, denominator)
}
}
impl<T: Integeral> std::iter::Sum for Rational<T> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), |acc, e| acc + e)
}
}
impl<T: Integeral> std::ops::Sub for Rational<T> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let numerator = self.numerator * rhs.denominator.to_owned()
- rhs.numerator * self.denominator.to_owned();
let denominator = self.denominator * rhs.denominator;
Self::create(numerator, denominator)
}
}
impl<T: Integeral> std::ops::Mul for Rational<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let numerator = self.numerator * rhs.numerator;
let denominator = self.denominator * rhs.denominator;
Self::create(numerator, denominator)
}
}
impl<T: Integeral> Default for Rational<T> {
fn default() -> Self {
Self {
numerator: T::zero(),
denominator: T::one(),
}
}
}
impl<T: Integeral> Zero for Rational<T> {
fn is_zero(&self) -> bool {
self.numerator.is_zero() && !self.denominator.is_zero()
}
}
impl<T: Integeral> One for Rational<T> {
fn one() -> Self {
Self::create(T::one(), T::one())
}
fn is_one(&self) -> bool {
self.numerator.gcd(&self.denominator).is_one()
}
}
impl<T: Integeral> Number for Rational<T> {
fn abs(self) -> Self {
Self::create(self.numerator.abs(), self.denominator.abs())
}
fn ndiv(self, rhs: Self) -> Result<Self, crate::error::MatrixError> {
if rhs.is_zero() {
Err(MatrixError::DividedByZero)
} else {
Ok(self * Self::create(rhs.denominator, rhs.numerator))
}
}
}