[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>
|
* 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, %}
|
||||||
|
|
|
@ -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
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.
|
//! 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));
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue