|
|
|
@ -0,0 +1,227 @@
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|