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

Implement Pow for BigUint and BigInt #54

merged 9 commits into from
Aug 4, 2018

Conversation

thomwiggers
Copy link
Contributor

Implements #29

Copy link
Member

@cuviper cuviper left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

src/biguint.rs Outdated
}
}

impl<'a> pow::Pow<$signed> for BigUint {
Copy link
Member

Choose a reason for hiding this comment

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

Let's not do signed exponents here at all. Notice that for primitives, we only have signed Pow for the floating point types.

src/biguint.rs Outdated
#[inline]
fn pow(self, exp: $unsigned) -> Self::Output {
// square and multiply from num_traits::pow
pow::pow(self.clone(), exp as usize)
Copy link
Member

Choose a reason for hiding this comment

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

This can be faster if done directly, because a lot of value cloning can be avoided. We can't make that change in num_traits::pow yet for compatibility, but for some history see rust-num/num-traits#18, and the implementation in rust-num/num#334. That was only merged on the next branch, and never ultimately used.

Feel free to copy that code and adapt it here. It might as well work with the exponent type directly too, so you don't have to worry about exp as usize overflowing for any type, even u128. Not that it's realistic to use such exponents, but we can at least try to do the right thing if we support it at all.

Copy link
Contributor Author

@thomwiggers thomwiggers Jul 9, 2018

Choose a reason for hiding this comment

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

Doing this directly also immediately deals with the u128 implementation that was still FIXME.

src/biguint.rs Outdated
#[inline]
fn pow(self, exp: $unsigned) -> Self::Output {
// square and multiply from num_traits::pow
pow::pow(self, exp as usize)
Copy link
Member

Choose a reason for hiding this comment

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

Given a direct implementation by-reference, I would just forward to that from here.

For completeness, we should also have Pow<&'a $unsigned> for BigUint and Pow<&'a $unsigned> for &'b BigUint.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The exponents we implement are all Copy so I don't see much value for a reference – one which we'll immediately clone anyway, as we need to mutate it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also got rid of the implementation on BigUint and BigInt directly, the references should suffice.

src/bigint.rs Outdated
@@ -66,6 +66,24 @@ impl Mul<Sign> for Sign {
}
}

impl<T: PrimInt + BitAnd<Output=T>> Pow<T> for Sign {
Copy link
Member

Choose a reason for hiding this comment

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

BitAnd is already implied by PrimInt.

I'm a little hesitant to have this impl at all, but I guess it's in line with Mul and Neg for Sign. It's a little weird that "0" NoSign to the 0th power should be Plus, as the closest thing to a "1", but whatever.

src/bigint.rs Outdated
if self != Minus {
self
} else if other == T::zero() {
Plus
Copy link
Member

Choose a reason for hiding this comment

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

The check should use other.is_zero(), and this case should be first, because we also need Plus for NoSign.pow(0).

src/bigint.rs Outdated
self
} else if other == T::zero() {
Plus
} else if other & T::one() == T::one() {
Copy link
Member

Choose a reason for hiding this comment

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

Slightly better to use (other & T::one()).is_one() -- or if you switch to T: Integer, it's just other.is_odd().

src/bigint.rs Outdated
@@ -811,6 +829,28 @@ impl Signed for BigInt {
}
}

impl<T: PrimInt> Pow<T> for BigInt
where BigUint: Pow<T, Output=BigUint>
Copy link
Member

Choose a reason for hiding this comment

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

Let's have exponent references too, as I mentioned on BigUint.

I think it's technically a breaking change to do this generically, as someone could already have an impl, however silly, like:

struct Foo(usize);

impl Pow<Foo> for BigInt {
    type Output = BigInt;
    
    fn pow(self, exp: Foo) -> BigInt {
        num_traits::pow(self, exp.0)
    }
}

Let's just avoid that question by implementing it in macros like BigUint.

Copy link
Contributor Author

@thomwiggers thomwiggers left a comment

Choose a reason for hiding this comment

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

Thanks for the useful feedback, it was helpful. I've incorporated it.

src/biguint.rs Outdated
#[inline]
fn pow(self, exp: $unsigned) -> Self::Output {
// square and multiply from num_traits::pow
pow::pow(self.clone(), exp as usize)
Copy link
Contributor Author

@thomwiggers thomwiggers Jul 9, 2018

Choose a reason for hiding this comment

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

Doing this directly also immediately deals with the u128 implementation that was still FIXME.

src/biguint.rs Outdated
#[inline]
fn pow(self, exp: $unsigned) -> Self::Output {
// square and multiply from num_traits::pow
pow::pow(self, exp as usize)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also got rid of the implementation on BigUint and BigInt directly, the references should suffice.

Copy link
Member

@cuviper cuviper left a comment

Choose a reason for hiding this comment

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

I do want all the ref/value combinations implemented, even though the one you have is rightly the primary way to do it. The rest can just forward to that.

It's useful for generic code to have all of these cases covered -- note how the primitive integers also have all their ref/value implementations of the standard ops, even though they're trivially Copy types.

src/biguint.rs Outdated

if exp == 1 { return base; }

let mut acc = self.clone();
Copy link
Member

Choose a reason for hiding this comment

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

This should be base.clone(). None of your tests reached this, because they're all power-of-two exponents that hit if exp == 1 above.

Copy link
Member

Choose a reason for hiding this comment

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

Err, actually you do have a test for pow(3) -- but since that has no trailing zeros, base and self are still the same. Try an exponent like 10 to see the failure I'm talking about.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, I missed that when copying the pow implementation from next.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a few tests as well.

tests/bigint.rs Outdated
let two = 2u32.to_bigint().unwrap();
let minus_two = -2i32.to_bigint().unwrap();
let four = 4u32.to_bigint().unwrap();
let eight = 8u32.to_bigint().unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

These can use From like BigInt::from(8u32) nowadays -- although I know many other tests are still doing it the hard way too. (We ought to clean that up...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

src/biguint.rs Outdated
// square and multiply from num_traits::pow in the next branch
let mut base = self.clone();

if exp == 0 { return BigUint::one(); }
Copy link
Member

Choose a reason for hiding this comment

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

Might as well check this first, to potentially avoid a useless clone.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point.

src/biguint.rs Outdated

#[inline]
fn pow(self, mut exp: $unsigned) -> Self::Output {
// square and multiply from num_traits::pow in the next branch
Copy link
Member

Choose a reason for hiding this comment

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

Let's not immortalize the next branch in comments -- that stuff is obsolete. If you're trying to give credit, then a note in your commit message is enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

src/bigint.rs Outdated
@@ -66,6 +66,24 @@ impl Mul<Sign> for Sign {
}
}

impl<'a, T: Integer> Pow<&'a T> for Sign {
Copy link
Member

Choose a reason for hiding this comment

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

I'm still hesitating whether we really want a public impl for this. It's kind of silly to have a bunch of traits implemented for Sign. (An alternative is to just have a private fn to get the new sign.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Private fn ✔️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd have expected the entire Sign trait private by the way, but there's probably some good reason for it.

Copy link
Member

Choose a reason for hiding this comment

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

It's an enum Sign, not a trait, and it's a public argument for some of the constructors. We have to indicate the sign somehow, since we're not using 2's complement, so an enum is relatively clean. It usually stays out of the way. :)

bors bot added a commit that referenced this pull request Jul 19, 2018
56: Implement Roots for BigInt and BigUint r=cuviper a=mancabizjak

Supersedes #51 .

Since there is now a `Roots` trait with `sqrt`, `cbrt` and `nth_root` methods in the `num-integer` crate, this PR implements it for `BigInt` and `BigUint` types. I also added inherent methods on both types to allow the users access to all these functions without having to import `Roots`.

PS: `nth_root` currently  uses `num_traits::pow`. Should we perhaps wait for #54 to get merged, and then replace the call to use the new `pow::Pow` implementation on `BigUint`?

Co-authored-by: Manca Bizjak <manca.bizjak@xlab.si>
Copy link
Contributor Author

@thomwiggers thomwiggers left a comment

Choose a reason for hiding this comment

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

I've addressed the feedback.

Pow is now implemented for &u8 and u8 (and the rest of the primitive integer types).

src/bigint.rs Outdated
@@ -66,6 +66,24 @@ impl Mul<Sign> for Sign {
}
}

impl<'a, T: Integer> Pow<&'a T> for Sign {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Private fn ✔️

src/biguint.rs Outdated

if exp == 1 { return base; }

let mut acc = self.clone();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, I missed that when copying the pow implementation from next.

src/biguint.rs Outdated

if exp == 1 { return base; }

let mut acc = self.clone();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a few tests as well.

tests/bigint.rs Outdated
let two = 2u32.to_bigint().unwrap();
let minus_two = -2i32.to_bigint().unwrap();
let four = 4u32.to_bigint().unwrap();
let eight = 8u32.to_bigint().unwrap();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

src/bigint.rs Outdated
@@ -66,6 +66,24 @@ impl Mul<Sign> for Sign {
}
}

impl<'a, T: Integer> Pow<&'a T> for Sign {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd have expected the entire Sign trait private by the way, but there's probably some good reason for it.

src/biguint.rs Outdated
// square and multiply from num_traits::pow in the next branch
let mut base = self.clone();

if exp == 0 { return BigUint::one(); }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point.

src/biguint.rs Outdated

#[inline]
fn pow(self, mut exp: $unsigned) -> Self::Output {
// square and multiply from num_traits::pow in the next branch
Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

@@ -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.

@cuviper
Copy link
Member

cuviper commented Aug 4, 2018

Thanks!

bors r+

bors bot added a commit that referenced this pull request Aug 4, 2018
54: Implement Pow for BigUint and BigInt r=cuviper a=thomwiggers

Implements #29

Co-authored-by: Thom Wiggers <thom@thomwiggers.nl>
@bors
Copy link
Contributor

bors bot commented Aug 4, 2018

Build succeeded

@bors bors bot merged commit 03e01cb into rust-num:master Aug 4, 2018
@thomwiggers
Copy link
Contributor Author

thomwiggers commented Aug 4, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants