use std::{fmt, ops}; #[derive(Debug, PartialEq, Eq)] pub struct Fraction { num: i64, den: i64, } #[derive(Debug, PartialEq, Eq)] pub enum FractionError { DivisionByZero, ZeroDenominator, Overflow, InvalidInteger, } 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"), FractionError::InvalidInteger => write!(f, "Can't convert to integer"), } } } 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.den * 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.den * 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 { write!(f, "{0}/{1}", self.num, self.den) } } impl From for Fraction { fn from(value: i32) -> Self { Fraction { num: value.into(), den: 1, } } } impl From for Fraction { fn from(value: i64) -> Self { Fraction { num: value, den: 1 } } } impl TryFrom<(i32, i32)> for Fraction { type Error = FractionError; fn try_from(value: (i32, i32)) -> Result { if value.1 == 0 { return Err(FractionError::ZeroDenominator); } Ok(Fraction { num: value.0.into(), den: value.1.into(), }) } } impl TryFrom<(i64, i64)> for Fraction { type Error = FractionError; fn try_from(value: (i64, i64)) -> Result { if value.1 == 0 { return Err(FractionError::ZeroDenominator); } Ok(Fraction { num: value.0, den: value.1, }) } } impl TryInto for Fraction { type Error = FractionError; fn try_into(self) -> Result { if self.den != 1 { return Err(FractionError::InvalidInteger); } Ok(self.num) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_new_valid_fraction() { let f = Fraction::new(2, 4).unwrap(); assert_eq!(f.num, 1); assert_eq!(f.den, 2); } #[test] fn test_new_zero_denominator_error() { let f = Fraction::new(2, 0); assert!(matches!(f, Err(FractionError::ZeroDenominator))); } #[test] fn test_new_reduces_fraction() { let f = Fraction::new(256, 512).unwrap(); assert_eq!(f.num, 1); assert_eq!(f.den, 2); } #[test] fn test_new_normalizes_signs() { let f = Fraction::new(1, -2).unwrap(); assert_eq!(f.num, -1); assert_eq!(f.den, 2); } #[test] fn test_add_basic() { let a = Fraction::new(7, 13).unwrap(); let b = Fraction::new(4, 7).unwrap(); let c = a + b; assert_eq!(c.num, 101); assert_eq!(c.den, 91); } #[test] fn test_add_with_common_denominator() { let a = Fraction::new(7, 13).unwrap(); let b = Fraction::new(4, 13).unwrap(); let c = a + b; assert_eq!(c.num, 11); assert_eq!(c.den, 13); } #[test] fn test_add_positive_and_negative() { let a = Fraction::new(-32, 237).unwrap(); let b = Fraction::new(22, 44).unwrap(); let c = a + b; assert_eq!(c.num, 173); assert_eq!(c.den, 474); } #[test] fn test_add_negative_and_negative() { let a = Fraction::new(-500, 12).unwrap(); let b = Fraction::new(-22, 4).unwrap(); let c = a + b; assert_eq!(c.num, -283); assert_eq!(c.den, 6); } #[test] fn test_add_with_zero() { let a = Fraction::new(0, 13).unwrap(); let b = Fraction::new(4, 7).unwrap(); let c = a + b; assert_eq!(c.num, 4); assert_eq!(c.den, 7); } #[test] fn test_sub_basic() { let a = Fraction::new(7, 13).unwrap(); let b = Fraction::new(4, 7).unwrap(); let c = a - b; assert_eq!(c.num, -3); assert_eq!(c.den, 91); } #[test] fn test_sub_with_common_denominator() { let a = Fraction::new(7, 13).unwrap(); let b = Fraction::new(4, 13).unwrap(); let c = a - b; assert_eq!(c.num, 3); assert_eq!(c.den, 13); } #[test] fn test_sub_positive_and_negative() { let a = Fraction::new(-32, 237).unwrap(); let b = Fraction::new(22, 44).unwrap(); let c = a - b; assert_eq!(c.num, -301); assert_eq!(c.den, 474); } #[test] fn test_sub_negative_and_negative() { let a = Fraction::new(-500, 12).unwrap(); let b = Fraction::new(-22, 4).unwrap(); let c = a - b; assert_eq!(c.num, -217); assert_eq!(c.den, 6); } #[test] fn test_sub_with_zero() { let a = Fraction::new(0, 13).unwrap(); let b = Fraction::new(4, 7).unwrap(); let c = a - b; assert_eq!(c.num, -4); assert_eq!(c.den, 7); } #[test] fn test_sub_result_zero() { let a = Fraction::new(5, 9).unwrap(); let b = Fraction::new(5, 9).unwrap(); let c = a - b; assert_eq!(c.num, 0); assert_eq!(c.den, 1); } }