2020-02-12 23:10:08 -05:00
|
|
|
//! # Rational Numbers (fractions)
|
2020-02-12 22:29:57 -05:00
|
|
|
|
2020-02-14 10:14:22 -05:00
|
|
|
use crate::num::*;
|
2020-02-18 20:59:58 -05:00
|
|
|
use std::cmp::{Ord, Ordering, PartialOrd};
|
2020-02-18 16:38:26 -05:00
|
|
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
2020-02-12 23:10:08 -05:00
|
|
|
|
2020-02-18 16:38:26 -05:00
|
|
|
/// Type representing a fraction
|
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]
|
2020-02-18 16:38:26 -05:00
|
|
|
/// Create a [Frac](rational/struct.Frac.html) type with signed or unsigned number literals
|
|
|
|
///
|
|
|
|
/// Accepts:
|
|
|
|
///
|
|
|
|
/// ```no-run
|
|
|
|
/// // Fractions
|
|
|
|
/// frac!(1/3);
|
|
|
|
///
|
|
|
|
/// // Whole numbers
|
|
|
|
/// frac!(5u8);
|
|
|
|
///
|
|
|
|
/// // Whole numbers and fractions
|
|
|
|
/// frac!(1 1/2);
|
|
|
|
/// ```
|
2020-02-14 17:24:51 -05:00
|
|
|
macro_rules! frac {
|
2020-02-18 10:19:57 -05:00
|
|
|
($w:literal $n:literal / $d:literal) => {
|
|
|
|
frac!($w) + frac!($n / $d)
|
2020-02-14 17:24:51 -05:00
|
|
|
};
|
2020-02-14 23:41:14 -05:00
|
|
|
($n:literal / $d:literal) => {
|
|
|
|
frac($n, $d)
|
2020-02-14 17:24:51 -05:00
|
|
|
};
|
2020-02-18 10:19:57 -05:00
|
|
|
($w:literal) => {
|
|
|
|
frac($w, 1)
|
|
|
|
};
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
|
2020-02-18 16:38:26 -05:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
|
|
enum FracOp {
|
|
|
|
Subtraction,
|
|
|
|
Other,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new rational number from signed or unsigned integers
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn frac<T: Int + Int<Un = U>, U: Unsigned>(n: T, d: T) -> Frac<U> {
|
2020-02-14 23:41:14 -05:00
|
|
|
let mut sign = Sign::Positive;
|
|
|
|
|
|
|
|
if n.is_neg() {
|
|
|
|
sign = !sign;
|
|
|
|
}
|
2020-02-14 17:24:51 -05:00
|
|
|
|
2020-02-14 23:41:14 -05:00
|
|
|
if d.is_neg() {
|
|
|
|
sign = !sign;
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
2020-02-14 23:41:14 -05:00
|
|
|
|
2020-02-18 16:38:26 -05:00
|
|
|
let numer = n.to_unsigned();
|
|
|
|
let denom = d.to_unsigned();
|
|
|
|
|
|
|
|
Frac::new(numer, denom, sign)
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
|
2020-02-12 22:29:57 -05:00
|
|
|
impl<T: Unsigned> Frac<T> {
|
2020-02-18 16:38:26 -05:00
|
|
|
/// Create a new rational number from unsigned integers and a sign
|
|
|
|
///
|
|
|
|
/// Generally, you will probably prefer to use the [frac!](../macro.frac.html) macro
|
|
|
|
/// instead, as that accepts both signed and unsigned arguments
|
2020-02-14 23:41:14 -05:00
|
|
|
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
|
2020-02-18 16:38:26 -05:00
|
|
|
fn get_sign(a: Self, b: Self, c: FracOp) -> Sign {
|
2020-02-14 17:24:51 -05:00
|
|
|
if a.sign != b.sign {
|
2020-02-18 16:38:26 -05:00
|
|
|
if c == FracOp::Subtraction && b.sign == Sign::Negative {
|
|
|
|
Sign::Positive
|
|
|
|
} else {
|
|
|
|
Sign::Negative
|
|
|
|
}
|
2020-02-14 17:24:51 -05:00
|
|
|
} 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-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> PartialOrd
|
|
|
|
for Frac<T>
|
|
|
|
{
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
|
|
Some(self.cmp(other))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lt(&self, other: &Self) -> bool {
|
|
|
|
self.cmp(other) == Ordering::Less
|
|
|
|
}
|
|
|
|
|
|
|
|
fn le(&self, other: &Self) -> bool {
|
|
|
|
self == other || self.cmp(other) == Ordering::Less
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gt(&self, other: &Self) -> bool {
|
|
|
|
self.cmp(other) == Ordering::Greater
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ge(&self, other: &Self) -> bool {
|
|
|
|
self == other || self.cmp(other) == Ordering::Greater
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Ord
|
|
|
|
for Frac<T>
|
|
|
|
{
|
|
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
|
|
if self.sign == other.sign {
|
|
|
|
if self.denom == other.denom {
|
|
|
|
return self.numer.cmp(&other.numer);
|
|
|
|
} else {
|
|
|
|
let gcd = T::gcd(self.denom, other.denom);
|
|
|
|
let x = gcd / self.denom;
|
|
|
|
let y = gcd / self.denom;
|
|
|
|
let mut a = self.clone();
|
|
|
|
let mut b = other.clone();
|
|
|
|
|
|
|
|
a.numer *= x;
|
|
|
|
a.denom *= x;
|
|
|
|
|
|
|
|
b.numer *= y;
|
|
|
|
b.denom *= y;
|
|
|
|
|
|
|
|
a.numer.cmp(&b.numer)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if self.sign == Sign::Positive {
|
|
|
|
return Ordering::Greater;
|
|
|
|
} else {
|
|
|
|
return Ordering::Less;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Mul
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-14 12:30:09 -05:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn mul(self, rhs: Self) -> Self {
|
|
|
|
let numer = self.numer * rhs.numer;
|
|
|
|
let denom = self.denom * rhs.denom;
|
2020-02-18 16:38:26 -05:00
|
|
|
let sign = Self::get_sign(self, rhs, FracOp::Other);
|
2020-02-14 17:24:51 -05:00
|
|
|
|
2020-02-14 23:41:14 -05:00
|
|
|
Self::new(numer, denom, sign)
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> MulAssign
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-18 10:19:57 -05:00
|
|
|
fn mul_assign(&mut self, rhs: Self) {
|
|
|
|
*self = self.clone() * rhs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Div
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-14 17:24:51 -05:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn div(self, rhs: Self) -> Self {
|
|
|
|
let numer = self.numer * rhs.denom;
|
|
|
|
let denom = self.denom * rhs.numer;
|
2020-02-18 16:38:26 -05:00
|
|
|
let sign = Self::get_sign(self, rhs, FracOp::Other);
|
2020-02-14 17:24:51 -05:00
|
|
|
|
2020-02-14 23:41:14 -05:00
|
|
|
Self::new(numer, denom, sign)
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
}
|
2020-02-14 12:30:09 -05:00
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> DivAssign
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-18 10:19:57 -05:00
|
|
|
fn div_assign(&mut self, rhs: Self) {
|
|
|
|
*self = self.clone() / rhs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Add
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-14 17:24:51 -05:00
|
|
|
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-18 20:59:58 -05:00
|
|
|
if a > b {
|
2020-02-14 23:41:14 -05:00
|
|
|
return a - b;
|
2020-02-18 20:59:58 -05:00
|
|
|
} else if a < b {
|
2020-02-14 23:41:14 -05:00
|
|
|
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;
|
2020-02-18 16:38:26 -05:00
|
|
|
let sign = Self::get_sign(a, b, FracOp::Other);
|
2020-02-14 17:24:51 -05:00
|
|
|
|
2020-02-14 23:41:14 -05:00
|
|
|
return Self::new(numer, denom, sign);
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let numer = a.numer + b.numer;
|
|
|
|
let denom = self.denom;
|
2020-02-18 16:38:26 -05:00
|
|
|
let sign = Self::get_sign(a, b, FracOp::Other);
|
2020-02-14 17:24:51 -05:00
|
|
|
|
2020-02-14 23:41:14 -05:00
|
|
|
Self::new(numer, denom, sign)
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> AddAssign
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-18 10:19:57 -05:00
|
|
|
fn add_assign(&mut self, rhs: Self) {
|
|
|
|
*self = self.clone() + rhs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Sub
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-14 17:24:51 -05:00
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
2020-02-18 20:59:58 -05:00
|
|
|
let a = self;
|
2020-02-18 16:38:26 -05:00
|
|
|
let b = rhs;
|
2020-02-14 17:24:51 -05:00
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
// @TODO handle sign "overflow" conditions
|
|
|
|
|
2020-02-18 10:19:57 -05:00
|
|
|
if a.denom != b.denom {
|
|
|
|
let numer = (a.numer * b.denom) - (b.numer * a.denom);
|
|
|
|
let denom = a.denom * b.denom;
|
2020-02-18 16:38:26 -05:00
|
|
|
let sign = Self::get_sign(a, b, FracOp::Subtraction);
|
2020-02-18 10:19:57 -05:00
|
|
|
|
|
|
|
return Self::new(numer, denom, sign);
|
|
|
|
}
|
|
|
|
|
|
|
|
let numer = a.numer - b.numer;
|
|
|
|
let denom = a.denom;
|
2020-02-18 16:38:26 -05:00
|
|
|
let sign = Self::get_sign(a, b, FracOp::Subtraction);
|
2020-02-18 10:19:57 -05:00
|
|
|
|
|
|
|
Self::new(numer, denom, sign)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:59:58 -05:00
|
|
|
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> SubAssign
|
|
|
|
for Frac<T>
|
|
|
|
{
|
2020-02-18 10:19:57 -05:00
|
|
|
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-18 16:38:26 -05:00
|
|
|
let frac1 = frac!(1 / 3u8);
|
|
|
|
let frac2 = frac!(2u8 / 3);
|
2020-02-14 12:11:57 -05:00
|
|
|
|
2020-02-18 16:38:26 -05:00
|
|
|
let expected = frac!(2u8 / 9);
|
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() {
|
2020-02-14 23:41:14 -05:00
|
|
|
assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2));
|
2020-02-18 16:38:26 -05:00
|
|
|
assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3");
|
|
|
|
assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3");
|
2020-02-14 17:24:51 -05:00
|
|
|
}
|
|
|
|
|
2020-02-18 10:19:57 -05:00
|
|
|
#[test]
|
|
|
|
fn sub_test() {
|
2020-02-18 16:38:26 -05:00
|
|
|
assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3));
|
2020-02-18 20:59:58 -05:00
|
|
|
// assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3");
|
|
|
|
// assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - 1/3");
|
2020-02-18 10:19:57 -05:00
|
|
|
}
|
|
|
|
|
2020-02-14 17:24:51 -05:00
|
|
|
#[test]
|
|
|
|
fn macro_test() {
|
2020-02-14 23:41:14 -05:00
|
|
|
let frac1 = frac!(1 / 3);
|
2020-02-18 16:38:26 -05:00
|
|
|
let frac2 = frac!(1u32 / 3);
|
2020-02-14 17:24:51 -05:00
|
|
|
assert_eq!(frac1, frac2);
|
|
|
|
|
2020-02-14 23:41:14 -05:00
|
|
|
let frac1 = -frac!(1 / 2);
|
2020-02-18 16:38:26 -05:00
|
|
|
let frac2 = -frac!(1u32 / 2);
|
2020-02-14 17:24:51 -05:00
|
|
|
assert_eq!(frac1, frac2);
|
2020-02-18 10:19:57 -05:00
|
|
|
|
|
|
|
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
|
|
|
}
|