[types] Fix up tests for Frac type
This commit is contained in:
parent
5aca4cfbe4
commit
d5e6913197
3 changed files with 83 additions and 31 deletions
|
@ -5,7 +5,7 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Add, Mul};
|
use std::ops::{Add, Mul};
|
||||||
use number::arith::{GCD, LCM};
|
use number::arith::GCD;
|
||||||
use number::{Int, Number};
|
use number::{Int, Number};
|
||||||
use object::{Obj, Object};
|
use object::{Obj, Object};
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ pub struct Frac { p: Int, q: Int }
|
||||||
|
|
||||||
impl Frac {
|
impl Frac {
|
||||||
pub fn new(p: Int, q: Int) -> Result<Frac, ()> {
|
pub fn new(p: Int, q: Int) -> Result<Frac, ()> {
|
||||||
if q == Int(0) {
|
if q.is_zero() {
|
||||||
// TODO: Return a more specific error about dividing by zero.
|
// TODO: Return a more specific error about dividing by zero.
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,29 +23,26 @@ impl Frac {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_ints(p: i64, q: i64) -> Result<Frac, ()> {
|
||||||
|
Frac::new(Int(p), Int(q))
|
||||||
|
}
|
||||||
|
|
||||||
fn reduced(self) -> Frac {
|
fn reduced(self) -> Frac {
|
||||||
let gcd = self.p.gcd(self.q);
|
let gcd = self.p.gcd(self.q);
|
||||||
Frac { p: self.p / gcd, q: self.q / gcd }
|
Frac { p: self.p / gcd, q: self.q / gcd }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _add(self, rhs: Frac) -> Frac {
|
fn _add(self, rhs: Frac) -> Frac {
|
||||||
let lcm = self.q.lcm(rhs.q);
|
let p = self.p * rhs.q + rhs.p * self.q;
|
||||||
let p = self.p * lcm + rhs.p * lcm;
|
let q = self.q * rhs.q;
|
||||||
let q = self.q * lcm;
|
Frac{p,q}.reduced()
|
||||||
Frac::new(p, q).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Number for Frac {
|
|
||||||
fn as_int(&self) -> Option<Int> {
|
|
||||||
if self.q == Int(1) {
|
|
||||||
Some(self.p)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_frac(&self) -> Option<Frac> { Frac::new(self.p, self.q).ok() }
|
fn _mul(self, rhs: Frac) -> Frac {
|
||||||
|
let p = self.p * rhs.p;
|
||||||
|
let q = self.q * rhs.q;
|
||||||
|
Frac{p,q}.reduced()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Frac {
|
impl Add for Frac {
|
||||||
|
@ -75,6 +72,41 @@ impl fmt::Display for Frac {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul for Frac {
|
||||||
|
type Output = Frac;
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
self._mul(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Mul<Frac> for &'a Frac {
|
||||||
|
type Output = Frac;
|
||||||
|
fn mul(self, rhs: Frac) -> Self::Output {
|
||||||
|
self._mul(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Mul<&'a Frac> for &'b Frac {
|
||||||
|
type Output = Frac;
|
||||||
|
fn mul(self, rhs: &Frac) -> Self::Output {
|
||||||
|
self._mul(*rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Number for Frac {
|
||||||
|
fn as_int(&self) -> Option<Int> {
|
||||||
|
if self.q == Int(1) {
|
||||||
|
Some(self.p)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_frac(&self) -> Option<Frac> { Frac::new(self.p, self.q).ok() }
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool { self.p.is_zero() }
|
||||||
|
}
|
||||||
|
|
||||||
impl Object for Frac {
|
impl Object for Frac {
|
||||||
fn as_any(&self) -> &Any { self }
|
fn as_any(&self) -> &Any { self }
|
||||||
fn as_num(&self) -> Option<&Number> { Some(self) }
|
fn as_num(&self) -> Option<&Number> { Some(self) }
|
||||||
|
@ -100,29 +132,43 @@ impl<'a> PartialEq<Number + 'a> for Frac {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use number::Number;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fracs_with_zero_q_are_invalid() {
|
||||||
|
assert!(Frac::from_ints(3, 0).is_err())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn equal_fracs_are_equal() {
|
fn equal_fracs_are_equal() {
|
||||||
assert_eq!(Frac(Int(3), Int(2)), Frac(Int(3), Int(2)));
|
assert_eq!(Frac::from_ints(3, 2), Frac::from_ints(3, 2));
|
||||||
assert_ne!(Frac(Int(12), Int(4)), Frac(Int(9), Int(7)));
|
assert_ne!(Frac::from_ints(12, 4), Frac::from_ints(9, 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fracs_should_reduce_to_ints_where_possible() {
|
fn fracs_should_reduce_to_ints_where_possible() {
|
||||||
let rational_as_integer = Frac(Int(3), Int(1)).as_int();
|
let fr = Frac::from_ints(3, 1).unwrap();
|
||||||
assert!(rational_as_integer.is_some());
|
assert_eq!(fr.as_int(), Some(Int(3)));
|
||||||
// Oh my god this line is so dumb.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fracs_should_not_reduce_to_ints_where_impossible() {
|
fn fracs_should_not_reduce_to_ints_where_impossible() {
|
||||||
let rational_as_integer = Frac(Int(3), Int(2)).as_int();
|
let fr = Frac::from_ints(3, 2).unwrap();
|
||||||
assert!(rational_as_integer.is_none());
|
assert_eq!(fr.as_int(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fracs_are_exact() {
|
fn fracs_are_exact() {
|
||||||
assert!(Frac(Int(4), Int(2)).is_exact());
|
let fr = Frac::from_ints(4, 2).unwrap();
|
||||||
|
assert!(fr.is_exact());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fracs_can_add() {
|
||||||
|
let a = Frac::from_ints(5, 6).unwrap();
|
||||||
|
let b = Frac::from_ints(2, 3).unwrap();
|
||||||
|
let r = Frac::from_ints(3, 2).unwrap();
|
||||||
|
assert_eq!(a + b, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,10 @@ use object::{Obj, Object};
|
||||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct Int(pub i64);
|
pub struct Int(pub i64);
|
||||||
|
|
||||||
|
impl Int {
|
||||||
|
pub fn zero() -> Int { Int(0) }
|
||||||
|
}
|
||||||
|
|
||||||
impl Add for Int {
|
impl Add for Int {
|
||||||
type Output = Int;
|
type Output = Int;
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
@ -63,25 +67,25 @@ impl<'a, 'b> Div<&'a Int> for &'b Int {
|
||||||
impl GCD for Int {
|
impl GCD for Int {
|
||||||
fn gcd(self, other: Int) -> Int {
|
fn gcd(self, other: Int) -> Int {
|
||||||
let (mut a, mut b) = if self > other {
|
let (mut a, mut b) = if self > other {
|
||||||
(self.0, other.0)
|
(self, other)
|
||||||
} else {
|
} else {
|
||||||
(other.0, self.0)
|
(other, self)
|
||||||
};
|
};
|
||||||
while b != 0 {
|
while !b.is_zero() {
|
||||||
let r = a % b;
|
let r = a % b;
|
||||||
a = b;
|
a = b;
|
||||||
b = r;
|
b = r;
|
||||||
}
|
}
|
||||||
Int(a)
|
a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LCM for Int {
|
impl LCM for Int {
|
||||||
fn lcm(self, other: Int) -> Int {
|
fn lcm(self, other: Int) -> Int {
|
||||||
if self.0 == 0 && other.0 == 0 {
|
if self.0 == 0 && other.0 == 0 {
|
||||||
Int(0)
|
Int::zero()
|
||||||
} else {
|
} else {
|
||||||
Int(self.0 * other.0 / self.gcd(other).0)
|
self * other / self.gcd(other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +98,7 @@ impl Object for Int {
|
||||||
impl Number for Int {
|
impl Number for Int {
|
||||||
fn as_int(&self) -> Option<Int> { Some(*self) }
|
fn as_int(&self) -> Option<Int> { Some(*self) }
|
||||||
fn as_frac(&self) -> Option<Frac> { Frac::new(*self, Int(1)).ok() }
|
fn as_frac(&self) -> Option<Frac> { Frac::new(*self, Int(1)).ok() }
|
||||||
|
fn is_zero(&self) -> bool { self.0 == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul for Int {
|
impl Mul for Int {
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub trait Number:
|
||||||
fn as_frac(&self) -> Option<Frac> { None }
|
fn as_frac(&self) -> Option<Frac> { None }
|
||||||
/// Return `true` if this Number is an exact representation of its value.
|
/// Return `true` if this Number is an exact representation of its value.
|
||||||
fn is_exact(&self) -> bool { true }
|
fn is_exact(&self) -> bool { true }
|
||||||
|
fn is_zero(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement PartialEq myself cause there are some weird nuances to comparing numbers.
|
// TODO: Implement PartialEq myself cause there are some weird nuances to comparing numbers.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue