[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