Created
March 15, 2023 19:38
-
-
Save wperron/ea7f0ed4704182800d814c76aeae9f26 to your computer and use it in GitHub Desktop.
Doing math with fractions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{collections::BTreeSet, fmt::Display, str::FromStr}; | |
/// Write a function that can do the 4 basic operations (add, subtract, multiply | |
/// and divide) on two fractions. Return the most simplified form of the result. | |
/// You can assume a non-zero denominator in the input, and don’t use any | |
/// built-in implementations in your language of choice, if you can! | |
/// | |
/// Example: | |
/// | |
/// ``` | |
/// > fractionMath("3/4", "add", "3/4") | |
/// > "3/2" | |
/// | |
/// > fractionMath("1/8", "multiply", "2/2") | |
/// > "1/8" | |
/// ``` | |
fn main() { | |
println!( | |
"{}", | |
fraction_math("2/5".to_owned(), Op::Add, "2/4".to_owned()) | |
); | |
} | |
enum Op { | |
Add, | |
Sub, | |
Multiply, | |
Divide, | |
} | |
#[derive(Debug, Clone, Eq, PartialEq)] | |
struct Fraction { | |
numerator: isize, | |
denominator: isize, | |
} | |
impl Fraction { | |
fn simplify(self) -> Self { | |
let numerator = self.numerator; | |
let denominator = self.denominator; | |
let num_factors = factors(numerator); | |
let den_factors = factors(denominator); | |
let (mut i, mut j) = (num_factors.len() - 1, den_factors.len() - 1); | |
while i < usize::MAX && j < usize::MAX { | |
if num_factors[i] == den_factors[j] { | |
let common = num_factors[i]; | |
return Self { | |
numerator: numerator / common, | |
denominator: denominator / common, | |
}; | |
} | |
if i > j { | |
i -= 1; | |
} else { | |
j -= 1; | |
} | |
} | |
// there's no way to simplify this fraction, so we just return it | |
self.clone() | |
} | |
fn to_denominator(&mut self, denom: isize) { | |
if denom != self.denominator { | |
let multiplier = denom / self.denominator; | |
self.numerator *= multiplier; | |
self.denominator *= multiplier; | |
} | |
} | |
} | |
impl Display for Fraction { | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
write!(f, "{}/{}", self.numerator, self.denominator) | |
} | |
} | |
impl FromStr for Fraction { | |
type Err = String; | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
if let Some(i) = s.find('/') { | |
let numerator = s.get(0..i).unwrap().parse().unwrap(); | |
let denominator = s.get(i + 1..).unwrap().parse().unwrap(); | |
Ok(Self { | |
numerator, | |
denominator, | |
}) | |
} else { | |
Err("no separator found".to_string()) | |
} | |
} | |
} | |
fn common_denominator(a: isize, b: isize) -> isize { | |
if a == b { | |
a | |
} else if a % b == 0 { | |
a | |
} else if b % a == 0 { | |
b | |
} else { | |
a * b | |
} | |
} | |
fn factors(n: isize) -> Vec<isize> { | |
let mut set = BTreeSet::new(); | |
for i in 1..10 { | |
if n % i == 0 { | |
let j = n / i; | |
set.insert(i); | |
set.insert(j); | |
} | |
} | |
set.into_iter().collect() | |
} | |
fn fraction_math(lhs: String, op: Op, rhs: String) -> Fraction { | |
let mut lhs = Fraction::from_str(&lhs).unwrap(); | |
let mut rhs = Fraction::from_str(&rhs).unwrap(); | |
match op { | |
Op::Add => { | |
let common = common_denominator(lhs.denominator, rhs.denominator); | |
lhs.to_denominator(common); | |
rhs.to_denominator(common); | |
let frac = Fraction { | |
numerator: lhs.numerator + rhs.numerator, | |
denominator: common, | |
}; | |
frac.simplify() | |
} | |
Op::Sub => { | |
let common = common_denominator(lhs.denominator, rhs.denominator); | |
lhs.to_denominator(common); | |
rhs.to_denominator(common); | |
let frac = Fraction { | |
numerator: lhs.numerator - rhs.numerator, | |
denominator: common, | |
}; | |
frac.simplify() | |
} | |
Op::Multiply => Fraction { | |
numerator: lhs.numerator * rhs.numerator, | |
denominator: lhs.denominator * rhs.denominator, | |
} | |
.simplify(), | |
Op::Divide => Fraction { | |
numerator: lhs.numerator * rhs.denominator, | |
denominator: lhs.denominator * rhs.numerator, | |
} | |
.simplify(), | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_simplify() { | |
let initial = Fraction { | |
numerator: 6, | |
denominator: 9, | |
}; | |
assert_eq!( | |
Fraction { | |
numerator: 2, | |
denominator: 3 | |
}, | |
initial.simplify() | |
); | |
let prime = Fraction { | |
numerator: 7, | |
denominator: 13, | |
}; | |
assert_eq!(prime.clone(), prime.simplify()); | |
} | |
#[test] | |
fn test_factors() { | |
assert_eq!(vec![1, 2, 3, 4, 6, 12], factors(12)); | |
} | |
#[test] | |
fn test_fraction_addition() { | |
let result = fraction_math("2/5".to_owned(), Op::Add, "2/4".to_owned()); | |
assert_eq!(Fraction::from_str("9/10").unwrap(), result); | |
} | |
#[test] | |
fn test_fraction_subtraction() { | |
let result = fraction_math("3/5".to_owned(), Op::Sub, "2/4".to_owned()); | |
assert_eq!(Fraction::from_str("1/10").unwrap(), result); | |
} | |
#[test] | |
fn test_fraction_multiplication() { | |
let result = fraction_math("3/5".to_owned(), Op::Multiply, "2/4".to_owned()); | |
assert_eq!(Fraction::from_str("3/10").unwrap(), result); | |
} | |
#[test] | |
fn test_fraction_division() { | |
let result = fraction_math("1/2".to_owned(), Op::Divide, "3/4".to_owned()); | |
assert_eq!(Fraction::from_str("2/3").unwrap(), result); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment