Skip to content

Instantly share code, notes, and snippets.

@wperron
Created March 15, 2023 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wperron/ea7f0ed4704182800d814c76aeae9f26 to your computer and use it in GitHub Desktop.
Save wperron/ea7f0ed4704182800d814c76aeae9f26 to your computer and use it in GitHub Desktop.
Doing math with fractions
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