use std::{fmt, ops}; pub struct Fraction { num: i64, den: i64, } #[derive(Debug)] pub enum FractionError { DivisionByZero, ZeroDenominator, Overflow, } impl fmt::Display for FractionError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FractionError::DivisionByZero => write!(f, "Division by zero"), FractionError::ZeroDenominator => write!(f, "Denominator can't be zero"), FractionError::Overflow => write!(f, "Numeric overflow"), } } } impl std::error::Error for FractionError {} impl Fraction { pub fn new(num: i64, den: i64) -> Result { if den == 0 { return Err(FractionError::ZeroDenominator); } let mut new = Fraction { num, den }; new.reduce(); new.correct_sign(); Ok(new) } pub fn reciprocal(&self) -> Result { Fraction::new(self.den, self.num) } pub fn abs(&self) -> Self { Fraction { num: self.num.abs(), den: self.den, } } pub fn is_zero(&self) -> bool { self.num == 0 } pub fn is_integer(&self) -> bool { self.den == 1 } fn gcd(a: i64, b: i64) -> i64 { if a == 0 { return b; } Fraction::gcd(b % a, a) } fn reduce(&mut self) { let gdc = Fraction::gcd(self.num, self.den); self.num /= gdc; self.den /= gdc; } fn correct_sign(&mut self) { if self.num < 0 && self.den < 0 { self.num = self.num.abs(); self.den = self.den.abs(); } else if self.den < 0 { self.num = -self.num; self.den = self.den.abs(); } } } impl ops::Mul for Fraction { type Output = Self; fn mul(self, other: Self) -> Self::Output { let mut new = Fraction { num: self.num * other.num, den: self.den * other.den, }; new.reduce(); new } } impl ops::Add for Fraction { type Output = Self; fn add(self, other: Self) -> Self::Output { let mut new = Fraction { num: (self.num * other.den) + (self.den * other.num), den: self.num * other.den, }; new.reduce(); new.correct_sign(); new } } impl ops::Div for Fraction { type Output = Result; fn div(self, other: Self) -> Self::Output { if other.is_zero() { return Err(FractionError::DivisionByZero); } let mut new = Fraction { num: self.num * other.den, den: self.den * other.num, }; new.reduce(); new.correct_sign(); Ok(new) } } impl ops::Sub for Fraction { type Output = Self; fn sub(self, other: Self) -> Self::Output { let mut new = Fraction { num: (self.num * other.den) - (self.den * other.num), den: self.num * other.den, }; new.reduce(); new.correct_sign(); new } } impl ops::Neg for Fraction { type Output = Self; fn neg(self) -> Self::Output { Fraction { num: -self.num, den: self.den, } } } impl fmt::Display for Fraction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { todo!() } } impl From for Fraction { fn from(value: i32) -> Self { todo!() } } impl From for Fraction { fn from(value: i64) -> Self { todo!() } } impl TryFrom<(i32, i32)> for Fraction { type Error = FractionError; fn try_from(value: (i32, i32)) -> Result { todo!() } } impl TryFrom<(i64, i64)> for Fraction { type Error = FractionError; fn try_from(value: (i64, i64)) -> Result { todo!() } } #[cfg(test)] mod tests { use super::*; }