2026-04-29 19:37:40 -06:00
|
|
|
use std::{
|
|
|
|
|
fmt::{self, write},
|
|
|
|
|
ops,
|
|
|
|
|
};
|
2026-04-27 17:04:41 -06:00
|
|
|
|
2026-04-27 20:54:15 -06:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
2026-04-27 16:48:37 -06:00
|
|
|
pub struct Fraction {
|
|
|
|
|
num: i64,
|
|
|
|
|
den: i64,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 18:15:38 -06:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2026-04-27 16:48:37 -06:00
|
|
|
pub enum FractionError {
|
|
|
|
|
DivisionByZero,
|
|
|
|
|
ZeroDenominator,
|
|
|
|
|
Overflow,
|
2026-04-27 18:01:48 -06:00
|
|
|
InvalidInteger,
|
2026-04-27 16:48:37 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 17:04:41 -06:00
|
|
|
impl fmt::Display for FractionError {
|
2026-04-27 16:48:37 -06:00
|
|
|
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"),
|
2026-04-27 18:01:48 -06:00
|
|
|
FractionError::InvalidInteger => write!(f, "Can't convert to integer"),
|
2026-04-27 16:48:37 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::error::Error for FractionError {}
|
|
|
|
|
|
|
|
|
|
impl Fraction {
|
|
|
|
|
pub fn new(num: i64, den: i64) -> Result<Self, FractionError> {
|
|
|
|
|
if den == 0 {
|
|
|
|
|
return Err(FractionError::ZeroDenominator);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 17:27:05 -06:00
|
|
|
let mut new = Fraction { num, den };
|
|
|
|
|
|
|
|
|
|
new.reduce();
|
2026-04-27 17:48:57 -06:00
|
|
|
new.correct_sign();
|
2026-04-27 17:27:05 -06:00
|
|
|
|
|
|
|
|
Ok(new)
|
2026-04-27 16:48:37 -06:00
|
|
|
}
|
2026-04-27 17:04:41 -06:00
|
|
|
|
|
|
|
|
pub fn reciprocal(&self) -> Result<Self, FractionError> {
|
2026-04-27 17:48:57 -06:00
|
|
|
Fraction::new(self.den, self.num)
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn abs(&self) -> Self {
|
2026-04-27 17:48:57 -06:00
|
|
|
Fraction {
|
|
|
|
|
num: self.num.abs(),
|
|
|
|
|
den: self.den,
|
|
|
|
|
}
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_zero(&self) -> bool {
|
2026-04-27 17:48:57 -06:00
|
|
|
self.num == 0
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_integer(&self) -> bool {
|
2026-04-27 17:48:57 -06:00
|
|
|
self.den == 1
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gcd(a: i64, b: i64) -> i64 {
|
2026-04-27 17:34:41 -06:00
|
|
|
if a == 0 {
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
Fraction::gcd(b % a, a)
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn reduce(&mut self) {
|
2026-04-27 17:34:41 -06:00
|
|
|
let gdc = Fraction::gcd(self.num, self.den);
|
|
|
|
|
|
|
|
|
|
self.num /= gdc;
|
|
|
|
|
self.den /= gdc;
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
2026-04-27 17:27:05 -06:00
|
|
|
|
|
|
|
|
fn correct_sign(&mut self) {
|
2026-04-27 17:34:41 -06:00
|
|
|
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();
|
|
|
|
|
}
|
2026-04-27 17:27:05 -06:00
|
|
|
}
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Mul for Fraction {
|
|
|
|
|
type Output = Self;
|
|
|
|
|
|
2026-04-27 17:27:05 -06:00
|
|
|
fn mul(self, other: Self) -> Self::Output {
|
|
|
|
|
let mut new = Fraction {
|
|
|
|
|
num: self.num * other.num,
|
|
|
|
|
den: self.den * other.den,
|
|
|
|
|
};
|
|
|
|
|
new.reduce();
|
2026-04-27 18:48:56 -06:00
|
|
|
new.correct_sign();
|
2026-04-27 17:27:05 -06:00
|
|
|
new
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Add for Fraction {
|
|
|
|
|
type Output = Self;
|
|
|
|
|
|
2026-04-27 17:27:05 -06:00
|
|
|
fn add(self, other: Self) -> Self::Output {
|
|
|
|
|
let mut new = Fraction {
|
|
|
|
|
num: (self.num * other.den) + (self.den * other.num),
|
2026-04-27 18:38:57 -06:00
|
|
|
den: self.den * other.den,
|
2026-04-27 17:27:05 -06:00
|
|
|
};
|
|
|
|
|
new.reduce();
|
|
|
|
|
new.correct_sign();
|
|
|
|
|
new
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Div for Fraction {
|
|
|
|
|
type Output = Result<Self, FractionError>;
|
|
|
|
|
|
2026-04-27 17:27:05 -06:00
|
|
|
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)
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Sub for Fraction {
|
|
|
|
|
type Output = Self;
|
|
|
|
|
|
2026-04-27 17:27:05 -06:00
|
|
|
fn sub(self, other: Self) -> Self::Output {
|
|
|
|
|
let mut new = Fraction {
|
|
|
|
|
num: (self.num * other.den) - (self.den * other.num),
|
2026-04-27 18:44:28 -06:00
|
|
|
den: self.den * other.den,
|
2026-04-27 17:27:05 -06:00
|
|
|
};
|
|
|
|
|
new.reduce();
|
|
|
|
|
new.correct_sign();
|
|
|
|
|
new
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ops::Neg for Fraction {
|
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
|
|
fn neg(self) -> Self::Output {
|
2026-04-27 17:27:05 -06:00
|
|
|
Fraction {
|
|
|
|
|
num: -self.num,
|
|
|
|
|
den: self.den,
|
|
|
|
|
}
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Fraction {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2026-04-29 19:37:40 -06:00
|
|
|
if self.num > 1 {
|
|
|
|
|
return write!(f, "{0}/{1}", self.num, self.den);
|
|
|
|
|
} else {
|
|
|
|
|
return write!(f, "{0}", self.num);
|
|
|
|
|
}
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<i32> for Fraction {
|
|
|
|
|
fn from(value: i32) -> Self {
|
2026-04-27 17:57:32 -06:00
|
|
|
Fraction {
|
|
|
|
|
num: value.into(),
|
|
|
|
|
den: 1,
|
|
|
|
|
}
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<i64> for Fraction {
|
|
|
|
|
fn from(value: i64) -> Self {
|
2026-04-27 17:57:32 -06:00
|
|
|
Fraction { num: value, den: 1 }
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<(i32, i32)> for Fraction {
|
|
|
|
|
type Error = FractionError;
|
|
|
|
|
|
|
|
|
|
fn try_from(value: (i32, i32)) -> Result<Self, Self::Error> {
|
2026-04-27 17:57:32 -06:00
|
|
|
if value.1 == 0 {
|
|
|
|
|
return Err(FractionError::ZeroDenominator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Fraction {
|
|
|
|
|
num: value.0.into(),
|
|
|
|
|
den: value.1.into(),
|
|
|
|
|
})
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<(i64, i64)> for Fraction {
|
|
|
|
|
type Error = FractionError;
|
|
|
|
|
|
|
|
|
|
fn try_from(value: (i64, i64)) -> Result<Self, Self::Error> {
|
2026-04-27 17:57:32 -06:00
|
|
|
if value.1 == 0 {
|
|
|
|
|
return Err(FractionError::ZeroDenominator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Fraction {
|
|
|
|
|
num: value.0,
|
|
|
|
|
den: value.1,
|
|
|
|
|
})
|
2026-04-27 17:04:41 -06:00
|
|
|
}
|
2026-04-27 16:37:18 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 18:01:48 -06:00
|
|
|
impl TryInto<i64> for Fraction {
|
|
|
|
|
type Error = FractionError;
|
|
|
|
|
|
|
|
|
|
fn try_into(self) -> Result<i64, Self::Error> {
|
|
|
|
|
if self.den != 1 {
|
|
|
|
|
return Err(FractionError::InvalidInteger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(self.num)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 16:37:18 -06:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
2026-04-27 18:15:38 -06:00
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
}
|
2026-04-27 18:38:57 -06:00
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
}
|
2026-04-27 18:44:28 -06:00
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
}
|
2026-04-27 18:48:56 -06:00
|
|
|
|
|
|
|
|
#[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() {
|
2026-04-27 18:53:13 -06:00
|
|
|
let a = Fraction::new(2, -3).unwrap();
|
2026-04-27 18:48:56 -06:00
|
|
|
let b = Fraction::new(3, 4).unwrap();
|
|
|
|
|
let c = a * b;
|
|
|
|
|
assert_eq!(c.num, -1);
|
|
|
|
|
assert_eq!(c.den, 2);
|
|
|
|
|
}
|
2026-04-27 18:50:26 -06:00
|
|
|
|
|
|
|
|
#[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() {
|
2026-04-27 18:53:13 -06:00
|
|
|
let a = Fraction::new(4, 6).unwrap();
|
|
|
|
|
let b = Fraction::new(8, 9).unwrap();
|
2026-04-27 18:50:26 -06:00
|
|
|
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() {
|
2026-04-27 18:53:13 -06:00
|
|
|
let a = Fraction::new(1000, 2000).unwrap();
|
|
|
|
|
let b = Fraction::new(3000, 4000).unwrap();
|
2026-04-27 18:50:26 -06:00
|
|
|
let c = (a / b).unwrap();
|
|
|
|
|
assert_eq!(c.num, 2);
|
|
|
|
|
assert_eq!(c.den, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_div_sign_normalization() {
|
2026-04-27 18:53:13 -06:00
|
|
|
let a = Fraction::new(2, -3).unwrap();
|
2026-04-27 18:50:26 -06:00
|
|
|
let b = Fraction::new(4, 5).unwrap();
|
|
|
|
|
let c = (a / b).unwrap();
|
|
|
|
|
assert_eq!(c.num, -5);
|
|
|
|
|
assert_eq!(c.den, 6);
|
|
|
|
|
}
|
2026-04-27 18:53:13 -06:00
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
}
|
2026-04-27 18:56:48 -06:00
|
|
|
|
|
|
|
|
#[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<i64, _> = 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);
|
|
|
|
|
}
|
2026-04-27 16:37:18 -06:00
|
|
|
}
|