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>
|
* 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};
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue