[types] Add Frac type

This commit is contained in:
Eryn Wells 2018-09-07 06:59:13 -07:00
parent 34d612a832
commit 45bc366a41
4 changed files with 101 additions and 94 deletions

87
types/src/number/frac.rs Normal file
View file

@ -0,0 +1,87 @@
/* types/src/number/frac.rs
* Eryn Wells <eryn@erynwells.me>
*/
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<Int> {
if self.1 == 1 {
Some(Int(self.0))
} else {
None
}
}
fn as_frac(&self) -> Option<Frac> {
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<Obj> 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<Number + 'a> 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());
}
}

View file

@ -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<Int> { Some(*self) }
fn as_frac(&self) -> Option<Frac> { Some(Frac(self.0, 1)) }
}
impl Mul for Int {
@ -80,7 +81,7 @@ impl PartialEq<Obj> for Int {
impl<'a> PartialEq<Number + 'a> 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));
}
}

View file

@ -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<Int> { None }
/// Cast this Number to a Frac if possible.
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 }
}

View file

@ -1,90 +0,0 @@
/* types/src/number/rational.rs
* Eryn Wells <eryn@erynwells.me>
*/
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<Box<Number>> {
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::<Self>().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());
}
}