[types] More operations on Ints and some tests for Frac
Implement Div on Int Make sure Fracs are reduced when they are produced, plus some error handling if you create a x/0 Frac
This commit is contained in:
parent
1aabce4f60
commit
a5ed11ea77
2 changed files with 48 additions and 15 deletions
|
@ -5,28 +5,40 @@
|
||||||
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;
|
||||||
use number::{Int, Number};
|
use number::{Int, Number};
|
||||||
use object::{Obj, Object};
|
use object::{Obj, Object};
|
||||||
|
|
||||||
/// A fraction consisting of a numerator and denominator.
|
/// A fraction consisting of a numerator and denominator.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct Frac(pub i64, pub u64);
|
pub struct Frac(Int, Int);
|
||||||
|
|
||||||
impl Frac {
|
impl Frac {
|
||||||
|
pub fn new(p: Int, q: Int) -> Result<Frac, ()> {
|
||||||
|
if q == Int(0) {
|
||||||
|
// TODO: Return a more specific error about dividing by zero.
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(Frac(p, q).reduced())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduced(self) -> Frac {
|
||||||
|
let gcd = self.0.gcd(self.1);
|
||||||
|
Frac(self.0 / gcd, self.1 / gcd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Number for Frac {
|
impl Number for Frac {
|
||||||
fn as_int(&self) -> Option<Int> {
|
fn as_int(&self) -> Option<Int> {
|
||||||
if self.1 == 1 {
|
if self.1 == Int(1) {
|
||||||
Some(Int(self.0))
|
Some(self.0)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_frac(&self) -> Option<Frac> {
|
fn as_frac(&self) -> Option<Frac> { Frac::new(self.0, self.1).ok() }
|
||||||
Some(Frac(self.0, self.1))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Frac {
|
impl fmt::Display for Frac {
|
||||||
|
@ -64,25 +76,25 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn equal_fracs_are_equal() {
|
fn equal_fracs_are_equal() {
|
||||||
assert_eq!(Frac(3, 2), Frac(3, 2));
|
assert_eq!(Frac(Int(3), Int(2)), Frac(Int(3), Int(2)));
|
||||||
assert_ne!(Frac(12, 4), Frac(9, 7));
|
assert_ne!(Frac(Int(12), Int(4)), Frac(Int(9), Int(7)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fracs_should_reduce_to_ints_where_possible() {
|
fn fracs_should_reduce_to_ints_where_possible() {
|
||||||
let rational_as_integer = Frac(3, 1).as_int();
|
let rational_as_integer = Frac(Int(3), Int(1)).as_int();
|
||||||
assert!(rational_as_integer.is_some());
|
assert!(rational_as_integer.is_some());
|
||||||
// Oh my god this line is so dumb.
|
// 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(3, 2).as_int();
|
let rational_as_integer = Frac(Int(3), Int(2)).as_int();
|
||||||
assert!(rational_as_integer.is_none());
|
assert!(rational_as_integer.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fracs_are_exact() {
|
fn fracs_are_exact() {
|
||||||
assert!(Frac(4, 2).is_exact());
|
assert!(Frac(Int(4), Int(2)).is_exact());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Add, Mul, Rem};
|
use std::ops::{Add, Div, Mul, Rem};
|
||||||
use number::arith::{GCD, LCM};
|
use number::arith::{GCD, LCM};
|
||||||
use number::{Frac, Number};
|
use number::{Frac, Number};
|
||||||
use object::{Obj, Object};
|
use object::{Obj, Object};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct Int(pub i64);
|
pub struct Int(pub i64);
|
||||||
|
|
||||||
impl Add for Int {
|
impl Add for Int {
|
||||||
|
@ -39,6 +39,27 @@ impl fmt::Display for Int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Div for Int {
|
||||||
|
type Output = Int;
|
||||||
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
Int(self.0 / rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Div<Int> for &'a Int {
|
||||||
|
type Output = Int;
|
||||||
|
fn div(self, rhs: Int) -> Self::Output {
|
||||||
|
Int(self.0 / rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Div<&'a Int> for &'b Int {
|
||||||
|
type Output = Int;
|
||||||
|
fn div(self, rhs: &Int) -> Self::Output {
|
||||||
|
Int(self.0 / rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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.0 > other.0 {
|
let (mut a, mut b) = if self.0 > other.0 {
|
||||||
|
@ -72,7 +93,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> { Some(Frac(self.0, 1)) }
|
fn as_frac(&self) -> Option<Frac> { Frac::new(*self, Int(1)).ok() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul for Int {
|
impl Mul for Int {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue