Move rational conversion to math.rs

This commit is contained in:
Eryn Wells 2017-04-15 11:30:31 -07:00
parent 392c9e5ef8
commit f31094d115
2 changed files with 57 additions and 12 deletions

View file

@ -2,7 +2,7 @@
* Eryn Wells <eryn@erynwells.me>
*/
use number::Int;
use number::{Int, Flt};
pub trait GCD {
/// Find the greatest common divisor of `self` and another number.
@ -14,6 +14,11 @@ pub trait LCM {
fn lcm(self, other: Self) -> Self;
}
pub trait Rational {
/// Convert `self` into a rational number -- the quotient of two whole numbers.
fn to_rational(self) -> (Int, Int);
}
impl GCD for Int {
fn gcd(self, other: Int) -> Int {
let (mut a, mut b) = if self > other {
@ -43,6 +48,30 @@ impl LCM for Int {
}
}
impl Rational for Int {
fn to_rational(self) -> (Int, Int) { (self, 1) }
}
impl Rational for Flt {
fn to_rational(self) -> (Int, Int) {
// Convert the float to a fraction by iteratively multiplying by 10 until the fractional part of the float is 0.0.
let whole_part = self.trunc();
let mut p = self.fract();
let mut q = 1.0;
while p.fract() != 0.0 {
p *= 10.0;
q *= 10.0;
}
p += whole_part * q;
// Integers from here down. Reduce the fraction before returning.
let p = p as Int;
let q = q as Int;
let gcd = p.gcd(q);
(p / gcd, q / gcd)
}
}
#[cfg(test)]
mod tests {
use super::{LCM, GCD};

View file

@ -24,13 +24,19 @@ impl Real {
///
/// assert_eq!(Real::Integer(12).reduce(), Real::Integer(12));
/// assert_eq!(Real::Rational(2, 4).reduce(), Real::Rational(1, 2));
/// assert_eq!(Real::Rational(3, 7).reduce(), Real::Rational(3, 7));
/// assert_eq!(Real::Irrational(2.4).reduce(), Real::Irrational(2.4));
/// ```
pub fn reduce(self) -> Real {
match self {
Real::Rational(p, q) => {
let gcd = p.gcd(q);
if gcd == 1 {
self
}
else {
Real::Rational(p / gcd, q / gcd)
}
},
_ => self
}
@ -75,6 +81,23 @@ impl Real {
}
/// Demote a Real to the next lowest type, if possible.
///
/// # Examples
///
/// Integers can't reduce.
///
/// ```
/// use sibiltypes::number::Real;
/// assert!(Real::Integer(3).demote_once().is_none());
/// ```
///
/// Rationals can be demoted in certain cases.
///
/// ```
/// use sibiltypes::number::Real;
/// assert_eq!(Real::Rational(4, 1).demote_once(), Some(Real::Integer(4)));
/// assert!(Real::Rational(4, 7).demote_once().is_none());
/// ```
pub fn demote_once(self) -> Option<Real> {
match self {
Real::Integer(_) => None,
@ -84,17 +107,10 @@ impl Real {
else {
None
},
// TODO: Convert an irrational into a fraction.
Real::Irrational(v) => {
let whole_part = v.trunc();
let mut p = v.fract();
let mut q = 1.0;
while p.fract() != 0.0 {
p *= 10.0;
q *= 10.0;
}
p += whole_part * q;
Some(Real::Rational(p as Int, q as Int).reduce())
let (p, q) = v.to_rational();
// No need to reduce here since p/q is already reduced.
Some(Real::Rational(p, q))
}
}
}