-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Implements a pow method for Integers #11503
Conversation
Well, this definitely needs to use https://en.wikipedia.org/wiki/Exponentiation_by_squaring instead of the linear exponentiation it uses in the commit. Also, the algorithm is the same for any type that implements multiplication, so maybe it should be a default method in a trait inheriting from Mul<T, T> or a global function. Then, the exponent should at least be uint instead of int (since exponentiation by a negative number is not defined for integers), and it probably should either be generic or also have a BigUint version. Finally, maybe there should be support for negative exponentiation for Ratio. |
#[inline] | ||
fn pow(&self, other: int) -> BigUint { | ||
let m1 = (*self).clone(); | ||
let m2 = (*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.
Is this clone
even necessary?
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.
You mean m2
or both of them?
Self cannot be de-referenced and BigUint is non-copiable, hence the 2 clones. Is there a better way to do this?
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.
Yes, at the very least range(1, other).fold(m1, |acc, _| acc * *self)
to avoid cloning to m2
, or even range(0, other).fold(One::one(), |acc, _| acc * *self)
to avoid both clones. (They should be implemented without clones when you convert it to exponentiation by squaring.)
@bill-myers thanks a lot for your comment. I'll apply your suggestions. |
@huonw r? |
Why isn't it possible to provide a single implementation for Mul<T, T> for any T instead of 4 copy&pasted implementations? Also, it's better to use a loop instead of recursion (especially because that recursive version is not even tail recursive). This should be an optimal implementation that avoids any unnecessary clone and any unnecessary instantiation of Zero or One (it's pseudocode):
|
BTW, that's still horribly inefficient for general exponents. The correct solution is not really to shift right, but rather to use a generic indexed "find next set bit" operation to iterate over set bits (which requires defining such a trait). |
@bill-myers thanks again for your comments. I should've definitely thought about the recursion issues. Let me update the patch with something along the lines of your pseudo-code. |
fn pow<U: Eq+Zero+One+BitAnd<U, U>+Shr<U, U>>(&self, exp: U) -> Self { | ||
let one: U = One::one(); | ||
|
||
if exp.is_zero() { return One::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.
Can't this just return 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.
oppps, yes!
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.
Actually I'm wrong: the return type is Self
but one
is U
.
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.
yeah, noticed it right after my comment. :)
It looks better now with squaring, but... the implementation seems rather complicated, I'll play around with it a bit later. |
fn square(&self) -> Self {self * *self} | ||
|
||
/** | ||
* Elevates a type implementing `Mul` to the |
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.
We now normally write these doc comments with ///
rather than /** */
.
I'm pretty sure it can be implemented with a single loop. I'll give it another go. |
@huonw r? |
let mut v: Self; | ||
let mut r: Self = self_one; | ||
|
||
if((i & one) == 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.
parens
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.
Also, what is this if
for? It's to avoid cloning self
, right? It'd be good to have a comment saying this.
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.
damn! Changed! Thnx
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.
Correct, it's to avoid cloning self.
@huonw r? |
#[inline] | ||
fn square(&self) -> Self {self * *self} | ||
|
||
/// |
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.
Normally we don't have these first and last ones.
As I said on IRC, I don't like adding a new trait just for method-call-syntax, and I especially don't like adding such a trait that the user has to actually implement themselves. If we are going to keep the trait, it would better to have it also have impl<T: One + Clone + Mul<T, T>> Power for T {} inside libstd. |
I understand your concern. However, the @brson @alexcrichton @bill-myers thoughts? |
Advantages of the trait: method call syntax. Disadvantages:
|
Those are all good points. Thanks! TBH, I'm ok with both, I do prefer the call syntax of the current implementation. IMHO, we could also make it part |
I don't think |
(Accidental close, sorry.) |
Right, we actually talked about that :D Ok, now I'm convinced too. I'll change the implementation to a standalone function. Thanks |
@huonw r? |
/// Raises a value to the power of exp, using | ||
/// exponentiation by squaring. | ||
/// | ||
/// It could be optimized by using indexed access to |
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 don't think this comment is relevant when exp: uint
, and it would be good to have an example.
@huonw r? |
@huonw r? |
@huonw r? :) |
The patch adds a `pow` function for types implementing `One`, `Mul` and `Clone` trait. The patch also renames f32 and f64 pow into powf in order to still have a way to easily have float powers. It uses llvms intrinsics. The pow implementation for all num types uses the exponentiation by square. Fixes bug rust-lang#11499
fix filter_map_bool_then with a bool reference changelog: [`filter_map_bool_then`]: Fix the incorrect autofix when the `bool` in question is a reference. fix rust-lang#11503
…=Jarcho [`filter_map_bool_then`]: include multiple derefs from adjustments In rust-lang#11506 this lint was improved to suggest one deref if the bool is behind references (fixed the FP rust-lang#11503), however it might need multiple dereferences if the bool is behind multiple layers of references or custom derefs. E.g. `&&&bool` needs `***b`. changelog: [`filter_map_bool_then`]: suggest as many dereferences as there are needed to get to the bool
The patch adds the missing pow method for all the implementations of the
Integer trait. This is a small addition that will most likely be
improved by the work happening in #10387.
Fixes #11499