Skip to content

Commit

Permalink
Implement Natural trait
Browse files Browse the repository at this point in the history
This adds the following methods to ints and uints:

- div
- modulo
- div_mod
- quot_rem
- gcd
- lcm
- divisible_by
- is_even
- is_odd

I have not implemented Natural for BigInt and BigUInt because they're a little over my head.
  • Loading branch information
brendanzab committed Apr 24, 2013
1 parent aef2490 commit f39152e
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/libcore/core.rc
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub use iter::{BaseIter, ExtendedIter, EqIter, CopyableIter};
pub use iter::{CopyableOrderedIter, CopyableNonstrictIter, Times};
pub use iter::{ExtendedMutableIter};

pub use num::{Num, Signed, Unsigned, NumCast};
pub use num::{Num, Signed, Unsigned, Natural, NumCast};
pub use ptr::Ptr;
pub use to_str::ToStr;
pub use clone::Clone;
Expand Down
245 changes: 245 additions & 0 deletions src/libcore/num/int-template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,24 @@ impl Div<T,T> for T {
#[cfg(stage2,notest)]
#[cfg(stage3,notest)]
impl Quot<T,T> for T {
/**
* Returns the integer quotient, truncated towards 0. As this behaviour reflects
* the underlying machine implementation it is more efficient than `Natural::div`.
*
* # Examples
*
* ~~~
* assert!( 8 / 3 == 2);
* assert!( 8 / -3 == -2);
* assert!(-8 / 3 == -2);
* assert!(-8 / -3 == 2);
* assert!( 1 / 2 == 0);
* assert!( 1 / -2 == 0);
* assert!(-1 / 2 == 0);
* assert!(-1 / -2 == 0);
* ~~~
*/
#[inline(always)]
fn quot(&self, other: &T) -> T { *self / *other }
}
Expand All @@ -205,6 +223,27 @@ impl Modulo<T,T> for T {
#[cfg(stage2,notest)]
#[cfg(stage3,notest)]
impl Rem<T,T> for T {
/**
* Returns the integer remainder after division, satisfying:
*
* ~~~
* assert!((n / d) * d + (n % d) == n)
* ~~~
*
* # Examples
*
* ~~~
* assert!( 8 % 3 == 2);
* assert!( 8 % -3 == 2);
* assert!(-8 % 3 == -2);
* assert!(-8 % -3 == -2);
* assert!( 1 % 2 == 1);
* assert!( 1 % -2 == 1);
* assert!(-1 % 2 == -1);
* assert!(-1 % -2 == -1);
* ~~~
*/
#[inline(always)]
fn rem(&self, other: &T) -> T { *self % *other }
}
Expand Down Expand Up @@ -247,6 +286,123 @@ impl Signed for T {
fn is_negative(&self) -> bool { *self < 0 }
}
impl Natural for T {
/**
* Floored integer division
*
* # Examples
*
* ~~~
* assert!(( 8).div( 3) == 2);
* assert!(( 8).div(-3) == -3);
* assert!((-8).div( 3) == -3);
* assert!((-8).div(-3) == 2);
*
* assert!(( 1).div( 2) == 0);
* assert!(( 1).div(-2) == -1);
* assert!((-1).div( 2) == -1);
* assert!((-1).div(-2) == 0);
* ~~~
*/
#[inline(always)]
fn div(&self, other: T) -> T {
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
match self.quot_rem(other) {
(q, r) if (r > 0 && other < 0)
|| (r < 0 && other > 0) => q - 1,
(q, _) => q,
}
}
/**
* Integer modulo, satisfying:
*
* ~~~
* assert!(n.div(d) * d + n.modulo(d) == n)
* ~~~
*
* # Examples
*
* ~~~
* assert!(( 8).modulo( 3) == 2);
* assert!(( 8).modulo(-3) == -1);
* assert!((-8).modulo( 3) == 1);
* assert!((-8).modulo(-3) == -2);
*
* assert!(( 1).modulo( 2) == 1);
* assert!(( 1).modulo(-2) == -1);
* assert!((-1).modulo( 2) == 1);
* assert!((-1).modulo(-2) == -1);
* ~~~
*/
#[inline(always)]
fn modulo(&self, other: T) -> T {
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
match *self % other {
r if (r > 0 && other < 0)
|| (r < 0 && other > 0) => r + other,
r => r,
}
}
/// Calculates `div` and `modulo` simultaneously
#[inline(always)]
fn div_mod(&self, other: T) -> (T,T) {
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
match self.quot_rem(other) {
(q, r) if (r > 0 && other < 0)
|| (r < 0 && other > 0) => (q - 1, r + other),
(q, r) => (q, r),
}
}
/// Calculates `quot` (`\`) and `rem` (`%`) simultaneously
#[inline(always)]
fn quot_rem(&self, other: T) -> (T,T) {
(*self / other, *self % other)
}
/**
* Calculates the Greatest Common Divisor (GCD) of the number and `other`
*
* The result is always positive
*/
#[inline(always)]
fn gcd(&self, other: T) -> T {
// Use Euclid's algorithm
let mut m = *self, n = other;
while m != 0 {
let temp = m;
m = n % temp;
n = temp;
}
n.abs()
}
/**
* Calculates the Lowest Common Multiple (LCM) of the number and `other`
*/
#[inline(always)]
fn lcm(&self, other: T) -> T {
((*self * other) / self.gcd(other)).abs() // should not have to recaluculate abs
}
/// Returns `true` if the number can be divided by `other` without leaving a remainder
#[inline(always)]
fn divisible_by(&self, other: T) -> bool { *self % other == 0 }
/// Returns `true` if the number is divisible by `2`
#[inline(always)]
fn is_even(&self) -> bool { self.divisible_by(2) }
/// Returns `true` if the number is not divisible by `2`
#[inline(always)]
fn is_odd(&self) -> bool { !self.is_even() }
}
#[cfg(notest)]
impl BitOr<T,T> for T {
#[inline(always)]
Expand Down Expand Up @@ -388,6 +544,95 @@ mod tests {
assert!((-1 as T).is_negative());
}
/**
* Checks that the division rule holds for:
*
* - `n`: numerator (dividend)
* - `d`: denominator (divisor)
* - `qr`: quotient and remainder
*/
#[cfg(test)]
fn test_division_rule(nd: (T,T), qr: (T,T)) {
let (n,d) = nd,
(q,r) = qr;
assert_eq!(d * q + r, n);
}
#[test]
fn test_quot_rem() {
fn test_nd_qr(nd: (T,T), qr: (T,T)) {
let (n,d) = nd;
let separate_quot_rem = (n / d, n % d);
let combined_quot_rem = n.quot_rem(d);
assert_eq!(separate_quot_rem, qr);
assert_eq!(combined_quot_rem, qr);
test_division_rule(nd, separate_quot_rem);
test_division_rule(nd, combined_quot_rem);
}
test_nd_qr(( 8, 3), ( 2, 2));
test_nd_qr(( 8, -3), (-2, 2));
test_nd_qr((-8, 3), (-2, -2));
test_nd_qr((-8, -3), ( 2, -2));
test_nd_qr(( 1, 2), ( 0, 1));
test_nd_qr(( 1, -2), ( 0, 1));
test_nd_qr((-1, 2), ( 0, -1));
test_nd_qr((-1, -2), ( 0, -1));
}
#[test]
fn test_div_mod() {
fn test_nd_dm(nd: (T,T), dm: (T,T)) {
let (n,d) = nd;
let separate_div_mod = (n.div(d), n.modulo(d));
let combined_div_mod = n.div_mod(d);
assert_eq!(separate_div_mod, dm);
assert_eq!(combined_div_mod, dm);
test_division_rule(nd, separate_div_mod);
test_division_rule(nd, combined_div_mod);
}
test_nd_dm(( 8, 3), ( 2, 2));
test_nd_dm(( 8, -3), (-3, -1));
test_nd_dm((-8, 3), (-3, 1));
test_nd_dm((-8, -3), ( 2, -2));
test_nd_dm(( 1, 2), ( 0, 1));
test_nd_dm(( 1, -2), (-1, -1));
test_nd_dm((-1, 2), (-1, 1));
test_nd_dm((-1, -2), ( 0, -1));
}
#[test]
fn test_gcd() {
assert_eq!((10 as T).gcd(2), 2 as T);
assert_eq!((10 as T).gcd(3), 1 as T);
assert_eq!((0 as T).gcd(3), 3 as T);
assert_eq!((3 as T).gcd(3), 3 as T);
assert_eq!((56 as T).gcd(42), 14 as T);
assert_eq!((3 as T).gcd(-3), 3 as T);
assert_eq!((-6 as T).gcd(3), 3 as T);
assert_eq!((-4 as T).gcd(-2), 2 as T);
}
#[test]
fn test_lcm() {
assert_eq!((1 as T).lcm(0), 0 as T);
assert_eq!((0 as T).lcm(1), 0 as T);
assert_eq!((1 as T).lcm(1), 1 as T);
assert_eq!((-1 as T).lcm(1), 1 as T);
assert_eq!((1 as T).lcm(-1), 1 as T);
assert_eq!((-1 as T).lcm(-1), 1 as T);
assert_eq!((8 as T).lcm(9), 72 as T);
assert_eq!((11 as T).lcm(5), 55 as T);
}
#[test]
fn test_bitwise_ops() {
assert_eq!(0b1110 as T, (0b1100 as T).bitor(&(0b1010 as T)));
Expand Down
16 changes: 16 additions & 0 deletions src/libcore/num/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ pub fn abs<T:Ord + Zero + Neg<T>>(v: T) -> T {
if v < Zero::zero() { v.neg() } else { v }
}

pub trait Natural: Num
+ Ord
+ Quot<Self,Self>
+ Rem<Self,Self> {
fn div(&self, other: Self) -> Self;
fn modulo(&self, other: Self) -> Self;
fn div_mod(&self, other: Self) -> (Self,Self);
fn quot_rem(&self, other: Self) -> (Self,Self);

fn gcd(&self, other: Self) -> Self;
fn lcm(&self, other: Self) -> Self;
fn divisible_by(&self, other: Self) -> bool;
fn is_even(&self) -> bool;
fn is_odd(&self) -> bool;
}

pub trait Round {
fn round(&self, mode: RoundMode) -> Self;

Expand Down
72 changes: 72 additions & 0 deletions src/libcore/num/uint-template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,59 @@ impl Neg<T> for T {
impl Unsigned for T {}
impl Natural for T {
/// Unsigned integer division. Returns the same result as `quot` (`/`).
#[inline(always)]
fn div(&self, other: T) -> T { *self / other }
/// Unsigned integer modulo operation. Returns the same result as `rem` (`%`).
#[inline(always)]
fn modulo(&self, other: T) -> T { *self / other }
/// Calculates `div` and `modulo` simultaneously
#[inline(always)]
fn div_mod(&self, other: T) -> (T,T) {
(*self / other, *self % other)
}
/// Calculates `quot` (`\`) and `rem` (`%`) simultaneously
#[inline(always)]
fn quot_rem(&self, other: T) -> (T,T) {
(*self / other, *self % other)
}
/// Calculates the Greatest Common Divisor (GCD) of the number and `other`
#[inline(always)]
fn gcd(&self, other: T) -> T {
// Use Euclid's algorithm
let mut m = *self, n = other;
while m != 0 {
let temp = m;
m = n % temp;
n = temp;
}
n
}
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`
#[inline(always)]
fn lcm(&self, other: T) -> T {
(*self * other) / self.gcd(other)
}
/// Returns `true` if the number can be divided by `other` without leaving a remainder
#[inline(always)]
fn divisible_by(&self, other: T) -> bool { *self % other == 0 }
/// Returns `true` if the number is divisible by `2`
#[inline(always)]
fn is_even(&self) -> bool { self.divisible_by(2) }
/// Returns `true` if the number is not divisible by `2`
#[inline(always)]
fn is_odd(&self) -> bool { !self.is_even() }
}
#[cfg(notest)]
impl BitOr<T,T> for T {
#[inline(always)]
Expand Down Expand Up @@ -303,6 +356,25 @@ mod tests {
use super::inst::T;
use prelude::*;
#[test]
fn test_gcd() {
assert_eq!((10 as T).gcd(2), 2 as T);
assert_eq!((10 as T).gcd(3), 1 as T);
assert_eq!((0 as T).gcd(3), 3 as T);
assert_eq!((3 as T).gcd(3), 3 as T);
assert_eq!((56 as T).gcd(42), 14 as T);
}
#[test]
fn test_lcm() {
assert_eq!((1 as T).lcm(0), 0 as T);
assert_eq!((0 as T).lcm(1), 0 as T);
assert_eq!((1 as T).lcm(1), 1 as T);
assert_eq!((8 as T).lcm(9), 72 as T);
assert_eq!((11 as T).lcm(5), 55 as T);
assert_eq!((99 as T).lcm(17), 1683 as T);
}
#[test]
fn test_bitwise_ops() {
assert_eq!(0b1110 as T, (0b1100 as T).bitor(&(0b1010 as T)));
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub use hash::Hash;
pub use iter::{BaseIter, ReverseIter, MutableIter, ExtendedIter, EqIter};
pub use iter::{CopyableIter, CopyableOrderedIter, CopyableNonstrictIter};
pub use iter::{Times, ExtendedMutableIter};
pub use num::{Num, Signed, Unsigned, NumCast};
pub use num::{Num, Signed, Unsigned, Natural, NumCast};
pub use path::GenericPath;
pub use path::Path;
pub use path::PosixPath;
Expand Down

0 comments on commit f39152e

Please sign in to comment.