2026-04-27 17:04:41 -06:00
|
|
|
use std::{fmt, ops};
|
|
|
|
|
|
2026-04-27 16:48:37 -06:00
|
|
|
pub struct Fraction {
|
|
|
|
|
num: i64,
|
|
|
|
|
den: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum FractionError {
|
|
|
|
|
DivisionByZero,
|
|
|
|
|
ZeroDenominator,
|
|
|
|
|
Overflow,
|
|
|
|
|
}
|
|
|
|
|
|
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"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
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),
|
|
|
|
|
den: self.num * other.den,
|
|
|
|
|
};
|
|
|
|
|
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),
|
|
|
|
|
den: self.num * other.den,
|
|
|
|
|
};
|
|
|
|
|
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 {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<i32> for Fraction {
|
|
|
|
|
fn from(value: i32) -> Self {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<i64> for Fraction {
|
|
|
|
|
fn from(value: i64) -> Self {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<(i32, i32)> for Fraction {
|
|
|
|
|
type Error = FractionError;
|
|
|
|
|
|
|
|
|
|
fn try_from(value: (i32, i32)) -> Result<Self, Self::Error> {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<(i64, i64)> for Fraction {
|
|
|
|
|
type Error = FractionError;
|
|
|
|
|
|
|
|
|
|
fn try_from(value: (i64, i64)) -> Result<Self, Self::Error> {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
2026-04-27 16:37:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
}
|