-
Notifications
You must be signed in to change notification settings - Fork 188
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
Conversation
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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
.
There was a problem hiding this 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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this 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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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...)
There was a problem hiding this comment.
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(); } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Private fn ✔️
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
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>
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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(); } |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
See also #56
It seems to break R1.15
@@ -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 { |
There was a problem hiding this comment.
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.
Thanks! bors r+ |
Build succeeded |
Yeah, that's something I've run into as well. I probably meant to comment that but forgot :p
With some luck will the compiler take care of the overhead of the ref where it could've been by value.
Josh Stone <notifications@github.com> schreef op August 4, 2018 12:12:04 AM UTC:
…cuviper commented on this pull request.
> @@ -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 {
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.
--
You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub:
#54 (review)
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
|
Implements #29