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

Make it easier to extend lcm/gcd/gcdx to custom types #34129

Merged
merged 2 commits into from
Jan 20, 2020

Conversation

sostock
Copy link
Contributor

@sostock sostock commented Dec 17, 2019

As discussed in #33910 (comment), this changes the implementation of lcm, gcd, and gcdx to use promotion for all Real arguments, thus making it easier to extend it to custom Real types.

@KlausC
Copy link
Contributor

KlausC commented Dec 18, 2019

The failure is unrelated to the changes in this PR.

@sostock
Copy link
Contributor Author

sostock commented Dec 19, 2019

Can this still be in 1.4?

@sostock
Copy link
Contributor Author

sostock commented Jan 2, 2020

I guess this got overlooked because there is no issue for it, only the comments on the older PR.

@StefanKarpinski I would really like to see this in 1.4. Can it be backported? I don’t see it as a feature, but rather a small correction of #33910, so I would argue it can be included in 1.4 regardless of the feature freeze.

@sostock sostock mentioned this pull request Jan 8, 2020
28 tasks
Copy link
Sponsor Member

@KristofferC KristofferC left a comment

Choose a reason for hiding this comment

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

Is it possible to add a test for what this new way of writing things enables?

lcm(a::Real, b::Real) = lcm(promote(a,b)...)
gcd(a::Real, b::Real...) = gcd(a, gcd(b...))
lcm(a::Real, b::Real...) = lcm(a, lcm(b...))
gcd(a::T, b::T) where T<:Real = throw(MethodError(gcd, (a,b)))
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Manually throwing method errors tend to often be a code smell from my experience. What is the reason for it? Is it to avoid an infinite recursion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the reason is to avoid infinite recursion, since some Real types don’t implement gcd/lcm/gcdx (AbstractFloats, for example).

@sostock
Copy link
Contributor Author

sostock commented Jan 16, 2020

Is it possible to add a test for what this new way of writing things enables?

Yes, I just added tests for the promotion of types that are not <:Union{Integer,Rational}.

base/intfuncs.jl Outdated
lcm(a::Union{Integer,Rational}, b::Union{Integer,Rational}...) = lcm(a, lcm(b...))
gcd(a::Real, b::Real) = gcd(promote(a,b)...)
lcm(a::Real, b::Real) = lcm(promote(a,b)...)
gcd(a::Real, b::Real...) = gcd(a, gcd(b...))
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

This should probably be gcd(a::Real, b::Real, c::Real...) to avoid trying to call gcd() --- it would be an error either way but clearer to get a method error for gcd(x::T) where the implementation for T is missing.

Copy link
Contributor Author

@sostock sostock Jan 17, 2020

Choose a reason for hiding this comment

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

I agree, will change it.

Edit: I changed it (a15c0bb).

@JeffBezanson
Copy link
Sponsor Member

👍 Ok by me and I'm ok with backporting it as well.

@sostock
Copy link
Contributor Author

sostock commented Jan 17, 2020

I just noticed that the gcd method for arrays is only correct for integers, because the method returns immediately when the gcd becomes 1:

julia> gcd(5, 2, 1//2)
1//2

julia> gcd([5, 2, 1//2]) # incorrectly returns 1, because gcd(5, 2) == 1
1//1

The wrong behavior was introduced in #33910 (by widening the argument type of the method) and is now also fixed as part of this PR (d46752e). I have added a test for it as well.

@StefanKarpinski
Copy link
Sponsor Member

Would it be possible to separate the bug fix in one commit and the API change in another? As it is, some of these commits need to be squashed, but it would be better to have those changes separate if possible.

@sostock
Copy link
Contributor Author

sostock commented Jan 17, 2020

Would it be possible to separate the bug fix in one commit and the API change in another? As it is, some of these commits need to be squashed, but it would be better to have those changes separate if possible.

I have added a PR that includes only the bugfix: #34417.

What would be the best way to realize the API-change PR? Should I rebase this PR on top of #34417?

Now that I typed this, I realized that you only mentioned two different commits, not different PRs. Should I make a PR that includes two commits, first the bugfix and then the API change?

@StefanKarpinski
Copy link
Sponsor Member

Up to you: you can make a PR that builds on that one or just add a second API commit in that PR.

@sostock
Copy link
Contributor Author

sostock commented Jan 17, 2020

Up to you: you can make a PR that builds on that one or just add a second API commit in that PR.

This PR now builds on #34417 and implements the API change on top of it.

@KristofferC KristofferC merged commit bcd5882 into JuliaLang:master Jan 20, 2020
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.

5 participants