]> git.lizzy.rs Git - rust.git/commitdiff
Improve std::num::pow implementation
authorBrendan Zabarauskas <bjzaba@yahoo.com.au>
Sun, 19 Jan 2014 13:39:05 +0000 (00:39 +1100)
committerBrendan Zabarauskas <bjzaba@yahoo.com.au>
Mon, 20 Jan 2014 07:09:46 +0000 (18:09 +1100)
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.

src/libstd/num/mod.rs

index 23a852cc3579ef9ca809f61d452ab5d836f3d0e2..34dd313d4421f12a6a6d453c434ffa0e67c8f1f9 100644 (file)
@@ -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.
@@ -1670,17 +1651,24 @@ fn test_from_primitive() {
 
     #[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);
     }
 }