Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Pow for BigUint and BigInt #54

Merged
merged 9 commits into from
Aug 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extern crate rand;
use std::mem::replace;
use test::Bencher;
use num_bigint::{BigInt, BigUint, RandBigInt};
use num_traits::{Zero, One, FromPrimitive, Num};
use num_traits::{Zero, One, FromPrimitive, Num, Pow};
use rand::{SeedableRng, StdRng};

fn get_rng() -> StdRng {
Expand Down Expand Up @@ -301,7 +301,7 @@ fn pow_bench(b: &mut Bencher) {
for i in 2..upper + 1 {
for j in 2..upper + 1 {
let i_big = BigUint::from_usize(i).unwrap();
num_traits::pow(i_big, j);
i_big.pow(j);
}
}
});
Expand Down
50 changes: 49 additions & 1 deletion src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use serde;

use integer::{Integer, Roots};
use traits::{ToPrimitive, FromPrimitive, Num, CheckedAdd, CheckedSub,
CheckedMul, CheckedDiv, Signed, Zero, One};
CheckedMul, CheckedDiv, Signed, Zero, One, Pow};

use self::Sign::{Minus, NoSign, Plus};

Expand Down Expand Up @@ -811,6 +811,54 @@ impl Signed for BigInt {
}
}


/// Help function for pow
///
/// Computes the effect of the exponent on the sign.
#[inline]
fn powsign<T: Integer>(sign: Sign, other: &T) -> Sign {
if other.is_zero() {
Plus
} else if sign != Minus {
sign
} else if other.is_odd() {
sign
} else {
-sign
}
}

macro_rules! pow_impl {
($T:ty) => {
impl<'a> Pow<$T> for &'a BigInt {
type Output = BigInt;

#[inline]
fn pow(self, rhs: $T) -> BigInt {
BigInt::from_biguint(powsign(self.sign, &rhs), (&self.data).pow(rhs))
}
}

impl<'a, 'b> Pow<&'b $T> for &'a BigInt {
type Output = BigInt;

#[inline]
fn pow(self, rhs: &$T) -> BigInt {
BigInt::from_biguint(powsign(self.sign, rhs), (&self.data).pow(rhs))
}
}
}
}

pow_impl!(u8);
pow_impl!(u16);
pow_impl!(u32);
pow_impl!(u64);
pow_impl!(usize);
#[cfg(has_i128)]
pow_impl!(u128);


// A convenience method for getting the absolute value of an i32 in a u32.
#[inline]
fn i32_abs_as_u32(a: i32) -> u32 {
Expand Down
53 changes: 51 additions & 2 deletions src/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use serde;

use integer::{Integer, Roots};
use traits::{ToPrimitive, FromPrimitive, Float, Num, Unsigned, CheckedAdd, CheckedSub, CheckedMul,
CheckedDiv, Zero, One, pow};
CheckedDiv, Zero, One, Pow};

use big_digit::{self, BigDigit, DoubleBigDigit};

Expand Down Expand Up @@ -434,6 +434,55 @@ impl One for BigUint {

impl Unsigned for BigUint {}

macro_rules! pow_impl {
($T:ty) => {
impl<'a> Pow<$T> for &'a BigUint {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for posterity: I was going to ask you to add for BigUint (by value) too, but I tried it myself first. That makes it really annoying when you do have a bigint value, as the pow method will then try to move ownership. You'd have to manually reference (&i).pow(exp) to avoid this and get the by-ref method, which is really bad for ergonomics.

So if anyone asks why we don't have that impl, here's the reason.

type Output = BigUint;

#[inline]
fn pow(self, mut exp: $T) -> Self::Output {
if exp == 0 { return BigUint::one(); }
let mut base = self.clone();


while exp & 1 == 0 {
base = &base * &base;
exp >>= 1;
}

if exp == 1 { return base; }

let mut acc = base.clone();
while exp > 1 {
exp >>= 1;
base = &base * &base;
if exp & 1 == 1 {
acc = &acc * &base;
}
}
acc
}
}

impl<'a, 'b> Pow<&'b $T> for &'a BigUint {
type Output = BigUint;

#[inline]
fn pow(self, exp: &$T) -> Self::Output {
self.pow(*exp)
}
}
}
}

pow_impl!(u8);
pow_impl!(u16);
pow_impl!(u32);
pow_impl!(u64);
pow_impl!(usize);
#[cfg(has_i128)]
pow_impl!(u128);

forward_all_binop_to_val_ref_commutative!(impl Add for BigUint, add);
forward_val_assign!(impl AddAssign for BigUint, add_assign);

Expand Down Expand Up @@ -1056,7 +1105,7 @@ impl Roots for BigUint {

loop {
s = u;
let q = self / pow(s.clone(), n_min_1);
let q = self / s.pow(n_min_1);
let t: BigUint = n_min_1 * &s + q;

u = t / n;
Expand Down
29 changes: 28 additions & 1 deletion tests/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::hash::{BuildHasher, Hasher, Hash};
use std::collections::hash_map::RandomState;

use num_integer::Integer;
use num_traits::{Zero, One, Signed, ToPrimitive, FromPrimitive, Num, Float};
use num_traits::{Zero, One, Signed, ToPrimitive, FromPrimitive, Num, Float, Pow};

mod consts;
use consts::*;
Expand Down Expand Up @@ -1092,3 +1092,30 @@ fn test_iter_product_generic() {
assert_eq!(result, data.iter().product());
assert_eq!(result, data.into_iter().product());
}

#[test]
fn test_pow() {
let one = BigInt::from(1i32);
let two = BigInt::from(2i32);
let four = BigInt::from(4i32);
let eight = BigInt::from(8i32);
let minus_two = BigInt::from(-2i32);
macro_rules! check {
($t:ty) => {
assert_eq!(two.pow(0 as $t), one);
assert_eq!(two.pow(1 as $t), two);
assert_eq!(two.pow(2 as $t), four);
assert_eq!(two.pow(3 as $t), eight);
assert_eq!(two.pow(&(3 as $t)), eight);
assert_eq!(minus_two.pow(0 as $t), one, "-2^0");
assert_eq!(minus_two.pow(1 as $t), minus_two, "-2^1");
assert_eq!(minus_two.pow(2 as $t), four, "-2^2");
assert_eq!(minus_two.pow(3 as $t), -&eight, "-2^3");
}
}
check!(u8);
check!(u16);
check!(u32);
check!(u64);
check!(usize);
}
32 changes: 30 additions & 2 deletions tests/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::hash::{BuildHasher, Hasher, Hash};
use std::collections::hash_map::RandomState;

use num_traits::{Num, Zero, One, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, ToPrimitive,
FromPrimitive, Float};
FromPrimitive, Float, Pow};

mod consts;
use consts::*;
Expand Down Expand Up @@ -766,7 +766,7 @@ fn test_sub() {
#[should_panic]
fn test_sub_fail_on_underflow() {
let (a, b): (BigUint, BigUint) = (Zero::zero(), One::one());
a - b;
let _ = a - b;
}

#[test]
Expand Down Expand Up @@ -1530,3 +1530,31 @@ fn test_iter_product_generic() {
assert_eq!(result, data.iter().product());
assert_eq!(result, data.into_iter().product());
}

#[test]
fn test_pow() {
let one = BigUint::from(1u32);
let two = BigUint::from(2u32);
let four = BigUint::from(4u32);
let eight = BigUint::from(8u32);
let tentwentyfour = BigUint::from(1024u32);
let twentyfourtyeight = BigUint::from(2048u32);
macro_rules! check {
($t:ty) => {
assert_eq!(two.pow(0 as $t), one);
assert_eq!(two.pow(1 as $t), two);
assert_eq!(two.pow(2 as $t), four);
assert_eq!(two.pow(3 as $t), eight);
assert_eq!(two.pow(10 as $t), tentwentyfour);
assert_eq!(two.pow(11 as $t), twentyfourtyeight);
assert_eq!(two.pow(&(11 as $t)), twentyfourtyeight);
}
}
check!(u8);
check!(u16);
check!(u32);
check!(u64);
check!(usize);
#[cfg(has_i128)]
check!(u128);
}
16 changes: 8 additions & 8 deletions tests/roots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate num_traits;

mod biguint {
use num_bigint::BigUint;
use num_traits::pow;
use num_traits::Pow;
use std::str::FromStr;

fn check(x: u64, n: u32) {
Expand All @@ -17,8 +17,8 @@ mod biguint {
assert_eq!(&res, &big_x.cbrt())
}

assert!(pow(res.clone(), n as usize) <= big_x);
assert!(pow(res.clone() + 1u32, n as usize) > big_x);
assert!(res.pow(n) <= big_x);
assert!((res + 1u32).pow(n) > big_x);
}

#[test]
Expand Down Expand Up @@ -58,7 +58,7 @@ mod biguint {

mod bigint {
use num_bigint::BigInt;
use num_traits::{Signed, pow};
use num_traits::{Signed, Pow};

fn check(x: i64, n: u32) {
let big_x = BigInt::from(x);
Expand All @@ -71,11 +71,11 @@ mod bigint {
}

if big_x.is_negative() {
assert!(pow(res.clone() - 1u32, n as usize) < big_x);
assert!(pow(res.clone(), n as usize) >= big_x);
assert!(res.pow(n) >= big_x);
assert!((res - 1u32).pow(n) < big_x);
} else {
assert!(pow(res.clone(), n as usize) <= big_x);
assert!(pow(res.clone() + 1u32, n as usize) > big_x);
assert!(res.pow(n) <= big_x);
assert!((res + 1u32).pow(n) > big_x);
}
}

Expand Down