diff --git a/src/libcore/core.rc b/src/libcore/core.rc index b0a3939db73a0..61fbf98a7c61d 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -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, NumCast}; +pub use num::{Num, Signed, Unsigned, Natural, NumCast}; pub use ptr::Ptr; pub use to_str::ToStr; pub use clone::Clone; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 62004710196ec..5d663844e5b79 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -11,6 +11,7 @@ //! Operations and constants for `f32` use num::strconv; +use num::Signed; use num; use option::Option; use from_str; @@ -163,38 +164,6 @@ pub fn gt(x: f32, y: f32) -> bool { return x > y; } // FIXME (#1999): replace the predicates below with llvm intrinsics or // calls to the libmath macros in the rust runtime for performance. -/// Returns true if `x` is a positive number, including +0.0f320 and +Infinity -#[inline(always)] -pub fn is_positive(x: f32) -> bool { - x > 0.0f32 || (1.0f32/x) == infinity -} - -/// Returns true if `x` is a negative number, including -0.0f320 and -Infinity -#[inline(always)] -pub fn is_negative(x: f32) -> bool { - x < 0.0f32 || (1.0f32/x) == neg_infinity -} - -/** - * Returns true if `x` is a negative number, including -0.0f320 and -Infinity - * - * This is the same as `f32::is_negative`. - */ -#[inline(always)] -pub fn is_nonpositive(x: f32) -> bool { - return x < 0.0f32 || (1.0f32/x) == neg_infinity; -} - -/** - * Returns true if `x` is a positive number, including +0.0f320 and +Infinity - * - * This is the same as `f32::is_positive`.) - */ -#[inline(always)] -pub fn is_nonnegative(x: f32) -> bool { - return x > 0.0f32 || (1.0f32/x) == infinity; -} - /// Returns true if `x` is a zero number (positive or negative zero) #[inline(always)] pub fn is_zero(x: f32) -> bool { @@ -259,11 +228,6 @@ pub mod consts { pub static ln_10: f32 = 2.30258509299404568401799145468436421_f32; } -#[inline(always)] -pub fn signbit(x: f32) -> int { - if is_negative(x) { return 1; } else { return 0; } -} - #[inline(always)] pub fn logarithm(n: f32, b: f32) -> f32 { return log2(n) / log2(b); @@ -351,15 +315,41 @@ impl Neg for f32 { fn neg(&self) -> f32 { -*self } } +impl Signed for f32 { + /// Computes the absolute value. Returns `NaN` if the number is `NaN`. + #[inline(always)] + fn abs(&self) -> f32 { abs(*self) } + + /** + * # Returns + * + * - `1.0` if the number is positive, `+0.0` or `infinity` + * - `-1.0` if the number is negative, `-0.0` or `neg_infinity` + * - `NaN` if the number is `NaN` + */ + #[inline(always)] + fn signum(&self) -> f32 { + if is_NaN(*self) { NaN } else { copysign(1.0, *self) } + } + + /// Returns `true` if the number is positive, including `+0.0` and `infinity` + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == infinity } + + /// Returns `true` if the number is negative, including `-0.0` and `neg_infinity` + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == neg_infinity } +} + impl num::Round for f32 { #[inline(always)] fn round(&self, mode: num::RoundMode) -> f32 { match mode { num::RoundDown => floor(*self), num::RoundUp => ceil(*self), - num::RoundToZero if is_negative(*self) => ceil(*self), + num::RoundToZero if self.is_negative() => ceil(*self), num::RoundToZero => floor(*self), - num::RoundFromZero if is_negative(*self) => floor(*self), + num::RoundFromZero if self.is_negative() => floor(*self), num::RoundFromZero => ceil(*self) } } @@ -370,7 +360,7 @@ impl num::Round for f32 { fn ceil(&self) -> f32 { ceil(*self) } #[inline(always)] fn fract(&self) -> f32 { - if is_negative(*self) { + if self.is_negative() { (*self) - ceil(*self) } else { (*self) - floor(*self) @@ -595,6 +585,50 @@ impl num::FromStrRadix for f32 { } } +#[cfg(test)] +mod tests { + use f32::*; + + #[test] + pub fn test_signed() { + assert_eq!(infinity.abs(), infinity); + assert_eq!(1f32.abs(), 1f32); + assert_eq!(0f32.abs(), 0f32); + assert_eq!((-0f32).abs(), 0f32); + assert_eq!((-1f32).abs(), 1f32); + assert_eq!(neg_infinity.abs(), infinity); + assert_eq!((1f32/neg_infinity).abs(), 0f32); + assert!(is_NaN(NaN.abs())); + + assert_eq!(infinity.signum(), 1f32); + assert_eq!(1f32.signum(), 1f32); + assert_eq!(0f32.signum(), 1f32); + assert_eq!((-0f32).signum(), -1f32); + assert_eq!((-1f32).signum(), -1f32); + assert_eq!(neg_infinity.signum(), -1f32); + assert_eq!((1f32/neg_infinity).signum(), -1f32); + assert!(is_NaN(NaN.signum())); + + assert!(infinity.is_positive()); + assert!(1f32.is_positive()); + assert!(0f32.is_positive()); + assert!(!(-0f32).is_positive()); + assert!(!(-1f32).is_positive()); + assert!(!neg_infinity.is_positive()); + assert!(!(1f32/neg_infinity).is_positive()); + assert!(!NaN.is_positive()); + + assert!(!infinity.is_negative()); + assert!(!1f32.is_negative()); + assert!(!0f32.is_negative()); + assert!((-0f32).is_negative()); + assert!((-1f32).is_negative()); + assert!(neg_infinity.is_negative()); + assert!((1f32/neg_infinity).is_negative()); + assert!(!NaN.is_negative()); + } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 4762c395a2561..48f23fe8ba946 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -11,6 +11,7 @@ //! Operations and constants for `f64` use num::strconv; +use num::Signed; use num; use option::Option; use to_str; @@ -183,36 +184,6 @@ pub fn ge(x: f64, y: f64) -> bool { return x >= y; } #[inline(always)] pub fn gt(x: f64, y: f64) -> bool { return x > y; } -/// Returns true if `x` is a positive number, including +0.0f640 and +Infinity -#[inline(always)] -pub fn is_positive(x: f64) -> bool - { return x > 0.0f64 || (1.0f64/x) == infinity; } - -/// Returns true if `x` is a negative number, including -0.0f640 and -Infinity -#[inline(always)] -pub fn is_negative(x: f64) -> bool - { return x < 0.0f64 || (1.0f64/x) == neg_infinity; } - -/** - * Returns true if `x` is a negative number, including -0.0f640 and -Infinity - * - * This is the same as `f64::is_negative`. - */ -#[inline(always)] -pub fn is_nonpositive(x: f64) -> bool { - return x < 0.0f64 || (1.0f64/x) == neg_infinity; -} - -/** - * Returns true if `x` is a positive number, including +0.0f640 and +Infinity - * - * This is the same as `f64::positive`. - */ -#[inline(always)] -pub fn is_nonnegative(x: f64) -> bool { - return x > 0.0f64 || (1.0f64/x) == infinity; -} - /// Returns true if `x` is a zero number (positive or negative zero) #[inline(always)] pub fn is_zero(x: f64) -> bool { @@ -278,11 +249,6 @@ pub mod consts { pub static ln_10: f64 = 2.30258509299404568401799145468436421_f64; } -#[inline(always)] -pub fn signbit(x: f64) -> int { - if is_negative(x) { return 1; } else { return 0; } -} - #[inline(always)] pub fn logarithm(n: f64, b: f64) -> f64 { return log2(n) / log2(b); @@ -357,15 +323,41 @@ impl Neg for f64 { fn neg(&self) -> f64 { -*self } } +impl Signed for f64 { + /// Computes the absolute value. Returns `NaN` if the number is `NaN`. + #[inline(always)] + fn abs(&self) -> f64 { abs(*self) } + + /** + * # Returns + * + * - `1.0` if the number is positive, `+0.0` or `infinity` + * - `-1.0` if the number is negative, `-0.0` or `neg_infinity` + * - `NaN` if the number is `NaN` + */ + #[inline(always)] + fn signum(&self) -> f64 { + if is_NaN(*self) { NaN } else { copysign(1.0, *self) } + } + + /// Returns `true` if the number is positive, including `+0.0` and `infinity` + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == infinity } + + /// Returns `true` if the number is negative, including `-0.0` and `neg_infinity` + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == neg_infinity } +} + impl num::Round for f64 { #[inline(always)] fn round(&self, mode: num::RoundMode) -> f64 { match mode { num::RoundDown => floor(*self), num::RoundUp => ceil(*self), - num::RoundToZero if is_negative(*self) => ceil(*self), + num::RoundToZero if self.is_negative() => ceil(*self), num::RoundToZero => floor(*self), - num::RoundFromZero if is_negative(*self) => floor(*self), + num::RoundFromZero if self.is_negative() => floor(*self), num::RoundFromZero => ceil(*self) } } @@ -376,7 +368,7 @@ impl num::Round for f64 { fn ceil(&self) -> f64 { ceil(*self) } #[inline(always)] fn fract(&self) -> f64 { - if is_negative(*self) { + if self.is_negative() { (*self) - ceil(*self) } else { (*self) - floor(*self) @@ -601,6 +593,50 @@ impl num::FromStrRadix for f64 { } } +#[cfg(test)] +mod tests { + use f64::*; + + #[test] + pub fn test_signed() { + assert_eq!(infinity.abs(), infinity); + assert_eq!(1f64.abs(), 1f64); + assert_eq!(0f64.abs(), 0f64); + assert_eq!((-0f64).abs(), 0f64); + assert_eq!((-1f64).abs(), 1f64); + assert_eq!(neg_infinity.abs(), infinity); + assert_eq!((1f64/neg_infinity).abs(), 0f64); + assert!(is_NaN(NaN.abs())); + + assert_eq!(infinity.signum(), 1f64); + assert_eq!(1f64.signum(), 1f64); + assert_eq!(0f64.signum(), 1f64); + assert_eq!((-0f64).signum(), -1f64); + assert_eq!((-1f64).signum(), -1f64); + assert_eq!(neg_infinity.signum(), -1f64); + assert_eq!((1f64/neg_infinity).signum(), -1f64); + assert!(is_NaN(NaN.signum())); + + assert!(infinity.is_positive()); + assert!(1f64.is_positive()); + assert!(0f64.is_positive()); + assert!(!(-0f64).is_positive()); + assert!(!(-1f64).is_positive()); + assert!(!neg_infinity.is_positive()); + assert!(!(1f64/neg_infinity).is_positive()); + assert!(!NaN.is_positive()); + + assert!(!infinity.is_negative()); + assert!(!1f64.is_negative()); + assert!(!0f64.is_negative()); + assert!((-0f64).is_negative()); + assert!((-1f64).is_negative()); + assert!(neg_infinity.is_negative()); + assert!((1f64/neg_infinity).is_negative()); + assert!(!NaN.is_negative()); + } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index c5c1e52a14cd7..036d295943c7b 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -22,6 +22,7 @@ use f64; use num::strconv; +use num::Signed; use num; use option::Option; use to_str; @@ -42,7 +43,6 @@ pub use f64::{erf, erfc, exp, expm1, exp2, abs_sub}; pub use f64::{mul_add, fmax, fmin, nextafter, frexp, hypot, ldexp}; pub use f64::{lgamma, ln, log_radix, ln1p, log10, log2, ilog_radix}; pub use f64::{modf, pow, powi, round, sinh, tanh, tgamma, trunc}; -pub use f64::signbit; pub use f64::{j0, j1, jn, y0, y1, yn}; pub static NaN: float = 0.0/0.0; @@ -348,14 +348,6 @@ pub fn pow_with_uint(base: uint, pow: uint) -> float { return total; } -#[inline(always)] -pub fn is_positive(x: float) -> bool { f64::is_positive(x as f64) } -#[inline(always)] -pub fn is_negative(x: float) -> bool { f64::is_negative(x as f64) } -#[inline(always)] -pub fn is_nonpositive(x: float) -> bool { f64::is_nonpositive(x as f64) } -#[inline(always)] -pub fn is_nonnegative(x: float) -> bool { f64::is_nonnegative(x as f64) } #[inline(always)] pub fn is_zero(x: float) -> bool { f64::is_zero(x as f64) } #[inline(always)] @@ -428,11 +420,11 @@ impl num::Round for float { => f64::floor(*self as f64) as float, num::RoundUp => f64::ceil(*self as f64) as float, - num::RoundToZero if is_negative(*self) + num::RoundToZero if self.is_negative() => f64::ceil(*self as f64) as float, num::RoundToZero => f64::floor(*self as f64) as float, - num::RoundFromZero if is_negative(*self) + num::RoundFromZero if self.is_negative() => f64::floor(*self as f64) as float, num::RoundFromZero => f64::ceil(*self as f64) as float @@ -445,7 +437,7 @@ impl num::Round for float { fn ceil(&self) -> float { f64::ceil(*self as f64) as float} #[inline(always)] fn fract(&self) -> float { - if is_negative(*self) { + if self.is_negative() { (*self) - (f64::ceil(*self as f64) as float) } else { (*self) - (f64::floor(*self as f64) as float) @@ -501,10 +493,76 @@ impl Neg for float { fn neg(&self) -> float { -*self } } +impl Signed for float { + /// Computes the absolute value. Returns `NaN` if the number is `NaN`. + #[inline(always)] + fn abs(&self) -> float { abs(*self) } + + /** + * # Returns + * + * - `1.0` if the number is positive, `+0.0` or `infinity` + * - `-1.0` if the number is negative, `-0.0` or `neg_infinity` + * - `NaN` if the number is NaN + */ + #[inline(always)] + fn signum(&self) -> float { + if is_NaN(*self) { NaN } else { f64::copysign(1.0, *self as f64) as float } + } + + /// Returns `true` if the number is positive, including `+0.0` and `infinity` + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == infinity } + + /// Returns `true` if the number is negative, including `-0.0` and `neg_infinity` + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == neg_infinity } +} + #[cfg(test)] mod tests { use super::*; use prelude::*; + + #[test] + pub fn test_signed() { + assert_eq!(infinity.abs(), infinity); + assert_eq!(1f.abs(), 1f); + assert_eq!(0f.abs(), 0f); + assert_eq!((-0f).abs(), 0f); + assert_eq!((-1f).abs(), 1f); + assert_eq!(neg_infinity.abs(), infinity); + assert_eq!((1f/neg_infinity).abs(), 0f); + assert!(is_NaN(NaN.abs())); + + assert_eq!(infinity.signum(), 1f); + assert_eq!(1f.signum(), 1f); + assert_eq!(0f.signum(), 1f); + assert_eq!((-0f).signum(), -1f); + assert_eq!((-1f).signum(), -1f); + assert_eq!(neg_infinity.signum(), -1f); + assert_eq!((1f/neg_infinity).signum(), -1f); + assert!(is_NaN(NaN.signum())); + + assert!(infinity.is_positive()); + assert!(1f.is_positive()); + assert!(0f.is_positive()); + assert!(!(-0f).is_positive()); + assert!(!(-1f).is_positive()); + assert!(!neg_infinity.is_positive()); + assert!(!(1f/neg_infinity).is_positive()); + assert!(!NaN.is_positive()); + + assert!(!infinity.is_negative()); + assert!(!1f.is_negative()); + assert!(!0f.is_negative()); + assert!((-0f).is_negative()); + assert!((-1f).is_negative()); + assert!(neg_infinity.is_negative()); + assert!((1f/neg_infinity).is_negative()); + assert!(!NaN.is_negative()); + } + #[test] pub fn test_to_str_exact_do_decimal() { let s = to_str_exact(5.0, 4u); @@ -538,11 +596,11 @@ mod tests { } // note: -0 == 0, hence these slightly more complex tests match from_str(~"-0") { - Some(v) if is_zero(v) => assert!(is_negative(v)), + Some(v) if is_zero(v) => assert!(v.is_negative()), _ => fail!() } match from_str(~"0") { - Some(v) if is_zero(v) => assert!(is_positive(v)), + Some(v) if is_zero(v) => assert!(v.is_positive()), _ => fail!() } @@ -585,11 +643,11 @@ mod tests { } // note: -0 == 0, hence these slightly more complex tests match from_str_hex(~"-0") { - Some(v) if is_zero(v) => assert!(is_negative(v)), + Some(v) if is_zero(v) => assert!(v.is_negative()), _ => fail!() } match from_str_hex(~"0") { - Some(v) if is_zero(v) => assert!(is_positive(v)), + Some(v) if is_zero(v) => assert!(v.is_positive()), _ => fail!() } assert_eq!(from_str_hex(~"e"), Some(14.)); @@ -641,50 +699,6 @@ mod tests { assert_eq!(from_str_radix(~"1000.001", 2u), Some(8.125)); } - #[test] - pub fn test_positive() { - assert!(is_positive(infinity)); - assert!(is_positive(1.)); - assert!(is_positive(0.)); - assert!(!is_positive(-1.)); - assert!(!is_positive(neg_infinity)); - assert!(!is_positive(1./neg_infinity)); - assert!(!is_positive(NaN)); - } - - #[test] - pub fn test_negative() { - assert!(!is_negative(infinity)); - assert!(!is_negative(1.)); - assert!(!is_negative(0.)); - assert!(is_negative(-1.)); - assert!(is_negative(neg_infinity)); - assert!(is_negative(1./neg_infinity)); - assert!(!is_negative(NaN)); - } - - #[test] - pub fn test_nonpositive() { - assert!(!is_nonpositive(infinity)); - assert!(!is_nonpositive(1.)); - assert!(!is_nonpositive(0.)); - assert!(is_nonpositive(-1.)); - assert!(is_nonpositive(neg_infinity)); - assert!(is_nonpositive(1./neg_infinity)); - assert!(!is_nonpositive(NaN)); - } - - #[test] - pub fn test_nonnegative() { - assert!(is_nonnegative(infinity)); - assert!(is_nonnegative(1.)); - assert!(is_nonnegative(0.)); - assert!(!is_nonnegative(-1.)); - assert!(!is_nonnegative(neg_infinity)); - assert!(!is_nonnegative(1./neg_infinity)); - assert!(!is_nonnegative(NaN)); - } - #[test] pub fn test_to_str_inf() { assert_eq!(to_str_digits(infinity, 10u), ~"inf"); diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 684083f53e992..426ed8a8b0f6e 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -14,6 +14,7 @@ use to_str::ToStr; use from_str::FromStr; use num::{ToStrRadix, FromStrRadix}; use num::strconv; +use num::Signed; use num; use prelude::*; @@ -70,15 +71,6 @@ pub fn ge(x: T, y: T) -> bool { x >= y } #[inline(always)] pub fn gt(x: T, y: T) -> bool { x > y } -#[inline(always)] -pub fn is_positive(x: T) -> bool { x > 0 as T } -#[inline(always)] -pub fn is_negative(x: T) -> bool { x < 0 as T } -#[inline(always)] -pub fn is_nonpositive(x: T) -> bool { x <= 0 as T } -#[inline(always)] -pub fn is_nonnegative(x: T) -> bool { x >= 0 as T } - /** * Iterate over the range [`lo`..`hi`) * @@ -139,9 +131,7 @@ pub fn compl(i: T) -> T { /// Computes the absolute value #[inline(always)] -pub fn abs(i: T) -> T { - if is_negative(i) { -i } else { i } -} +pub fn abs(i: T) -> T { i.abs() } #[cfg(notest)] impl Ord for T { @@ -201,6 +191,24 @@ impl Div for T { #[cfg(stage2,notest)] #[cfg(stage3,notest)] impl Quot 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 } } @@ -215,6 +223,27 @@ impl Modulo for T { #[cfg(stage2,notest)] #[cfg(stage3,notest)] impl Rem 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 } } @@ -225,6 +254,155 @@ impl Neg for T { fn neg(&self) -> T { -*self } } +impl Signed for T { + /// Computes the absolute value + #[inline(always)] + fn abs(&self) -> T { + if self.is_negative() { -*self } else { *self } + } + + /** + * # Returns + * + * - `0` if the number is zero + * - `1` if the number is positive + * - `-1` if the number is negative + */ + #[inline(always)] + fn signum(&self) -> T { + match *self { + n if n > 0 => 1, + 0 => 0, + _ => -1, + } + } + + /// Returns true if the number is positive + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0 } + + /// Returns true if the number is negative + #[inline(always)] + 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 for T { #[inline(always)] @@ -344,6 +522,117 @@ mod tests { use super::inst::T; use prelude::*; + #[test] + pub fn test_signed() { + assert_eq!((1 as T).abs(), 1 as T); + assert_eq!((0 as T).abs(), 0 as T); + assert_eq!((-1 as T).abs(), 1 as T); + + assert_eq!((1 as T).signum(), 1 as T); + assert_eq!((0 as T).signum(), 0 as T); + assert_eq!((-0 as T).signum(), 0 as T); + assert_eq!((-1 as T).signum(), -1 as T); + + assert!((1 as T).is_positive()); + assert!(!(0 as T).is_positive()); + assert!(!(-0 as T).is_positive()); + assert!(!(-1 as T).is_positive()); + + assert!(!(1 as T).is_negative()); + assert!(!(0 as T).is_negative()); + assert!(!(-0 as T).is_negative()); + 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))); diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index a0ff510cde7da..577bb3f0f150a 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -60,10 +60,37 @@ pub trait One { fn one() -> Self; } +pub trait Signed: Num + + Neg { + fn abs(&self) -> Self; + fn signum(&self) -> Self; + fn is_positive(&self) -> bool; + fn is_negative(&self) -> bool; +} + +pub trait Unsigned: Num {} + +// This should be moved into the default implementation for Signed::abs pub fn abs>(v: T) -> T { if v < Zero::zero() { v.neg() } else { v } } +pub trait Natural: Num + + Ord + + Quot + + Rem { + 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; diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index 4bb93907a3a50..a0da84a8c5359 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -15,6 +15,7 @@ use to_str::ToStr; use from_str::FromStr; use num::{ToStrRadix, FromStrRadix}; use num::strconv; +use num::Unsigned; use num; use option::Option; use prelude::*; @@ -51,15 +52,6 @@ pub fn ge(x: T, y: T) -> bool { x >= y } #[inline(always)] pub fn gt(x: T, y: T) -> bool { x > y } -#[inline(always)] -pub fn is_positive(x: T) -> bool { x > 0 as T } -#[inline(always)] -pub fn is_negative(x: T) -> bool { x < 0 as T } -#[inline(always)] -pub fn is_nonpositive(x: T) -> bool { x <= 0 as T } -#[inline(always)] -pub fn is_nonnegative(x: T) -> bool { x >= 0 as T } - #[inline(always)] /** * Iterate over the range [`start`,`start`+`step`..`stop`) @@ -190,6 +182,61 @@ impl Neg for T { fn neg(&self) -> T { -*self } } +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 for T { #[inline(always)] @@ -309,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))); diff --git a/src/libcore/prelude.rs b/src/libcore/prelude.rs index 15aa6c78ed6e0..03e6065a85caa 100644 --- a/src/libcore/prelude.rs +++ b/src/libcore/prelude.rs @@ -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, NumCast}; +pub use num::{Num, Signed, Unsigned, Natural, NumCast}; pub use path::GenericPath; pub use path::Path; pub use path::PosixPath; diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index c1cc01ed05a72..d3619a247677d 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -299,9 +299,9 @@ pub fn eval_const_expr_partial(tcx: middle::ty::ctxt, e: @expr) add => Ok(const_int(a + b)), subtract => Ok(const_int(a - b)), mul => Ok(const_int(a * b)), - quot if b == 0 => Err(~"quotient zero"), + quot if b == 0 => Err(~"attempted quotient with a divisor of zero"), quot => Ok(const_int(a / b)), - rem if b == 0 => Err(~"remainder zero"), + rem if b == 0 => Err(~"attempted remainder with a divisor of zero"), rem => Ok(const_int(a % b)), and | bitand => Ok(const_int(a & b)), or | bitor => Ok(const_int(a | b)), @@ -321,9 +321,9 @@ pub fn eval_const_expr_partial(tcx: middle::ty::ctxt, e: @expr) add => Ok(const_uint(a + b)), subtract => Ok(const_uint(a - b)), mul => Ok(const_uint(a * b)), - quot if b == 0 => Err(~"quotient zero"), + quot if b == 0 => Err(~"attempted quotient with a divisor of zero"), quot => Ok(const_uint(a / b)), - rem if b == 0 => Err(~"remainder zero"), + rem if b == 0 => Err(~"attempted remainder with a divisor of zero"), rem => Ok(const_uint(a % b)), and | bitand => Ok(const_uint(a & b)), or | bitor => Ok(const_uint(a | b)), diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index da87568b3d0c0..de64441fd95bf 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -785,9 +785,9 @@ pub fn cast_shift_rhs(op: ast::binop, pub fn fail_if_zero(cx: block, span: span, quotrem: ast::binop, rhs: ValueRef, rhs_t: ty::t) -> block { let text = if quotrem == ast::quot { - @~"quotient zero" + @~"attempted quotient with a divisor of zero" } else { - @~"remainder zero" + @~"attempted remainder with a divisor of zero" }; let is_zero = match ty::get(rhs_t).sty { ty::ty_int(t) => { diff --git a/src/libstd/num/bigint.rs b/src/libstd/num/bigint.rs index ee9749af5320e..5f0fd76640a30 100644 --- a/src/libstd/num/bigint.rs +++ b/src/libstd/num/bigint.rs @@ -154,6 +154,8 @@ impl One for BigUint { pub fn one() -> BigUint { BigUint::new(~[1]) } } +impl Unsigned for BigUint {} + impl Add for BigUint { fn add(&self, other: &BigUint) -> BigUint { let new_len = uint::max(self.data.len(), other.data.len()); @@ -469,11 +471,8 @@ pub impl BigUint { } fn is_zero(&self) -> bool { self.data.is_empty() } + fn is_not_zero(&self) -> bool { !self.data.is_empty() } - fn is_positive(&self) -> bool { self.is_not_zero() } - fn is_negative(&self) -> bool { false } - fn is_nonpositive(&self) -> bool { self.is_zero() } - fn is_nonnegative(&self) -> bool { true } fn to_uint(&self) -> uint { match self.data.len() { @@ -693,6 +692,27 @@ impl One for BigInt { } } +impl Signed for BigInt { + fn abs(&self) -> BigInt { + match self.sign { + Plus | Zero => copy *self, + Minus => BigInt::from_biguint(Plus, copy self.data) + } + } + + fn signum(&self) -> BigInt { + match self.sign { + Plus => BigInt::from_biguint(Plus, One::one()), + Minus => BigInt::from_biguint(Minus, One::one()), + Zero => Zero::zero(), + } + } + + fn is_positive(&self) -> bool { self.sign == Plus } + + fn is_negative(&self) -> bool { self.sign == Minus } +} + impl Add for BigInt { fn add(&self, other: &BigInt) -> BigInt { match (self.sign, other.sign) { @@ -888,11 +908,8 @@ pub impl BigInt { } fn is_zero(&self) -> bool { self.sign == Zero } + fn is_not_zero(&self) -> bool { self.sign != Zero } - fn is_positive(&self) -> bool { self.sign == Plus } - fn is_negative(&self) -> bool { self.sign == Minus } - fn is_nonpositive(&self) -> bool { self.sign != Plus } - fn is_nonnegative(&self) -> bool { self.sign != Minus } fn to_uint(&self) -> uint { match self.sign { diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index d368f9d6769de..0123341957903 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -1,6 +1,6 @@ enum test { - quot_zero = 1/0, //~ERROR expected constant: quotient zero - rem_zero = 1%0 //~ERROR expected constant: remainder zero + quot_zero = 1/0, //~ERROR expected constant: attempted quotient with a divisor of zero + rem_zero = 1%0 //~ERROR expected constant: attempted remainder with a divisor of zero } fn main() {} diff --git a/src/test/run-fail/divide-by-zero.rs b/src/test/run-fail/divide-by-zero.rs index 7a17dd024153c..d4f3828ea7174 100644 --- a/src/test/run-fail/divide-by-zero.rs +++ b/src/test/run-fail/divide-by-zero.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:quotient zero +// error-pattern:attempted quotient with a divisor of zero fn main() { let y = 0; let z = 1 / y; diff --git a/src/test/run-fail/mod-zero.rs b/src/test/run-fail/mod-zero.rs index c379a8fc65fd7..b3e083c77fb23 100644 --- a/src/test/run-fail/mod-zero.rs +++ b/src/test/run-fail/mod-zero.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:remainder zero +// error-pattern:attempted remainder with a divisor of zero fn main() { let y = 0; let z = 1 % y; diff --git a/src/test/run-pass/block-arg.rs b/src/test/run-pass/block-arg.rs index 4fecbd4e956b3..93c44b8faa128 100644 --- a/src/test/run-pass/block-arg.rs +++ b/src/test/run-pass/block-arg.rs @@ -18,28 +18,28 @@ pub fn main() { } // Usable at all: - let mut any_negative = do vec::any(v) |e| { float::is_negative(*e) }; + let mut any_negative = do vec::any(v) |e| { e.is_negative() }; assert!(any_negative); // Higher precedence than assignments: - any_negative = do vec::any(v) |e| { float::is_negative(*e) }; + any_negative = do vec::any(v) |e| { e.is_negative() }; assert!(any_negative); // Higher precedence than unary operations: - let abs_v = do vec::map(v) |e| { float::abs(*e) }; - assert!(do vec::all(abs_v) |e| { float::is_nonnegative(*e) }); - assert!(!do vec::any(abs_v) |e| { float::is_negative(*e) }); + let abs_v = do vec::map(v) |e| { e.abs() }; + assert!(do vec::all(abs_v) |e| { e.is_positive() }); + assert!(!do vec::any(abs_v) |e| { e.is_negative() }); // Usable in funny statement-like forms: - if !do vec::any(v) |e| { float::is_positive(*e) } { + if !do vec::any(v) |e| { e.is_positive() } { assert!(false); } - match do vec::all(v) |e| { float::is_negative(*e) } { + match do vec::all(v) |e| { e.is_negative() } { true => { fail!(~"incorrect answer."); } false => { } } match 3 { - _ if do vec::any(v) |e| { float::is_negative(*e) } => { + _ if do vec::any(v) |e| { e.is_negative() } => { } _ => { fail!(~"wrong answer."); @@ -56,7 +56,7 @@ pub fn main() { // In the tail of a block let w = - if true { do vec::any(abs_v) |e| { float::is_nonnegative(*e) } } + if true { do vec::any(abs_v) |e| { e.is_positive() } } else { false }; assert!(w); }