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> * Eryn Wells <eryn@erynwells.me>
*/ */
use number::Int; use number::{Int, Flt};
pub trait GCD { pub trait GCD {
/// Find the greatest common divisor of `self` and another number. /// Find the greatest common divisor of `self` and another number.
@ -14,6 +14,11 @@ pub trait LCM {
fn lcm(self, other: Self) -> Self; 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 { impl GCD for Int {
fn gcd(self, other: Int) -> Int { fn gcd(self, other: Int) -> Int {
let (mut a, mut b) = if self > other { 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)] #[cfg(test)]
mod tests { mod tests {
use super::{LCM, GCD}; use super::{LCM, GCD};

View file

@ -24,13 +24,19 @@ impl Real {
/// ///
/// assert_eq!(Real::Integer(12).reduce(), Real::Integer(12)); /// assert_eq!(Real::Integer(12).reduce(), Real::Integer(12));
/// assert_eq!(Real::Rational(2, 4).reduce(), Real::Rational(1, 2)); /// 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)); /// assert_eq!(Real::Irrational(2.4).reduce(), Real::Irrational(2.4));
/// ``` /// ```
pub fn reduce(self) -> Real { pub fn reduce(self) -> Real {
match self { match self {
Real::Rational(p, q) => { Real::Rational(p, q) => {
let gcd = p.gcd(q); let gcd = p.gcd(q);
Real::Rational(p / gcd, q / gcd) if gcd == 1 {
self
}
else {
Real::Rational(p / gcd, q / gcd)
}
}, },
_ => self _ => self
} }
@ -75,6 +81,23 @@ impl Real {
} }
/// Demote a Real to the next lowest type, if possible. /// 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> { pub fn demote_once(self) -> Option<Real> {
match self { match self {
Real::Integer(_) => None, Real::Integer(_) => None,
@ -84,17 +107,10 @@ impl Real {
else { else {
None None
}, },
// TODO: Convert an irrational into a fraction.
Real::Irrational(v) => { Real::Irrational(v) => {
let whole_part = v.trunc(); let (p, q) = v.to_rational();
let mut p = v.fract(); // No need to reduce here since p/q is already reduced.
let mut q = 1.0; Some(Real::Rational(p, q))
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())
} }
} }
} }