use std::{fmt, ops}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] 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.correct_sign(); 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); } #[test] fn test_mul_basic() { let a = Fraction::new(2, 3).unwrap(); let b = Fraction::new(3, 4).unwrap(); let c = a * b; assert_eq!(c.num, 1); assert_eq!(c.den, 2); } #[test] fn test_mul_with_zero() { let a = Fraction::new(0, 5).unwrap(); let b = Fraction::new(3, 7).unwrap(); let c = a * b; assert_eq!(c.num, 0); assert_eq!(c.den, 1); } #[test] fn test_mul_negative_positive() { let a = Fraction::new(-2, 3).unwrap(); let b = Fraction::new(3, 5).unwrap(); let c = a * b; assert_eq!(c.num, -2); assert_eq!(c.den, 5); } #[test] fn test_mul_negative_negative() { let a = Fraction::new(-2, 3).unwrap(); let b = Fraction::new(-3, 5).unwrap(); let c = a * b; assert_eq!(c.num, 2); assert_eq!(c.den, 5); } #[test] fn test_mul_result_reduced() { let a = Fraction::new(4, 6).unwrap(); let b = Fraction::new(9, 12).unwrap(); let c = a * b; assert_eq!(c.num, 1); assert_eq!(c.den, 2); } #[test] fn test_mul_by_one() { let a = Fraction::new(5, 7).unwrap(); let b = Fraction::from(1); let c = a * b; assert_eq!(c.num, 5); assert_eq!(c.den, 7); } #[test] fn test_mul_large_numbers() { let a = Fraction::new(1000, 2000).unwrap(); let b = Fraction::new(3000, 4000).unwrap(); let c = a * b; assert_eq!(c.num, 3); assert_eq!(c.den, 8); } #[test] fn test_mul_commutative() { let a = Fraction::new(7, 9).unwrap(); let b = Fraction::new(2, 5).unwrap(); let c1 = a * b; let c2 = b * a; assert_eq!(c1.num, c2.num); assert_eq!(c1.den, c2.den); } #[test] fn test_mul_with_negative_denominator_input() { let a = Fraction::new(2, -3).unwrap(); let b = Fraction::new(3, 4).unwrap(); let c = a * b; assert_eq!(c.num, -1); assert_eq!(c.den, 2); } #[test] fn test_div_basic() { let a = Fraction::new(2, 3).unwrap(); let b = Fraction::new(4, 5).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, 5); assert_eq!(c.den, 6); } #[test] fn test_div_by_one() { let a = Fraction::new(7, 9).unwrap(); let b = Fraction::from(1); let c = (a / b).unwrap(); assert_eq!(c.num, 7); assert_eq!(c.den, 9); } #[test] fn test_div_self() { let a = Fraction::new(5, 8).unwrap(); let c = (a / a).unwrap(); assert_eq!(c.num, 1); assert_eq!(c.den, 1); } #[test] fn test_div_negative_positive() { let a = Fraction::new(-2, 3).unwrap(); let b = Fraction::new(4, 5).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, -5); assert_eq!(c.den, 6); } #[test] fn test_div_negative_negative() { let a = Fraction::new(-2, 3).unwrap(); let b = Fraction::new(-4, 5).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, 5); assert_eq!(c.den, 6); } #[test] fn test_div_result_reduced() { let a = Fraction::new(4, 6).unwrap(); let b = Fraction::new(8, 9).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, 3); assert_eq!(c.den, 4); } #[test] fn test_div_by_zero_error() { let a = Fraction::new(3, 5).unwrap(); let b = Fraction::new(0, 7).unwrap(); let result = a / b; assert!(matches!(result, Err(FractionError::DivisionByZero))); } #[test] fn test_div_zero_by_fraction() { let a = Fraction::new(0, 5).unwrap(); let b = Fraction::new(3, 7).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, 0); assert_eq!(c.den, 1); } #[test] fn test_div_large_numbers() { let a = Fraction::new(1000, 2000).unwrap(); let b = Fraction::new(3000, 4000).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, 2); assert_eq!(c.den, 3); } #[test] fn test_div_sign_normalization() { let a = Fraction::new(2, -3).unwrap(); let b = Fraction::new(4, 5).unwrap(); let c = (a / b).unwrap(); assert_eq!(c.num, -5); assert_eq!(c.den, 6); } #[test] fn test_reciprocal_basic() { let a = Fraction::new(2, 3).unwrap(); let r = a.reciprocal().unwrap(); assert_eq!(r.num, 3); assert_eq!(r.den, 2); } #[test] fn test_reciprocal_reduces() { let a = Fraction::new(4, 6).unwrap(); let r = a.reciprocal().unwrap(); assert_eq!(r.num, 3); assert_eq!(r.den, 2); } #[test] fn test_reciprocal_negative() { let a = Fraction::new(-2, 3).unwrap(); let r = a.reciprocal().unwrap(); assert_eq!(r.num, -3); assert_eq!(r.den, 2); } #[test] fn test_reciprocal_negative_denominator_input() { let a = Fraction::new(2, -3).unwrap(); let r = a.reciprocal().unwrap(); assert_eq!(r.num, -3); assert_eq!(r.den, 2); } #[test] fn test_reciprocal_of_one() { let a = Fraction::from(1); let r = a.reciprocal().unwrap(); assert_eq!(r.num, 1); assert_eq!(r.den, 1); } #[test] fn test_reciprocal_of_negative_one() { let a = Fraction::new(-1, 1).unwrap(); let r = a.reciprocal().unwrap(); assert_eq!(r.num, -1); assert_eq!(r.den, 1); } #[test] fn test_reciprocal_of_zero_error() { let a = Fraction::new(0, 5).unwrap(); let r = a.reciprocal(); assert!(matches!(r, Err(FractionError::ZeroDenominator))); } #[test] fn test_reciprocal_twice_returns_original() { let a = Fraction::new(7, 9).unwrap(); let r = a.reciprocal().unwrap().reciprocal().unwrap(); assert_eq!(r.num, a.num); assert_eq!(r.den, a.den); } #[test] fn test_from_i32_basic() { let f = Fraction::from(5i32); assert_eq!(f.num, 5); assert_eq!(f.den, 1); } #[test] fn test_from_i32_negative() { let f = Fraction::from(-7i32); assert_eq!(f.num, -7); assert_eq!(f.den, 1); } #[test] fn test_from_i64_basic() { let f = Fraction::from(10i64); assert_eq!(f.num, 10); assert_eq!(f.den, 1); } #[test] fn test_from_i64_zero() { let f = Fraction::from(0i64); assert_eq!(f.num, 0); assert_eq!(f.den, 1); } #[test] fn test_try_from_i32_tuple_valid() { let f = Fraction::try_from((6i32, 8i32)).unwrap(); assert_eq!(f.num, 6); assert_eq!(f.den, 8); } #[test] fn test_try_from_i32_tuple_zero_denominator() { let f = Fraction::try_from((1i32, 0i32)); assert!(matches!(f, Err(FractionError::ZeroDenominator))); } #[test] fn test_try_from_i32_tuple_negative_denominator() { let f = Fraction::try_from((3i32, -4i32)).unwrap(); assert_eq!(f.num, 3); assert_eq!(f.den, -4); } #[test] fn test_try_from_i64_tuple_valid() { let f = Fraction::try_from((15i64, 20i64)).unwrap(); assert_eq!(f.num, 15); assert_eq!(f.den, 20); } #[test] fn test_try_from_i64_tuple_zero_denominator() { let f = Fraction::try_from((1i64, 0i64)); assert!(matches!(f, Err(FractionError::ZeroDenominator))); } #[test] fn test_try_from_i64_tuple_negative_values() { let f = Fraction::try_from((-3i64, -5i64)).unwrap(); assert_eq!(f.num, -3); assert_eq!(f.den, -5); } #[test] fn test_try_into_i64_valid() { let f = Fraction::new(8, 1).unwrap(); let value: i64 = f.try_into().unwrap(); assert_eq!(value, 8); } #[test] fn test_try_into_i64_invalid_fraction() { let f = Fraction::new(3, 2).unwrap(); let result: Result = f.try_into(); assert!(matches!(result, Err(FractionError::InvalidInteger))); } #[test] fn test_try_into_i64_negative() { let f = Fraction::new(-10, 1).unwrap(); let value: i64 = f.try_into().unwrap(); assert_eq!(value, -10); } }