Move rational conversion to math.rs
This commit is contained in:
parent
392c9e5ef8
commit
f31094d115
2 changed files with 57 additions and 12 deletions
|
@ -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};
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue