From 45bc366a411a80d9f4a7c78d778dcdccb6faa0c0 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 7 Sep 2018 06:59:13 -0700 Subject: [PATCH] [types] Add Frac type --- types/src/number/frac.rs | 87 ++++++++++++++++++++++++++++++++++ types/src/number/integer.rs | 12 +++-- types/src/number/mod.rs | 6 ++- types/src/number/rational.rs | 90 ------------------------------------ 4 files changed, 101 insertions(+), 94 deletions(-) create mode 100644 types/src/number/frac.rs delete mode 100644 types/src/number/rational.rs diff --git a/types/src/number/frac.rs b/types/src/number/frac.rs new file mode 100644 index 0000000..a22df8e --- /dev/null +++ b/types/src/number/frac.rs @@ -0,0 +1,87 @@ +/* types/src/number/frac.rs + * Eryn Wells + */ + +use std::any::Any; +use std::fmt; +use number::{Int, Number}; +use object::{Obj, Object}; + +/// A fraction consisting of a numerator and denominator. +#[derive(Debug, Eq, PartialEq)] +pub struct Frac(pub i64, pub u64); + +impl Frac { +} + +impl Number for Frac { + fn as_int(&self) -> Option { + if self.1 == 1 { + Some(Int(self.0)) + } else { + None + } + } + + fn as_frac(&self) -> Option { + Some(Frac(self.0, self.1)) + } +} + +impl fmt::Display for Frac { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}/{}", self.0, self.1) + } +} + +impl Object for Frac { + fn as_any(&self) -> &Any { self } + fn as_num(&self) -> Option<&Number> { Some(self) } +} + +impl PartialEq for Frac { + 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 Frac { + fn eq(&self, rhs: &(Number + 'a)) -> bool { + match rhs.as_frac() { + Some(rhs) => *self == rhs, + None => false + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn equal_fracs_are_equal() { + assert_eq!(Frac(3, 2), Frac(3, 2)); + assert_ne!(Frac(12, 4), Frac(9, 7)); + } + + #[test] + fn fracs_should_reduce_to_ints_where_possible() { + let rational_as_integer = Frac(3, 1).as_int(); + assert!(rational_as_integer.is_some()); + // Oh my god this line is so dumb. + } + + #[test] + fn fracs_should_not_reduce_to_ints_where_impossible() { + let rational_as_integer = Frac(3, 2).as_int(); + assert!(rational_as_integer.is_none()); + } + + #[test] + fn fracs_are_exact() { + assert!(Frac(4, 2).is_exact()); + } +} diff --git a/types/src/number/integer.rs b/types/src/number/integer.rs index 169e2a7..35b87fb 100644 --- a/types/src/number/integer.rs +++ b/types/src/number/integer.rs @@ -5,7 +5,7 @@ use std::any::Any; use std::fmt; use std::ops::{Add, Mul}; -use number::Number; +use number::{Frac, Number}; use object::{Obj, Object}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -44,7 +44,8 @@ impl Object for Int { } impl Number for Int { - fn as_int(&self) -> Option<&Int> { Some(self) } + fn as_int(&self) -> Option { Some(*self) } + fn as_frac(&self) -> Option { Some(Frac(self.0, 1)) } } impl Mul for Int { @@ -80,7 +81,7 @@ impl PartialEq for Int { impl<'a> PartialEq for Int { fn eq(&self, rhs: &(Number + 'a)) -> bool { match rhs.as_int() { - Some(rhs) => *self == *rhs, + Some(rhs) => *self == rhs, None => false } } @@ -107,4 +108,9 @@ mod tests { fn integers_are_exact() { assert!(Int(4).is_exact()); } + + #[test] + fn integers_add() { + assert_eq!(Int(4) + Int(8), Int(12)); + } } diff --git a/types/src/number/mod.rs b/types/src/number/mod.rs index e63aa46..ae775bc 100644 --- a/types/src/number/mod.rs +++ b/types/src/number/mod.rs @@ -12,16 +12,20 @@ //! be represented as an Integer. mod integer; +mod frac; use object::Object; pub use self::integer::Int; +pub use self::frac::Frac; pub trait Number: Object { /// Cast this Number to an Int if possible. - fn as_int(&self) -> Option<&Int> { None } + fn as_int(&self) -> Option { None } + /// Cast this Number to a Frac if possible. + fn as_frac(&self) -> Option { None } /// Return `true` if this Number is an exact representation of its value. fn is_exact(&self) -> bool { true } } diff --git a/types/src/number/rational.rs b/types/src/number/rational.rs deleted file mode 100644 index 0ddbef2..0000000 --- a/types/src/number/rational.rs +++ /dev/null @@ -1,90 +0,0 @@ -/* types/src/number/rational.rs - * Eryn Wells - */ - -use std::any::Any; -use value::*; -use super::*; - -#[derive(Debug, Eq, PartialEq)] -pub struct Rational(pub Int, pub Int); - -impl Number for Rational { - fn convert_down(&self) -> Option> { - if self.1 == 1 { - Some(Box::new(Integer(self.0))) - } - else { - None - } - } - - fn is_exact(&self) -> bool { true } -} - -impl Value for Rational { - fn as_value(&self) -> &Value { self } -} - -impl IsBool for Rational { } -impl IsChar for Rational { } - -impl IsNumber for Rational { - fn is_rational(&self) -> bool { true } -} - -impl ValueEq for Rational { - fn eq(&self, other: &Value) -> bool { - other.as_any().downcast_ref::().map_or(false, |x| x == self) - } - - fn as_any(&self) -> &Any { self } -} - -#[cfg(test)] -mod tests { - use std::ops::Deref; - use number::*; - use value::*; - - #[test] - fn equal_rationals_are_equal() { - assert_eq!(Rational(3, 2), Rational(3, 2)); - assert_ne!(Rational(12, 4), Rational(9, 7)); - assert_eq!(Rational(4, 5).as_value(), Rational(4, 5).as_value()); - assert_ne!(Rational(5, 6).as_value(), Rational(7, 6).as_value()); - } - - #[test] - fn rationals_are_rationals() { - assert!(Rational(4, 3).is_complex()); - assert!(Rational(4, 3).is_real()); - assert!(Rational(4, 3).is_rational()); - assert!(!Rational(4, 3).is_integer()); - assert!(Rational(4, 3).is_number()); - assert!(!Rational(6, 8).is_char()); - assert!(!Rational(6, 9).is_bool()); - } - - #[test] - fn rationals_should_reduce_to_integers_where_possible() { - let rational_as_integer = Rational(3, 1).convert_down(); - assert!(rational_as_integer.is_some()); - // Oh my god this line is so dumb. - let rational_as_integer = rational_as_integer.unwrap(); - let rational_as_integer = rational_as_integer.as_value(); - assert_eq!(rational_as_integer.deref(), Integer(3).as_value()); - } - - #[test] - fn rationals_should_not_reduce_to_integers_where_impossible() { - let rational_as_integer = Rational(3, 2).convert_down(); - assert!(rational_as_integer.is_none()); - } - - #[test] - fn rationals_are_exact() { - assert!(Rational(4, 2).is_exact()); - assert!(!Rational(4, 2).is_inexact()); - } -}