[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>
*/
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, %}

View file

@ -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<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 {
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<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 {
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<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 {
fn eq<'a>(&self, rhs: &'a Obj) -> bool {
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)]
mod tests {
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.
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<Frac> { 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<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));
// }
//}