[types] Move arithmetic ops to a macro 🤯
Sketch out Irr type and implement arithmetic types on it.
This commit is contained in:
		
							parent
							
								
									ce50ab5101
								
							
						
					
					
						commit
						1b26497d19
					
				
					 4 changed files with 108 additions and 188 deletions
				
			
		|  | @ -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, %} | ||||
|  |  | |||
|  | @ -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
									
								
							
							
						
						
									
										62
									
								
								types/src/number/irr.rs
									
										
									
									
									
										Normal 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 | ||||
|     } | ||||
| } | ||||
|  | @ -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));
 | ||||
| //    }
 | ||||
| //}
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue