2020-02-12 23:10:08 -05:00
|
|
|
//! # Rational Numbers (fractions)
|
|
|
|
//!
|
|
|
|
//! Traits to implement:
|
|
|
|
//! * Add
|
|
|
|
//! * AddAssign
|
|
|
|
//! * Div
|
|
|
|
//! * DivAssign
|
|
|
|
//! * Mul
|
|
|
|
//! * MulAssign
|
|
|
|
//! * Neg
|
|
|
|
//! * Sub
|
|
|
|
//! * SubAssign
|
2020-02-12 22:29:57 -05:00
|
|
|
|
2020-02-14 10:14:22 -05:00
|
|
|
use crate::num::*;
|
2020-02-13 17:13:25 -05:00
|
|
|
use std::ops::{Mul, Neg};
|
2020-02-12 23:10:08 -05:00
|
|
|
|
2020-02-13 17:13:25 -05:00
|
|
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
2020-02-12 22:29:57 -05:00
|
|
|
pub struct Frac<T: Unsigned = usize> {
|
|
|
|
numer: T,
|
|
|
|
denom: T,
|
2020-02-12 23:10:08 -05:00
|
|
|
sign: Sign,
|
2020-02-12 22:29:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Unsigned> Frac<T> {
|
|
|
|
/// Create a new rational number
|
|
|
|
pub fn new(n: T, d: T) -> Self {
|
2020-02-13 17:13:25 -05:00
|
|
|
if d.is_zero() {
|
|
|
|
panic!("Fraction can not have a zero denominator");
|
|
|
|
}
|
|
|
|
|
2020-02-12 22:29:57 -05:00
|
|
|
Frac {
|
|
|
|
numer: n,
|
|
|
|
denom: d,
|
2020-02-12 23:10:08 -05:00
|
|
|
sign: Sign::Positive,
|
2020-02-12 22:29:57 -05:00
|
|
|
}
|
2020-02-13 17:13:25 -05:00
|
|
|
.reduce()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_neg(n: T, d: T) -> Self {
|
|
|
|
let mut frac = Frac::new(n, d);
|
|
|
|
frac.sign = Sign::Negative;
|
|
|
|
|
|
|
|
frac
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reduce(mut self) -> Self {
|
|
|
|
let gcd = T::gcd(self.numer, self.denom);
|
|
|
|
self.numer /= gcd;
|
|
|
|
self.denom /= gcd;
|
|
|
|
|
|
|
|
self
|
2020-02-12 22:29:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-13 17:13:25 -05:00
|
|
|
macro_rules! impl_mul {
|
2020-02-14 12:11:57 -05:00
|
|
|
($( $Type: ty ),* ) => {
|
|
|
|
$(
|
|
|
|
impl Mul for Frac<$Type> {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn mul(self, rhs: Self) -> Self {
|
|
|
|
let numer = self.numer * rhs.numer;
|
|
|
|
let denom = self.denom * rhs.denom;
|
|
|
|
|
|
|
|
// Figure out the sign
|
|
|
|
if self.sign != rhs.sign {
|
|
|
|
Self::new_neg(numer, denom)
|
|
|
|
} else {
|
|
|
|
Self::new(numer, denom)
|
|
|
|
}
|
2020-02-13 17:13:25 -05:00
|
|
|
}
|
|
|
|
}
|
2020-02-14 12:11:57 -05:00
|
|
|
)*
|
2020-02-13 17:13:25 -05:00
|
|
|
};
|
|
|
|
}
|
2020-02-14 12:11:57 -05:00
|
|
|
impl_mul!(u8, u16, u32, u64, usize, u128);
|
2020-02-13 17:13:25 -05:00
|
|
|
|
2020-02-12 23:10:08 -05:00
|
|
|
impl<T: Unsigned> Neg for Frac<T> {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn neg(self) -> Self::Output {
|
|
|
|
let mut out = self.clone();
|
|
|
|
out.sign = !self.sign;
|
|
|
|
|
|
|
|
out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 22:29:57 -05:00
|
|
|
#[cfg(test)]
|
2020-02-14 12:11:57 -05:00
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn mul_test() {
|
|
|
|
let frac1 = Frac::new(1u8, 3u8);
|
|
|
|
let frac2 = Frac::new(2u8, 3u8);
|
|
|
|
|
|
|
|
let expected = Frac::new(2u8, 9u8);
|
|
|
|
|
|
|
|
assert_eq!(frac1 * frac2, expected);
|
|
|
|
}
|
|
|
|
}
|