rusty-numbers/src/rational.rs

103 lines
2.0 KiB
Rust
Raw Normal View History

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
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);
}
}