[types] Move arithmetic ops to a macro 🤯

Sketch out Irr type and implement arithmetic types on it.
This commit is contained in:
Eryn Wells 2018-09-09 11:23:44 -07:00
parent ce50ab5101
commit 1b26497d19
4 changed files with 108 additions and 188 deletions

View file

@ -2,6 +2,9 @@
* Eryn Wells <eryn@erynwells.me> * Eryn Wells <eryn@erynwells.me>
*/ */
use std::ops::{Add, Div, Mul, Sub, Rem};
use number::{Int, Irr};
pub trait GCD { pub trait GCD {
/// Find the greatest common divisor of `self` and another number. /// Find the greatest common divisor of `self` and another number.
fn gcd(self, other: Self) -> Self; fn gcd(self, other: Self) -> Self;
@ -12,26 +15,41 @@ pub trait LCM {
fn lcm(self, other: Self) -> Self; fn lcm(self, other: Self) -> Self;
} }
//impl Rational for Int { macro_rules! impl_newtype_arith_op {
// fn to_rational(self) -> (Int, Int) { (self, 1) } ($id:ident, $opt:ident, $opm:ident, $op:tt) => {
//} impl $opt for $id {
// type Output = $id;
//impl Rational for Flt { #[inline]
// fn to_rational(self) -> (Int, Int) { fn $opm(self, rhs: $id) -> Self::Output {
// // Convert the float to a fraction by iteratively multiplying by 10 until the fractional part of the float is 0.0. $id(self.0 $op rhs.0)
// let whole_part = self.trunc(); }
// let mut p = self.fract(); }
// let mut q = 1.0; impl<'a> $opt<$id> for &'a $id {
// while p.fract() != 0.0 { type Output = $id;
// p *= 10.0; #[inline]
// q *= 10.0; fn $opm(self, rhs: $id) -> Self::Output {
// } $id(self.0 $op rhs.0)
// p += whole_part * q; }
// }
// // Integers from here down. Reduce the fraction before returning. impl<'a, 'b> $opt<&'a $id> for &'b $id {
// let p = p as Int; type Output = $id;
// let q = q as Int; #[inline]
// let gcd = p.gcd(q); fn $opm(self, rhs: &$id) -> Self::Output {
// (p / gcd, q / gcd) $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, %}

View file

@ -4,7 +4,6 @@
use std::any::Any; use std::any::Any;
use std::fmt; use std::fmt;
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};
@ -16,54 +15,12 @@ impl Int {
pub fn zero() -> Int { Int(0) } 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<Int> 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 { impl fmt::Display for Int {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0) 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<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 > other { let (mut a, mut b) = if self > other {
@ -101,27 +58,6 @@ impl Number for Int {
fn is_zero(&self) -> bool { self.0 == 0 } 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<Int> 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<Obj> for Int { impl PartialEq<Obj> for Int {
fn eq<'a>(&self, rhs: &'a Obj) -> bool { fn eq<'a>(&self, rhs: &'a Obj) -> bool {
match rhs.obj().and_then(Object::as_num) { match rhs.obj().and_then(Object::as_num) {
@ -140,27 +76,6 @@ impl<'a> PartialEq<Number + 'a> for Int {
} }
} }
impl Rem for Int {
type Output = Int;
fn rem(self, rhs: Self) -> Self::Output {
Int(self.0 % rhs.0)
}
}
impl<'a> Rem<Int> 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

62
types/src/number/irr.rs Normal file
View file

@ -0,0 +1,62 @@
/* types/src/number/irr.rs
* Eryn Wells <eryn@erynwells.me>
*/
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<Int> {
if self.0.trunc() == self.0 {
Some(Int(self.0.trunc() as i64))
} else {
None
}
}
fn as_frac(&self) -> Option<Frac> {
if !self.0.is_infinite() && !self.0.is_nan() {
// TODO
None
} else {
None
}
}
fn is_zero(&self) -> bool { self.0 == 0.0 }
}
impl PartialEq<Obj> 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<Number + 'a> for Irr {
fn eq(&self, rhs: &(Number + 'a)) -> bool {
false
}
}

View file

@ -12,13 +12,15 @@
//! be represented as an Integer. //! be represented as an Integer.
mod arith; mod arith;
mod integer;
mod frac; mod frac;
mod integer;
mod irr;
use object::Object; use object::Object;
pub use self::integer::Int;
pub use self::frac::Frac; pub use self::frac::Frac;
pub use self::integer::Int;
pub use self::irr::Irr;
pub trait Number: pub trait Number:
Object Object
@ -29,83 +31,6 @@ 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 }
/// Return `true` if this Number is equal to 0.
fn is_zero(&self) -> bool; 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<Real>,
// exact: Exact,
//}
//impl Number {
// fn new(real: Real, imag: Option<Real>, 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));
// }
//}