From 1b26497d19514333207e60488f1f8b9f227699d1 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 9 Sep 2018 11:23:44 -0700 Subject: [PATCH] =?UTF-8?q?[types]=20Move=20arithmetic=20ops=20to=20a=20ma?= =?UTF-8?q?cro=20=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sketch out Irr type and implement arithmetic types on it. --- types/src/number/arith.rs | 64 ++++++++++++++++++---------- types/src/number/integer.rs | 85 ------------------------------------- types/src/number/irr.rs | 62 +++++++++++++++++++++++++++ types/src/number/mod.rs | 85 +++---------------------------------- 4 files changed, 108 insertions(+), 188 deletions(-) create mode 100644 types/src/number/irr.rs diff --git a/types/src/number/arith.rs b/types/src/number/arith.rs index 69740b5..546901f 100644 --- a/types/src/number/arith.rs +++ b/types/src/number/arith.rs @@ -2,6 +2,9 @@ * Eryn Wells */ +use std::ops::{Add, Div, Mul, Sub, Rem}; +use number::{Int, Irr}; + pub trait GCD { /// Find the greatest common divisor of `self` and another number. fn gcd(self, other: Self) -> Self; @@ -12,26 +15,41 @@ pub trait LCM { fn lcm(self, other: Self) -> Self; } -//impl Rational for Int { -// fn to_rational(self) -> (Int, Int) { (self, 1) } -//} -// -//impl Rational for Flt { -// fn to_rational(self) -> (Int, Int) { -// // Convert the float to a fraction by iteratively multiplying by 10 until the fractional part of the float is 0.0. -// let whole_part = self.trunc(); -// let mut p = self.fract(); -// let mut q = 1.0; -// while p.fract() != 0.0 { -// p *= 10.0; -// q *= 10.0; -// } -// p += whole_part * q; -// -// // Integers from here down. Reduce the fraction before returning. -// let p = p as Int; -// let q = q as Int; -// let gcd = p.gcd(q); -// (p / gcd, q / gcd) -// } -//} +macro_rules! impl_newtype_arith_op { + ($id:ident, $opt:ident, $opm:ident, $op:tt) => { + impl $opt for $id { + type Output = $id; + #[inline] + fn $opm(self, rhs: $id) -> Self::Output { + $id(self.0 $op rhs.0) + } + } + impl<'a> $opt<$id> for &'a $id { + type Output = $id; + #[inline] + fn $opm(self, rhs: $id) -> Self::Output { + $id(self.0 $op rhs.0) + } + } + impl<'a, 'b> $opt<&'a $id> for &'b $id { + type Output = $id; + #[inline] + fn $opm(self, rhs: &$id) -> Self::Output { + $id(self.0 $op rhs.0) + } + } + } +} + +macro_rules! impl_newtype_arith { + ($($id:ident)*) => ($( + impl_newtype_arith_op!{$id, Add, add, +} + impl_newtype_arith_op!{$id, Div, div, /} + impl_newtype_arith_op!{$id, Mul, mul, *} + impl_newtype_arith_op!{$id, Sub, sub, -} + )*) +} + +impl_newtype_arith!{ Int Irr } +impl_newtype_arith_op!{Int, Rem, rem, %} +impl_newtype_arith_op!{Irr, Rem, rem, %} diff --git a/types/src/number/integer.rs b/types/src/number/integer.rs index 21cb6c4..96853ad 100644 --- a/types/src/number/integer.rs +++ b/types/src/number/integer.rs @@ -4,7 +4,6 @@ use std::any::Any; use std::fmt; -use std::ops::{Add, Div, Mul, Rem}; use number::arith::{GCD, LCM}; use number::{Frac, Number}; use object::{Obj, Object}; @@ -16,54 +15,12 @@ impl Int { pub fn zero() -> Int { Int(0) } } -impl Add for Int { - type Output = Int; - fn add(self, rhs: Self) -> Self::Output { - Int(self.0 + rhs.0) - } -} - -impl<'a> Add for &'a Int { - type Output = Int; - fn add(self, rhs: Int) -> Self::Output { - Int(self.0 + rhs.0) - } -} - -impl<'a, 'b> Add<&'a Int> for &'b Int { - type Output = Int; - fn add(self, rhs: &Int) -> Self::Output { - Int(self.0 + rhs.0) - } -} - impl fmt::Display for Int { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } -impl Div for Int { - type Output = Int; - fn div(self, rhs: Self) -> Self::Output { - Int(self.0 / rhs.0) - } -} - -impl<'a> Div 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 { fn gcd(self, other: Int) -> Int { let (mut a, mut b) = if self > other { @@ -101,27 +58,6 @@ impl Number for Int { fn is_zero(&self) -> bool { self.0 == 0 } } -impl Mul for Int { - type Output = Int; - fn mul(self, rhs: Self) -> Self::Output { - Int(self.0 * rhs.0) - } -} - -impl<'a> Mul for &'a Int { - type Output = Int; - fn mul(self, rhs: Int) -> Self::Output { - Int(self.0 * rhs.0) - } -} - -impl<'a, 'b> Mul<&'a Int> for &'b Int { - type Output = Int; - fn mul(self, rhs: &Int) -> Self::Output { - Int(self.0 * rhs.0) - } -} - impl PartialEq for Int { fn eq<'a>(&self, rhs: &'a Obj) -> bool { match rhs.obj().and_then(Object::as_num) { @@ -140,27 +76,6 @@ impl<'a> PartialEq for Int { } } -impl Rem for Int { - type Output = Int; - fn rem(self, rhs: Self) -> Self::Output { - Int(self.0 % rhs.0) - } -} - -impl<'a> Rem for &'a Int { - type Output = Int; - fn rem(self, rhs: Int) -> Self::Output { - Int(self.0 % rhs.0) - } -} - -impl<'a, 'b> Rem<&'a Int> for &'b Int { - type Output = Int; - fn rem(self, rhs: &Int) -> Self::Output { - Int(self.0 % rhs.0) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/types/src/number/irr.rs b/types/src/number/irr.rs new file mode 100644 index 0000000..be24df0 --- /dev/null +++ b/types/src/number/irr.rs @@ -0,0 +1,62 @@ +/* types/src/number/irr.rs + * Eryn Wells + */ + +use std::any::Any; +use std::fmt; +use number::{Frac, Int, Number}; +use object::{Obj, Object}; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Irr(pub f64); + +impl Irr { + pub fn zero() -> Irr { Irr(0.0) } +} + +impl fmt::Display for Irr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Object for Irr { + fn as_any(&self) -> &Any { self } + fn as_num(&self) -> Option<&Number> { Some(self) } +} + +impl Number for Irr { + fn as_int(&self) -> Option { + if self.0.trunc() == self.0 { + Some(Int(self.0.trunc() as i64)) + } else { + None + } + } + + fn as_frac(&self) -> Option { + if !self.0.is_infinite() && !self.0.is_nan() { + // TODO + None + } else { + None + } + } + + fn is_zero(&self) -> bool { self.0 == 0.0 } +} + +impl PartialEq for Irr { + fn eq<'a>(&self, rhs: &'a Obj) -> bool { + match rhs.obj().and_then(Object::as_num) { + Some(num) => self == num, + None => false + } + } +} + +impl<'a> PartialEq for Irr { + fn eq(&self, rhs: &(Number + 'a)) -> bool { + false + } +} diff --git a/types/src/number/mod.rs b/types/src/number/mod.rs index 8424942..bc03b72 100644 --- a/types/src/number/mod.rs +++ b/types/src/number/mod.rs @@ -12,13 +12,15 @@ //! be represented as an Integer. mod arith; -mod integer; mod frac; +mod integer; +mod irr; use object::Object; -pub use self::integer::Int; pub use self::frac::Frac; +pub use self::integer::Int; +pub use self::irr::Irr; pub trait Number: Object @@ -29,83 +31,6 @@ pub trait Number: fn as_frac(&self) -> Option { None } /// Return `true` if this Number is an exact representation of its value. fn is_exact(&self) -> bool { true } + /// Return `true` if this Number is equal to 0. fn is_zero(&self) -> bool; } - -// TODO: Implement PartialEq myself cause there are some weird nuances to comparing numbers. -//#[derive(Debug, PartialEq)] -//pub struct Number { -// real: Real, -// imag: Option, -// exact: Exact, -//} - -//impl Number { -// fn new(real: Real, imag: Option, exact: Exact) -> Number { -// Number { -// real: real.reduce(), -// imag: imag.map(|n| n.reduce()), -// exact: exact, -// } -// } -// -// pub fn from_int(value: Int, exact: Exact) -> Number { -// Number::new(Real::Integer(value), None, exact) -// } -// -// pub fn from_quotient(p: Int, q: Int, exact: Exact) -> Number { -// let real = if exact == Exact::Yes { -// // Make an exact rational an integer if possible. -// Real::Rational(p, q).demote() -// } -// else { -// // Make an inexact rational an irrational. -// Real::Rational(p, q).promote_once() -// }; -// Number::new(real, None, exact) -// } -// -// pub fn from_float(value: Flt, exact: Exact) -> Number { -// let real = if exact == Exact::Yes { -// // Attempt to demote irrationals. -// Real::Irrational(value).demote() -// } -// else { -// Real::Irrational(value) -// }; -// Number::new(real, None, exact) -// } -// -// pub fn is_exact(&self) -> bool { -// match self.exact { -// Exact::Yes => true, -// Exact::No => false, -// } -// } -//} -// -//impl fmt::Display for Number { -// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -// write!(f, "{}", self.real).and_then( -// |r| self.imag.map(|i| write!(f, "{:+}i", i)).unwrap_or(Ok(r))) -// } -//} -// -//#[cfg(test)] -//mod tests { -// use super::Exact; -// use super::Number; -// use super::real::Real; -// -// #[test] -// fn exact_numbers_are_exact() { -// assert!(Number::from_int(3, Exact::Yes).is_exact()); -// assert!(!Number::from_int(3, Exact::No).is_exact()); -// } -// -// #[test] -// fn exact_irrationals_are_reduced() { -// let real = Real::Rational(3, 2); -// assert_eq!(Number::from_float(1.5, Exact::Yes), Number::new(real, None, Exact::Yes)); -// } -//}