[types] Add Frac type
This commit is contained in:
parent
34d612a832
commit
45bc366a41
4 changed files with 101 additions and 94 deletions
87
types/src/number/frac.rs
Normal file
87
types/src/number/frac.rs
Normal 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());
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue