Skip to content

Commit

Permalink
Improve std::num::pow implementation
Browse files Browse the repository at this point in the history
The implementation has been made more succinct and no longer requires Clone. The coverage of the associated unit test has also been increased to check more combinations of bases, exponents, and expected results.
  • Loading branch information
brendanzab committed Jan 20, 2014
1 parent cf56624 commit 509283d
Showing 1 changed file with 30 additions and 42 deletions.
72 changes: 30 additions & 42 deletions src/libstd/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,48 +304,29 @@ pub trait Real: Signed
fn to_radians(&self) -> Self;
}

/// Raises a value to the power of exp, using
/// exponentiation by squaring.
/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// # Example
///
/// ```rust
/// use std::num;
///
/// let sixteen = num::pow(2, 4u);
/// assert_eq!(sixteen, 16);
/// assert_eq!(num::pow(2, 4), 16);
/// ```
#[inline]
pub fn pow<T: Clone+One+Mul<T, T>>(num: T, exp: uint) -> T {
let one: uint = One::one();
let num_one: T = One::one();

if exp.is_zero() { return num_one; }
if exp == one { return num.clone(); }

let mut i: uint = exp;
let mut v: T;
let mut r: T = num_one;

// This if is to avoid cloning self.
if (i & one) == one {
r = r * num;
i = i - one;
}

i = i >> one;
v = num * num;

while !i.is_zero() {
if (i & one) == one {
r = r * v;
i = i - one;
pub fn pow<T: One + Mul<T, T>>(mut base: T, mut exp: uint) -> T {
if exp == 1 { base }
else {
let mut acc = one::<T>();
while exp > 0 {
if (exp & 1) == 1 {
acc = acc * base;
}
base = base * base;
exp = exp >> 1;
}
i = i >> one;
v = v * v;
acc
}

r
}

/// Raise a number to a power.
Expand Down Expand Up @@ -1670,17 +1651,24 @@ mod tests {

#[test]
fn test_pow() {
fn assert_pow<T: Eq+Clone+One+Mul<T, T>>(num: T, exp: uint) -> () {
assert_eq!(num::pow(num.clone(), exp),
range(1u, exp).fold(num.clone(), |acc, _| acc * num));
fn naive_pow<T: One + Mul<T, T>>(base: T, exp: uint) -> T {
range(0, exp).fold(one::<T>(), |acc, _| acc * base)
}

assert_eq!(num::pow(3, 0), 1);
assert_eq!(num::pow(5, 1), 5);
assert_pow(-4, 2);
assert_pow(8, 3);
assert_pow(8, 5);
assert_pow(2u64, 50);
macro_rules! assert_pow(
(($num:expr, $exp:expr) => $expected:expr) => {{
let result = pow($num, $exp);
assert_eq!(result, $expected);
assert_eq!(result, naive_pow($num, $exp));
}}
)
assert_pow!((3, 0 ) => 1);
assert_pow!((5, 1 ) => 5);
assert_pow!((-4, 2 ) => 16);
assert_pow!((0.5, 5 ) => 0.03125);
assert_pow!((8, 3 ) => 512);
assert_pow!((8.0, 5 ) => 32768.0);
assert_pow!((8.5, 5 ) => 44370.53125);
assert_pow!((2u64, 50) => 1125899906842624);
}
}

Expand Down

0 comments on commit 509283d

Please sign in to comment.