rusty-numbers/src/rational.rs

256 lines
6.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-14 17:24:51 -05:00
use std::ops::{Add, Div, Mul, Neg, Sub};
2020-02-12 23:10:08 -05:00
2020-02-14 17:24:51 -05:00
#[derive(Debug, 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
}
2020-02-14 17:24:51 -05:00
#[macro_export]
/// Create a `Frac` type with signed number literals
2020-02-14 17:24:51 -05:00
macro_rules! frac {
($w:literal $n:literal / $d:literal) => {
frac!($w) + frac!($n / $d)
2020-02-14 17:24:51 -05:00
};
($n:literal / $d:literal) => {
frac($n, $d)
2020-02-14 17:24:51 -05:00
};
($w:literal) => {
frac($w, 1)
};
2020-02-14 17:24:51 -05:00
}
/// Create a new rational number from unsigned integers
fn frac<S: Signed + Signed<Un = U>, U: Unsigned>(n: S, d: S) -> Frac<U> {
// Converting from signed to unsigned should always be safe
// when using the absolute value, especially since I'm converting
// between the same bit size
let mut sign = Sign::Positive;
let numer = n.to_unsigned();
let denom = d.to_unsigned();
if n.is_neg() {
sign = !sign;
}
2020-02-14 17:24:51 -05:00
if d.is_neg() {
sign = !sign;
2020-02-14 17:24:51 -05:00
}
Frac { numer, denom, sign }.reduce()
2020-02-14 17:24:51 -05:00
}
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, s: Sign) -> Frac<T> {
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-14 17:24:51 -05:00
sign: s,
2020-02-12 22:29:57 -05:00
}
2020-02-13 17:13:25 -05:00
.reduce()
}
2020-02-14 17:24:51 -05:00
/// Determine the output sign given the two input signs
fn get_sign(a: Self, b: Self) -> Sign {
if a.sign != b.sign {
Sign::Negative
} else {
Sign::Positive
}
2020-02-13 17:13:25 -05:00
}
2020-02-14 17:24:51 -05:00
/// Convert the fraction to its simplest form
2020-02-13 17:13:25 -05:00
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-14 12:30:09 -05:00
impl<T: Unsigned + Mul<Output = T>> Mul for Frac<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let numer = self.numer * rhs.numer;
let denom = self.denom * rhs.denom;
2020-02-14 17:24:51 -05:00
let sign = Self::get_sign(self, rhs);
Self::new(numer, denom, sign)
2020-02-14 17:24:51 -05:00
}
}
impl<T: Unsigned + Mul<Output = T>> MulAssign for Frac<T> {
fn mul_assign(&mut self, rhs: Self) {
*self = self.clone() * rhs
}
}
2020-02-14 17:24:51 -05:00
impl<T: Unsigned + Mul<Output = T>> Div for Frac<T> {
type Output = Self;
fn div(self, rhs: Self) -> Self {
let numer = self.numer * rhs.denom;
let denom = self.denom * rhs.numer;
let sign = Self::get_sign(self, rhs);
Self::new(numer, denom, sign)
2020-02-14 17:24:51 -05:00
}
}
2020-02-14 12:30:09 -05:00
impl<T: Unsigned + Mul<Output = T>> DivAssign for Frac<T> {
fn div_assign(&mut self, rhs: Self) {
*self = self.clone() / rhs
}
}
2020-02-14 17:24:51 -05:00
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> Add for Frac<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let a = self;
let b = rhs;
// If the sign of one input differs,
// subtraction is equivalent
2020-02-14 12:30:09 -05:00
if self.sign != rhs.sign {
2020-02-14 17:24:51 -05:00
if a.numer > b.numer {
return a - b;
2020-02-14 17:24:51 -05:00
} else if a.numer < b.numer {
return b - a;
2020-02-14 17:24:51 -05:00
}
2020-02-14 12:30:09 -05:00
}
2020-02-14 17:24:51 -05:00
// Find a common denominator if needed
if a.denom != b.denom {
// Let's just use the simplest method, rather than
// worrying about reducing to the least common denominator
let numer = (a.numer * b.denom) + (b.numer * a.denom);
let denom = a.denom * b.denom;
let sign = Self::get_sign(a, b);
return Self::new(numer, denom, sign);
2020-02-14 17:24:51 -05:00
}
let numer = a.numer + b.numer;
let denom = self.denom;
let sign = Self::get_sign(a, b);
Self::new(numer, denom, sign)
2020-02-14 17:24:51 -05:00
}
}
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> AddAssign for Frac<T> {
fn add_assign(&mut self, rhs: Self) {
*self = self.clone() + rhs
}
}
impl<T: Unsigned + Sub<Output = T> + Mul<Output = T>> Sub for Frac<T> {
2020-02-14 17:24:51 -05:00
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let a = if self.numer >= rhs.numer {
self
} else {
rhs
};
let b = if self.numer < rhs.numer {
self
} else {
rhs
};
2020-02-14 17:24:51 -05:00
if a.denom != b.denom {
let numer = (a.numer * b.denom) - (b.numer * a.denom);
let denom = a.denom * b.denom;
let sign = Self::get_sign(a, b);
return Self::new(numer, denom, sign);
}
let numer = a.numer - b.numer;
let denom = a.denom;
let sign = Self::get_sign(a, b);
Self::new(numer, denom, sign)
}
}
impl<T: Unsigned + Sub<Output = T> + Mul<Output = T>> SubAssign for Frac<T> {
fn sub_assign(&mut self, rhs: Self) {
*self = self.clone() - rhs
2020-02-14 12:30:09 -05:00
}
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() {
2020-02-14 17:24:51 -05:00
let frac1 = Frac::new(1u8, 3u8, Sign::Positive);
let frac2 = Frac::new(2u8, 3u8, Sign::Positive);
2020-02-14 12:11:57 -05:00
2020-02-14 17:24:51 -05:00
let expected = Frac::new(2u8, 9u8, Sign::Positive);
2020-02-14 12:11:57 -05:00
assert_eq!(frac1 * frac2, expected);
}
2020-02-14 17:24:51 -05:00
#[test]
fn add_test() {
assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2));
2020-02-14 17:24:51 -05:00
}
#[test]
fn sub_test() {
assert_eq!(frac!(1/6), frac!(1 / 2) - frac!(1/3));
}
2020-02-14 17:24:51 -05:00
#[test]
fn macro_test() {
let frac1 = frac!(1 / 3);
let frac2 = Frac::new(1u32, 3, Sign::Positive);
2020-02-14 17:24:51 -05:00
assert_eq!(frac1, frac2);
let frac1 = -frac!(1 / 2);
let frac2 = Frac::new(1u32, 2, Sign::Negative);
2020-02-14 17:24:51 -05:00
assert_eq!(frac1, frac2);
assert_eq!(frac!(3 / 2), frac!(1 1/2));
assert_eq!(frac!(3 / 1), frac!(3));
2020-02-14 17:24:51 -05:00
}
2020-02-14 12:11:57 -05:00
}