Formatting and proper comparision
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good

This commit is contained in:
Timothy Warren 2020-02-19 09:39:19 -05:00
parent ba3952206a
commit 55d2d407e3
2 changed files with 114 additions and 80 deletions

View File

@ -257,6 +257,7 @@ mod tests {
#[test] #[test]
fn test_gcd() { fn test_gcd() {
assert_eq!(u8::gcd(2, 3), 1);
assert_eq!(u8::gcd(2, 2), 2); assert_eq!(u8::gcd(2, 2), 2);
assert_eq!(u8::gcd(2, 8), 2); assert_eq!(u8::gcd(2, 8), 2);
assert_eq!(u16::gcd(36, 48), 12); assert_eq!(u16::gcd(36, 48), 12);
@ -264,6 +265,8 @@ mod tests {
#[test] #[test]
fn test_lcm() { fn test_lcm() {
assert_eq!(u32::lcm(2, 8), 8);
assert_eq!(u16::lcm(2, 3), 6);
assert_eq!(usize::lcm(15, 30), 30); assert_eq!(usize::lcm(15, 30), 30);
assert_eq!(u128::lcm(1, 5), 5); assert_eq!(u128::lcm(1, 5), 5);
} }

View File

@ -32,10 +32,10 @@ macro_rules! frac {
frac!($w) + frac!($n / $d) frac!($w) + frac!($n / $d)
}; };
($n:literal / $d:literal) => { ($n:literal / $d:literal) => {
frac($n, $d) $crate::rational::Frac::new($n, $d)
}; };
($w:literal) => { ($w:literal) => {
frac($w, 1) $crate::rational::Frac::new($w, 1)
}; };
} }
@ -45,35 +45,44 @@ enum FracOp {
Other, 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> {
let mut sign = Sign::Positive;
if n.is_neg() {
sign = !sign;
}
if d.is_neg() {
sign = !sign;
}
let numer = n.to_unsigned();
let denom = d.to_unsigned();
Frac::new(numer, denom, sign)
}
impl<T: Unsigned> Frac<T> { impl<T: Unsigned> Frac<T> {
/// Create a new rational number from unsigned integers and a sign /// Create a new rational number from signed or unsigned arguments
/// ///
/// Generally, you will probably prefer to use the [frac!](../macro.frac.html) macro /// Generally, you will probably prefer to use the [frac!](../macro.frac.html) macro
/// instead, as that accepts both signed and unsigned arguments /// instead, as that is easier for mixed fractions and whole numbers
pub fn new(n: T, d: T, s: Sign) -> Frac<T> { pub fn new<N: Int + Int<Un = T>>(n: N, d: N) -> Frac<T> {
Self::new_raw(n, d, s).reduce() Self::new_unreduced(n, d).reduce()
} }
fn new_raw(n: T, d: T, s: Sign) -> Frac<T> { /// Create a new rational number from signed or unsigned arguments
/// where the resulting fraction is not reduced
pub fn new_unreduced<N: Int + Int<Un = T>>(n: N, d: N) -> Frac<T> {
if d.is_zero() {
panic!("Fraction can not have a zero denominator");
}
let mut sign = Sign::Positive;
if n.is_neg() {
sign = !sign;
}
if d.is_neg() {
sign = !sign;
}
let numer = n.to_unsigned();
let denom = d.to_unsigned();
Frac {
numer,
denom,
sign,
}
}
/// Create a new rational from the raw parts
fn raw(n: T, d: T, s: Sign) -> Frac<T> {
if d.is_zero() { if d.is_zero() {
panic!("Fraction can not have a zero denominator"); panic!("Fraction can not have a zero denominator");
} }
@ -82,7 +91,7 @@ impl<T: Unsigned> Frac<T> {
numer: n, numer: n,
denom: d, denom: d,
sign: s, sign: s,
} }.reduce()
} }
/// Determine the output sign given the two input signs /// Determine the output sign given the two input signs
@ -108,51 +117,63 @@ impl<T: Unsigned> Frac<T> {
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> PartialOrd impl<T> PartialOrd for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Ord impl<T> Ord for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
if self.sign == other.sign { if self.sign != other.sign {
if self.denom == other.denom { return if self.sign == Sign::Positive {
return self.numer.cmp(&other.numer); Ordering::Greater
} else { } else {
// @TODO fix logic for comparing fractions of different denominators Ordering::Less
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 = a.numer * x;
a.denom = a.denom * x;
b.numer = b.numer * y;
b.denom = b.denom * y;
assert_eq!(a.denom, b.denom, "Denominators should be equal here. {:#?}{:#?}{:?}{:?}", a, b, x, y);
a.numer.cmp(&b.numer)
}
} else {
if self.sign == Sign::Positive {
return Ordering::Greater;
} else {
return Ordering::Less;
}
} }
if self.denom == other.denom {
return self.numer.cmp(&other.numer);
}
let mut a = self.reduce();
let mut b = other.reduce();
if a.denom == b.denom {
assert!(false, "{:#?}\n{:#?}", a, b);
return a.numer.cmp(&b.numer);
}
let lcm = T::lcm(self.denom, other.denom);
let x = lcm / self.denom;
let y = lcm / other.denom;
a.numer *= x;
a.denom *= x;
b.numer *= y;
b.denom *= y;
debug_assert_eq!(
a.denom, b.denom,
"Denominators should be equal here. \n{:#?}\n{:#?}\n{:?}\n{:?}",
a, b, x, y
);
a.numer.cmp(&b.numer)
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Mul impl<T> Mul for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
type Output = Self; type Output = Self;
@ -161,20 +182,22 @@ impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Out
let denom = self.denom * rhs.denom; let denom = self.denom * rhs.denom;
let sign = Self::get_sign(self, rhs, FracOp::Other); let sign = Self::get_sign(self, rhs, FracOp::Other);
Self::new(numer, denom, sign) Self::raw(numer, denom, sign)
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> MulAssign impl<T> MulAssign for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
fn mul_assign(&mut self, rhs: Self) { fn mul_assign(&mut self, rhs: Self) {
*self = self.clone() * rhs *self = self.clone() * rhs
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Div impl<T> Div for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
type Output = Self; type Output = Self;
@ -183,20 +206,22 @@ impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Out
let denom = self.denom * rhs.numer; let denom = self.denom * rhs.numer;
let sign = Self::get_sign(self, rhs, FracOp::Other); let sign = Self::get_sign(self, rhs, FracOp::Other);
Self::new(numer, denom, sign) Self::raw(numer, denom, sign)
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> DivAssign impl<T> DivAssign for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
fn div_assign(&mut self, rhs: Self) { fn div_assign(&mut self, rhs: Self) {
*self = self.clone() / rhs *self = self.clone() / rhs
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Add impl<T> Add for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
type Output = Self; type Output = Self;
@ -222,27 +247,29 @@ impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Out
let denom = a.denom * b.denom; let denom = a.denom * b.denom;
let sign = Self::get_sign(a, b, FracOp::Other); let sign = Self::get_sign(a, b, FracOp::Other);
return Self::new(numer, denom, sign); return Self::raw(numer, denom, sign);
} }
let numer = a.numer + b.numer; let numer = a.numer + b.numer;
let denom = self.denom; let denom = self.denom;
let sign = Self::get_sign(a, b, FracOp::Other); let sign = Self::get_sign(a, b, FracOp::Other);
Self::new(numer, denom, sign) Self::raw(numer, denom, sign)
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> AddAssign impl<T> AddAssign for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
fn add_assign(&mut self, rhs: Self) { fn add_assign(&mut self, rhs: Self) {
*self = self.clone() + rhs *self = self.clone() + rhs
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> Sub impl<T> Sub for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
type Output = Self; type Output = Self;
@ -257,19 +284,20 @@ impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Out
let denom = a.denom * b.denom; let denom = a.denom * b.denom;
let sign = Self::get_sign(a, b, FracOp::Subtraction); let sign = Self::get_sign(a, b, FracOp::Subtraction);
return Self::new(numer, denom, sign); return Self::raw(numer, denom, sign);
} }
let numer = a.numer - b.numer; let numer = a.numer - b.numer;
let denom = a.denom; let denom = a.denom;
let sign = Self::get_sign(a, b, FracOp::Subtraction); let sign = Self::get_sign(a, b, FracOp::Subtraction);
Self::new(numer, denom, sign) Self::raw(numer, denom, sign)
} }
} }
impl<T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>> SubAssign impl<T> SubAssign for Frac<T>
for Frac<T> where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{ {
fn sub_assign(&mut self, rhs: Self) { fn sub_assign(&mut self, rhs: Self) {
*self = self.clone() - rhs *self = self.clone() - rhs
@ -317,7 +345,10 @@ mod tests {
#[test] #[test]
fn cmp_test() { fn cmp_test() {
// assert_eq!(Ordering::Equal, Frac::new_raw(1u8, 2u8, Sign::Positive).cmp(&Frac::new_raw(4u8, 8u8, Sign::Positive))); assert!(-frac!(5 / 3) < frac!(1 / 10_000));
assert!(frac!(1 / 10_000) > -frac!(10));
assert!(frac!(1 / 3) < frac!(1 / 2));
assert_eq!(frac!(1 / 2), frac!(3 / 6));
} }
#[test] #[test]