-
Notifications
You must be signed in to change notification settings - Fork 59
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 factor work for Int128, UInt128, BigInt #1951
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1951 +/- ##
=======================================
Coverage 87.92% 87.92%
=======================================
Files 99 99
Lines 36404 36416 +12
=======================================
+ Hits 32007 32018 +11
- Misses 4397 4398 +1 ☔ View full report in Codecov by Sentry. |
# trivial case: input is 1 | ||
F1 = factor(1) | ||
F1_ZZ = factor(ZZ(1)) | ||
@test length(F1.fac) == 0 |
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.
@test length(F1.fac) == 0 | |
@test length(F1) == 0 |
this should work
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.
OK, thanks for the suggested clean up. I have just checked in the relevant changes.
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.
@JohnAAbbott Maybe you checked them in but did not push them? They don't appear to be in the merged code. Perhaps send a follow-up PR to clean that up?
for i in 1:F.num | ||
z = F.p[i] | ||
res[z] = F.exp[i] | ||
end | ||
return Fac(u, res) | ||
end | ||
|
||
# This is supposed to be called only for T in [Int128, UInt128, BigInt] |
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 really understand this comment. The code clearly can also be called for e.g. T
equal to Int16
. So who is supposed to "only" call it for the listed types? What happens if I call it with another Julia Integer
type? Is there a problem (then we should address it)? Or is actually all fine (then remove this comment?)
function factor(a::T) where T <: Integer | ||
iszero(a) && throw(ArgumentError("Argument must be non-zero")) | ||
u = sign(a) | ||
F = factor(ZZ(abs(a))) |
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.
Another thing you could try in a follow-up PR: if T
is an Integer type that is "smaller" than Int
or UInt
, this might be more efficient:
F = factor(ZZ(abs(a))) | |
F = factor(flintify(abs(a))) |
Check out the docstring for Nemo.flintify
to get an explanation.
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 hint. Note that flintify
seems always to give a ZZRingElem
when the supplied value is Unsigned
; is this intentional?
I think some @JohnAAbbott can you open a new PR to fix this? The test should test the interface and not internals. |
throw(DomainError("Cannot factorize a boolean")); | ||
end | ||
|
||
# This function handles machine integer types (up to 64 bits) | ||
function factor(a::T) where T <: Union{Int, UInt} | ||
iszero(a) && throw(ArgumentError("Argument must be non-zero")) | ||
u = sign(a) | ||
a = u < 0 ? -a : a |
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 code is actually incorrect if a == typemin(Int)
, as then -a == a
and we get an error casting that negative value to UInt
.
But instead of fixing this locally, I suggest a slightly bigger change which then also allows avoiding creating Dictionaries twice:
Change this function to look like roughly this (might contain syntax errors):
function factor(::Type{T}, a::UInt) where T <: Integer
iszero(a) && throw(ArgumentError("Argument must be non-zero"))
F = n_factor()
# ... now continue as before
end
factor(a::UInt) = factor(UInt, a)
I.e. the caller can specify T
.
Then continue:
# another base cased, needed because `flintify` may produce `Int`
function factor(::Type{T}, a::Int) where T <: Integer
u = reinterpret(UInt, a < 0 ? -a : a)
return factor(T, u)
end
function factor(a::T) where T <: Integer
return factor(T, flintify(a))
end
function factor(::Type{T}, N::ZZRingElem) where T <: Integer
F = factor(N)
res = Dict{T, Int}() # factor-multiplicity pairs
for (fac,exp) in F.fac
res[T(fac)] = exp
end
return Fac(u, res)
end
Of course the final function is also not optimal, better would be to modify function factor(N::ZZRingElem)
to take T
as argument and use it, but that could be done later (in any case this is no worse than what your code right now).
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 decided not to alter factor(::ZZRingElem)
whose implementation is "special" (and possibly fragile?).
No description provided.