[types] Add GCD and LCM to Int; implement Rem on Int

This commit is contained in:
Eryn Wells 2018-09-07 08:14:33 -07:00
parent 45bc366a41
commit 1aabce4f60
5 changed files with 116 additions and 96 deletions

37
types/src/number/arith.rs Normal file
View file

@ -0,0 +1,37 @@
/* types/src/number/arith.rs
* Eryn Wells <eryn@erynwells.me>
*/
pub trait GCD {
/// Find the greatest common divisor of `self` and another number.
fn gcd(self, other: Self) -> Self;
}
pub trait LCM {
/// Find the least common multiple of `self` and another number.
fn lcm(self, other: Self) -> Self;
}
//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)
// }
//}

View file

@ -4,6 +4,7 @@
use std::any::Any;
use std::fmt;
use std::ops::{Add, Mul};
use number::{Int, Number};
use object::{Obj, Object};

View file

@ -4,7 +4,8 @@
use std::any::Any;
use std::fmt;
use std::ops::{Add, Mul};
use std::ops::{Add, Mul, Rem};
use number::arith::{GCD, LCM};
use number::{Frac, Number};
use object::{Obj, Object};
@ -38,6 +39,32 @@ impl fmt::Display for Int {
}
}
impl GCD for Int {
fn gcd(self, other: Int) -> Int {
let (mut a, mut b) = if self.0 > other.0 {
(self.0, other.0)
} else {
(other.0, self.0)
};
while b != 0 {
let r = a % b;
a = b;
b = r;
}
Int(a)
}
}
impl LCM for Int {
fn lcm(self, other: Int) -> Int {
if self.0 == 0 && other.0 == 0 {
Int(0)
} else {
Int(self.0 * other.0 / self.gcd(other).0)
}
}
}
impl Object for Int {
fn as_any(&self) -> &Any { self }
fn as_num(&self) -> Option<&Number> { Some(self) }
@ -87,6 +114,27 @@ impl<'a> PartialEq<Number + 'a> for Int {
}
}
impl Rem for Int {
type Output = Int;
fn rem(self, rhs: Self) -> Self::Output {
Int(self.0 % rhs.0)
}
}
impl<'a> Rem<Int> for &'a Int {
type Output = Int;
fn rem(self, rhs: Int) -> Self::Output {
Int(self.0 % rhs.0)
}
}
impl<'a, 'b> Rem<&'a Int> for &'b Int {
type Output = Int;
fn rem(self, rhs: &Int) -> Self::Output {
Int(self.0 % rhs.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -113,4 +161,32 @@ mod tests {
fn integers_add() {
assert_eq!(Int(4) + Int(8), Int(12));
}
#[test]
fn integers_multiply() {
assert_eq!(Int(4) * Int(5), Int(20));
}
#[test]
fn integer_modulo_divide() {
assert_eq!(Int(20) % Int(5), Int(0));
assert_eq!(Int(20) % Int(6), Int(2));
}
#[test]
fn finding_int_gcd() {
assert_eq!(Int(0), Int(0).gcd(Int(0)));
assert_eq!(Int(10), Int(10).gcd(Int(0)));
assert_eq!(Int(10), Int(0).gcd(Int(10)));
assert_eq!(Int(10), Int(10).gcd(Int(20)));
assert_eq!(Int(44), Int(2024).gcd(Int(748)));
}
#[test]
fn finding_int_lcm() {
assert_eq!(Int(0), Int(0).lcm(Int(0)));
assert_eq!(Int(0), Int(10).lcm(Int(0)));
assert_eq!(Int(0), Int(10).lcm(Int(0)));
assert_eq!(Int(42), Int(21).lcm(Int(6)));
}
}

View file

@ -1,95 +0,0 @@
/* types/src/number/math.rs
* Eryn Wells <eryn@erynwells.me>
*/
use number::{Int, Flt};
pub trait GCD {
/// Find the greatest common divisor of `self` and another number.
fn gcd(self, other: Self) -> Self;
}
pub trait LCM {
/// Find the least common multiple of `self` and another number.
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 {
(self, other)
} else {
(other, self)
};
while b != 0 {
let r = a % b;
a = b;
b = r;
}
a
}
}
impl LCM for Int {
fn lcm(self, other: Int) -> Int {
if self == 0 && other == 0 {
0
}
else {
self * other / self.gcd(other)
}
}
}
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};
#[test]
fn gcd_works() {
assert_eq!(0, 0.gcd(0));
assert_eq!(10, 10.gcd(0));
assert_eq!(10, 0.gcd(10));
assert_eq!(10, 10.gcd(20));
assert_eq!(44, 2024.gcd(748));
}
#[test]
fn lcm_works() {
assert_eq!(0, 0.lcm(0));
assert_eq!(0, 10.lcm(0));
assert_eq!(0, 10.lcm(0));
assert_eq!(42, 21.lcm(6));
}
}

View file

@ -11,6 +11,7 @@
//! Integer can be cast as a Rational (by putting its value over 1), but a Rational like 1/2 cannot
//! be represented as an Integer.
mod arith;
mod integer;
mod frac;