From 0e7b324bfd801c42df954e688a6864e83879d86f Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 10:30:06 +0200 Subject: [PATCH 01/12] Move `Float64(::QQFieldElem)` --- src/HeckeMiscInteger.jl | 5 ----- src/flint/fmpq.jl | 2 ++ test/flint/fmpq-test.jl | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl index fcc7ea2a9..c93b1d779 100644 --- a/src/HeckeMiscInteger.jl +++ b/src/HeckeMiscInteger.jl @@ -1,8 +1,3 @@ -function Float64(a::QQFieldElem) - b = a * ZZRingElem(2)^53 - Float64(div(numerator(b), denominator(b))) / (Float64(2)^53) #CF 2^53 is bad in 32bit -end - function euler_phi(x::Fac{ZZRingElem}) return prod((p - 1) * p^(v - 1) for (p, v) = x.fac) end diff --git a/src/flint/fmpq.jl b/src/flint/fmpq.jl index 83a8961b3..dc0089a4c 100644 --- a/src/flint/fmpq.jl +++ b/src/flint/fmpq.jl @@ -1341,6 +1341,8 @@ function BigFloat(a::QQFieldElem) return r end +Float64(a::QQFieldElem) = Float64(BigFloat(a)) + ############################################################################### # # Convenience methods for arithmetics (since `QQFieldElem` and `ZZRingElem` are not `Number` types) diff --git a/test/flint/fmpq-test.jl b/test/flint/fmpq-test.jl index 834a754d9..5555efc26 100644 --- a/test/flint/fmpq-test.jl +++ b/test/flint/fmpq-test.jl @@ -126,6 +126,7 @@ end @test_throws Exception ZZ(big(3)//2) @test BigFloat(QQFieldElem(3, 4)) == BigFloat(0.75) + @test Float64(QQFieldElem(3, 4)) == 0.75 end @testset "QQFieldElem.vector_arithmetics" begin From a333241728bfd3f98840f5f50b90c1b5daf39ab1 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 10:32:47 +0200 Subject: [PATCH 02/12] Move `euler_phi(::Fac{ZZRingElem})` --- src/HeckeMiscInteger.jl | 4 ---- src/flint/fmpz.jl | 4 ++++ test/flint/fmpz-test.jl | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl index c93b1d779..8b8ebc109 100644 --- a/src/HeckeMiscInteger.jl +++ b/src/HeckeMiscInteger.jl @@ -1,7 +1,3 @@ -function euler_phi(x::Fac{ZZRingElem}) - return prod((p - 1) * p^(v - 1) for (p, v) = x.fac) -end - Integer(a::ZZRingElem) = BigInt(a) ^(a::T, n::IntegerUnion) where {T<:RingElem} = _generic_power(a, n) diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index 031784c68..a4874413f 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -2242,6 +2242,10 @@ end euler_phi(x::Int) = Int(euler_phi(ZZRingElem(x))) +function euler_phi(x::Fac{ZZRingElem}) + return prod((p - 1) * p^(v - 1) for (p, v) in x) +end + @doc raw""" number_of_partitions(x::Int) number_of_partitions(x::ZZRingElem) diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index 44fe32b9e..bc8be97f8 100644 --- a/test/flint/fmpz-test.jl +++ b/test/flint/fmpz-test.jl @@ -1108,6 +1108,7 @@ end @test_throws DomainError divisor_sigma(ZZRingElem(1), -1) @test euler_phi(ZZRingElem(12480)) == 3072 + @test euler_phi(factor(ZZ(12480))) == 3072 @test fibonacci(2) == 1 From 2be3ce70a9a41fd74be1c1da4388f237e28df846 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 10:35:40 +0200 Subject: [PATCH 03/12] Move `Integer(::ZZRingElem)` --- src/HeckeMiscInteger.jl | 2 -- src/flint/fmpz.jl | 2 ++ test/flint/fmpz-test.jl | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl index 8b8ebc109..8b95b50f5 100644 --- a/src/HeckeMiscInteger.jl +++ b/src/HeckeMiscInteger.jl @@ -1,5 +1,3 @@ -Integer(a::ZZRingElem) = BigInt(a) - ^(a::T, n::IntegerUnion) where {T<:RingElem} = _generic_power(a, n) function _generic_power(a, n::IntegerUnion) diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index a4874413f..977371fb2 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -3048,6 +3048,8 @@ end (::Type{Int128})(a::ZZRingElem) = Int128(BigInt(a)) +Integer(a::ZZRingElem) = BigInt(a) + convert(::Type{T}, a::ZZRingElem) where T <: Integer = T(a) function (::Type{Float64})(n::ZZRingElem) diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index bc8be97f8..5df434fc2 100644 --- a/test/flint/fmpz-test.jl +++ b/test/flint/fmpz-test.jl @@ -124,6 +124,10 @@ end @test x == -123 end + x = @inferred Integer(a) + @test x isa BigInt + @test x == -123 + @testset "ZZRingElem.convert for $T" for T in [UInt8, UInt16, UInt32, UInt] x = @inferred T(b) @test x isa T From bc8a7ec5cea26efdb15df019f95243742004d12d Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 10:56:41 +0200 Subject: [PATCH 04/12] Move `^(::RingElement, ::IntegerUnion)` --- src/HeckeMiscInteger.jl | 18 ------------------ src/flint/fmpz.jl | 24 ++++++++++++++++++++++++ test/flint/fmpq_poly-test.jl | 1 + 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl index 8b95b50f5..db5351ea0 100644 --- a/src/HeckeMiscInteger.jl +++ b/src/HeckeMiscInteger.jl @@ -1,21 +1,3 @@ -^(a::T, n::IntegerUnion) where {T<:RingElem} = _generic_power(a, n) - -function _generic_power(a, n::IntegerUnion) - fits(Int, n) && return a^Int(n) - if is_negative(n) - a = inv(a) - n = -n - end - r = one(parent(a)) - for b = bits(n) - r = mul!(r, r, r) - if b - r = mul!(r, r, a) - end - end - return r -end - ################################################################################ # # Modular reduction with symmetric residue system diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index 977371fb2..aaaa96eb6 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -826,6 +826,30 @@ function ^(x::ZZRingElem, y::Union{Int, UInt, ZZRingElem}) end end +############################################################################### +# +# Generic powering by an ZZRingElem +# +############################################################################### + +^(a::T, n::IntegerUnion) where {T<:RingElem} = _generic_power(a, n) + +function _generic_power(a, n::IntegerUnion) + fits(Int, n) && return a^Int(n) + if is_negative(n) + a = inv(a) + n = -n + end + r = one(parent(a)) + for b = bits(n) + r = mul!(r, r, r) + if b + r = mul!(r, r, a) + end + end + return r +end + ############################################################################### # # Comparison diff --git a/test/flint/fmpq_poly-test.jl b/test/flint/fmpq_poly-test.jl index 0c9ac2f10..18285bdd6 100644 --- a/test/flint/fmpq_poly-test.jl +++ b/test/flint/fmpq_poly-test.jl @@ -329,6 +329,7 @@ end f = 3*y^2 + 7*y + 3 @test f^5 == 243*y^10 + 2835*y^9 + 14445*y^8 + 42210*y^7 + 78135*y^6 + 95557*y^5 + 78135*y^4 + 42210*y^3 + 14445*y^2 + 2835*y + 243 + @test f^ZZ(5) == 243*y^10 + 2835*y^9 + 14445*y^8 + 42210*y^7 + 78135*y^6 + 95557*y^5 + 78135*y^4 + 42210*y^3 + 14445*y^2 + 2835*y + 243 @test_throws DomainError f^(-1) end From 5dde5cec74b714a9ed6e9189bb606121b2fe263c Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 11:16:01 +0200 Subject: [PATCH 05/12] Move/put together `mod_sym` --- src/HeckeMiscInteger.jl | 12 ------------ src/HeckeMoreStuff.jl | 8 -------- src/flint/fmpz.jl | 14 +++++++++++++- test/flint/fmpz-test.jl | 11 +++++++++++ 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl index db5351ea0..ee7b1e5ac 100644 --- a/src/HeckeMiscInteger.jl +++ b/src/HeckeMiscInteger.jl @@ -4,18 +4,6 @@ # ################################################################################ -function mod_sym(a::ZZRingElem, b::ZZRingElem) - c = mod(a, b) - @assert c >= 0 - if b > 0 && 2 * c > b - return c - b - elseif b < 0 && 2 * c > -b - return c + b - else - return c - end -end - #TODO # need to be mapped onto proper Flint primitives # flints needs a proper interface to randomness - I think diff --git a/src/HeckeMoreStuff.jl b/src/HeckeMoreStuff.jl index 5dceb8ca9..654f84873 100644 --- a/src/HeckeMoreStuff.jl +++ b/src/HeckeMoreStuff.jl @@ -998,14 +998,6 @@ function mod_sym!(a::T, b::T) where {T} return mod!(a, b) end -function mod_sym!(a::ZZRingElem, b::ZZRingElem) - mod!(a, a, b) - if a > div(b, 2) - sub!(a, a, b) - end - return a -end - Base.replace!(::typeof(-), m::ZZMatrix) = -m function (A::AbsSimpleNumField)(a::ZZPolyRingElem) diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index aaaa96eb6..d7be6018f 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -999,12 +999,24 @@ function mod(x::ZZRingElem, y::ZZRingElem) return mod!(r, x, y) end - function mod(x::ZZRingElem, c::UInt) c == 0 && throw(DivideError()) ccall((:fmpz_fdiv_ui, libflint), UInt, (Ref{ZZRingElem}, UInt), x, c) end +function mod_sym(a::ZZRingElem, b::ZZRingElem) + return mod_sym!(deepcopy(a), b) +end + +function mod_sym!(a::ZZRingElem, b::ZZRingElem) + mod!(a, a, b) + if (b > 0 && a > div(b, 2)) || (b < 0 && a < div(b, 2)) + sub!(a, a, b) + end + return a +end + + @doc raw""" powermod(x::ZZRingElem, p::ZZRingElem, m::ZZRingElem) diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index 5df434fc2..97f883754 100644 --- a/test/flint/fmpz-test.jl +++ b/test/flint/fmpz-test.jl @@ -897,6 +897,17 @@ end @test_throws DomainError sqrtmod(ZZRingElem(12), ZZRingElem(-13)) @test_throws ErrorException sqrtmod(ZZRingElem(-7), ZZRingElem(1024)) + + @test mod_sym(ZZ(0), ZZ(5)) == ZZ(0) + @test mod_sym(ZZ(0), ZZ(-5)) == ZZ(0) + @test mod_sym(ZZ(1), ZZ(5)) == ZZ(1) + @test mod_sym(ZZ(1), ZZ(-5)) == ZZ(1) + @test mod_sym(ZZ(-1), ZZ(5)) == ZZ(-1) + @test mod_sym(ZZ(-1), ZZ(-5)) == ZZ(-1) + @test mod_sym(ZZ(4), ZZ(5)) == ZZ(-1) + @test mod_sym(ZZ(4), ZZ(-5)) == ZZ(-1) + @test mod_sym(ZZ(-4), ZZ(5)) == ZZ(1) + @test mod_sym(ZZ(-4), ZZ(-5)) == ZZ(1) end @testset "ZZRingElem.crt" begin From 491b8a5505a93bb86e6e178323eaeeea59ae16ef Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 11:35:14 +0200 Subject: [PATCH 06/12] Move `rand` for integer ranges --- src/HeckeMiscInteger.jl | 68 ----------------------------------------- src/flint/fmpz.jl | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 68 deletions(-) diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl index ee7b1e5ac..ad4c78f6d 100644 --- a/src/HeckeMiscInteger.jl +++ b/src/HeckeMiscInteger.jl @@ -1,71 +1,3 @@ -################################################################################ -# -# Modular reduction with symmetric residue system -# -################################################################################ - -#TODO -# need to be mapped onto proper Flint primitives -# flints needs a proper interface to randomness - I think -# currently one simply cannot use it at all -# -# should be tied(?) to the Julia rng stuff? -# similarly, all the derived rand functions should probably also do this -# -# inspired by/copied from a former BigInt implementation from the stdlib in -# `Random/src/generation.jl` -# - -function rand(rng::AbstractRNG, a::ZZRingElemUnitRange) - m = Base.last(a) - Base.first(a) - m < 0 && error("range empty") - nd = ndigits(m, 2) - nl, high = Base.divrem(nd, 8 * sizeof(Base.GMP.Limb)) - if high > 0 - mask = m >> (nl * 8 * sizeof(Base.GMP.Limb)) - end - s = ZZRingElem(0) - c = (8 * sizeof(Base.GMP.Limb)) - while true - s = ZZRingElem(0) - for i = 1:nl - ccall((:fmpz_mul_2exp, libflint), Nothing, - (Ref{ZZRingElem}, Ref{ZZRingElem}, Int), s, s, c) - add!(s, s, rand(rng, Base.GMP.Limb)) - end - if high > 0 - s = s << high - s += rand(rng, 0:Base.GMP.Limb(mask)) - end - if s <= m - break - end - end - return s + first(a) -end - -struct RangeGeneratorfmpz# <: Base.Random.RangeGenerator - a::StepRange{ZZRingElem,ZZRingElem} -end - -function Random.RangeGenerator(r::StepRange{ZZRingElem,ZZRingElem}) - m = last(r) - first(r) - m < 0 && throw(ArgumentError("range must be non-empty")) - return RangeGeneratorfmpz(r) -end - -function rand(rng::AbstractRNG, g::RangeGeneratorfmpz) - return rand(rng, g.a) -end - -function rand!(A::Vector{ZZRingElem}, v::StepRange{ZZRingElem,ZZRingElem}) - for i in 1:length(A) - A[i] = rand(v) - end - return A -end - - function bits end module BitsMod diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index d7be6018f..cf8045b33 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -3030,6 +3030,66 @@ function rand(rng::AbstractRNG, sp::SamplerFmpz) add!(z, z, sp.a) end +#TODO +# need to be mapped onto proper Flint primitives +# flints needs a proper interface to randomness - I think +# currently one simply cannot use it at all +# +# should be tied(?) to the Julia rng stuff? +# similarly, all the derived rand functions should probably also do this +# +# inspired by/copied from a former BigInt implementation from the stdlib in +# `Random/src/generation.jl` +# + +function rand(rng::AbstractRNG, a::ZZRingElemUnitRange) + m = Base.last(a) - Base.first(a) + m < 0 && error("range empty") + nd = ndigits(m, 2) + nl, high = Base.divrem(nd, 8 * sizeof(Base.GMP.Limb)) + if high > 0 + mask = m >> (nl * 8 * sizeof(Base.GMP.Limb)) + end + s = ZZRingElem(0) + c = (8 * sizeof(Base.GMP.Limb)) + while true + s = ZZRingElem(0) + for i = 1:nl + ccall((:fmpz_mul_2exp, libflint), Nothing, + (Ref{ZZRingElem}, Ref{ZZRingElem}, Int), s, s, c) + add!(s, s, rand(rng, Base.GMP.Limb)) + end + if high > 0 + s = s << high + s += rand(rng, 0:Base.GMP.Limb(mask)) + end + if s <= m + break + end + end + return s + first(a) +end + +struct RangeGeneratorfmpz# <: Base.Random.RangeGenerator + a::StepRange{ZZRingElem,ZZRingElem} +end + +function Random.RangeGenerator(r::StepRange{ZZRingElem,ZZRingElem}) + m = last(r) - first(r) + m < 0 && throw(ArgumentError("range must be non-empty")) + return RangeGeneratorfmpz(r) +end + +function rand(rng::AbstractRNG, g::RangeGeneratorfmpz) + return rand(rng, g.a) +end + +function rand!(A::Vector{ZZRingElem}, v::StepRange{ZZRingElem,ZZRingElem}) + for i in 1:length(A) + A[i] = rand(v) + end + return A +end ############################################################################### # From edea7684f5f8b8ed6f4a6de49c1fec0da50bbca8 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 11:37:04 +0200 Subject: [PATCH 07/12] Move `bits` iterator --- src/HeckeMiscInteger.jl | 146 ---------------------------------------- src/flint/fmpz.jl | 130 +++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 146 deletions(-) delete mode 100644 src/HeckeMiscInteger.jl diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl deleted file mode 100644 index ad4c78f6d..000000000 --- a/src/HeckeMiscInteger.jl +++ /dev/null @@ -1,146 +0,0 @@ -function bits end - -module BitsMod - -using ..Nemo - -import Base: ^ -import Base: getindex -import Base: iterate -import Base: length -import Base: show - -export bits -export Limbs - - -const hb = UInt(1) << 63 - -#= not used - lacks length -struct BitsUInt -a::UInt -end - -function bits(a::UInt) -l = nbits(a) -return BitsUInt(a<<(sizeof(a)*8-l)) -end - - -function Base.iterate(x::BitsUInt) -return iterate(x, x.a) -end - -@inline function Base.iterate(x::BitsUInt, u::UInt) -iszero(u) && return nothing -return (u&hb) != 0, u<<1 -end -=# - -struct Limbs - a::ZZRingElem - len::Int - b::Ptr{UInt} - function Limbs(a::ZZRingElem; MSW::Bool=true) - if Nemo._fmpz_is_small(a) - return new(a, 0, convert(Ptr{UInt}, 0)) - end - z = convert(Ptr{Cint}, unsigned(a.d) << 2) - len = unsafe_load(z, 2) - d = convert(Ptr{Ptr{UInt}}, unsigned(a.d) << 2) + 2 * sizeof(Cint) - p = unsafe_load(d) - if !MSW - new(a, -len, p) - else - new(a, len, p) - end - end -end - -function show(io::IO, L::Limbs) - print(io, "limb-access for: ", L.a) -end - -@inline function getindex(L::Limbs, i::Int) - if L.len == 0 - return UInt(abs(L.a.d)) #error??? - end - @boundscheck @assert i <= abs(L.len) - return unsafe_load(L.b, i) -end - -function iterate(L::Limbs) - L.len < 0 && return L[1], 1 - - return L[L.len], L.len -end - -function iterate(L::Limbs, i::Int) - if L.len < 0 - i > -L.len && return nothing - return L[i+1], i + 1 - end - i == 0 && return nothing - return L[i-1], i - 1 -end - -length(L::Limbs) = L.len + 1 - -#= -#from https://github.com/JuliaLang/julia/issues/11592 -#compiles directly down to the ror/rol in assembly -for T in Base.BitInteger_types -mask = UInt8(sizeof(T) << 3 - 1) -@eval begin -ror(x::$T, k::Integer) = (x >>> ($mask & k)) | (x << ($mask & -k)) -rol(x::$T, k::Integer) = (x << ($mask & k)) | (x >>> ($mask & -k)) -end -end -=# - -struct BitsFmpz - L::Limbs - - function BitsFmpz(b::ZZRingElem) - return new(Limbs(b)) - end -end - -function iterate(B::BitsFmpz) - L = B.L - a = L[L.len] - b = UInt(1) << (nbits(a) - 1) - return true, (b, L.len) -end - -@inline function iterate(B::BitsFmpz, s::Tuple{UInt,Int}) - b = s[1] >> 1 - if b == 0 - l = s[2] - 1 - if l < 1 - return nothing - end - b = hb - a = B.L[l] - return a & b != 0, (b, l) - end - return B.L[s[2]] & b != 0, (b, s[2]) -end - -function show(io::IO, B::BitsFmpz) - print(io, "bit iterator for:", B.L.a) -end - -length(B::BitsFmpz) = nbits(B.L.a) - -Nemo.bits(a::ZZRingElem) = BitsFmpz(a) -#= wrong order, thus disabled - -function getindex(B::BitsFmpz, i::Int) -return ccall((:fmpz_tstbit, libflint), Int, (Ref{ZZRingElem}, Int), B.L.a, i) != 0 -end -=# - -end - -using .BitsMod diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index cf8045b33..b73394d0d 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -3297,3 +3297,133 @@ end //(v::Vector{ZZRingElem}, x::ZZRingElem) = v .// x *(x::ZZRingElem, v::Vector{ZZRingElem}) = x .* v *(v::Vector{ZZRingElem}, x::ZZRingElem) = v .* x + +############################################################################### +# +# Bit iteration +# +############################################################################### + +function bits end + +module BitsMod + +using ..Nemo + +import Base: ^ +import Base: getindex +import Base: iterate +import Base: length +import Base: show + +export bits +export Limbs + +const hb = UInt(1) << 63 + +struct Limbs + a::ZZRingElem + len::Int + b::Ptr{UInt} + function Limbs(a::ZZRingElem; MSW::Bool=true) + if Nemo._fmpz_is_small(a) + return new(a, 0, convert(Ptr{UInt}, 0)) + end + z = convert(Ptr{Cint}, unsigned(a.d) << 2) + len = unsafe_load(z, 2) + d = convert(Ptr{Ptr{UInt}}, unsigned(a.d) << 2) + 2 * sizeof(Cint) + p = unsafe_load(d) + if !MSW + new(a, -len, p) + else + new(a, len, p) + end + end +end + +function show(io::IO, L::Limbs) + print(io, "limb-access for: ", L.a) +end + +@inline function getindex(L::Limbs, i::Int) + if L.len == 0 + return UInt(abs(L.a.d)) #error??? + end + @boundscheck @assert i <= abs(L.len) + return unsafe_load(L.b, i) +end + +function iterate(L::Limbs) + L.len < 0 && return L[1], 1 + + return L[L.len], L.len +end + +function iterate(L::Limbs, i::Int) + if L.len < 0 + i > -L.len && return nothing + return L[i+1], i + 1 + end + i == 0 && return nothing + return L[i-1], i - 1 +end + +length(L::Limbs) = L.len + 1 + +#= +#from https://github.com/JuliaLang/julia/issues/11592 +#compiles directly down to the ror/rol in assembly +for T in Base.BitInteger_types +mask = UInt8(sizeof(T) << 3 - 1) +@eval begin +ror(x::$T, k::Integer) = (x >>> ($mask & k)) | (x << ($mask & -k)) +rol(x::$T, k::Integer) = (x << ($mask & k)) | (x >>> ($mask & -k)) +end +end +=# + +struct BitsFmpz + L::Limbs + + function BitsFmpz(b::ZZRingElem) + return new(Limbs(b)) + end +end + +function iterate(B::BitsFmpz) + L = B.L + a = L[L.len] + b = UInt(1) << (nbits(a) - 1) + return true, (b, L.len) +end + +@inline function iterate(B::BitsFmpz, s::Tuple{UInt,Int}) + b = s[1] >> 1 + if b == 0 + l = s[2] - 1 + if l < 1 + return nothing + end + b = hb + a = B.L[l] + return a & b != 0, (b, l) + end + return B.L[s[2]] & b != 0, (b, s[2]) +end + +function show(io::IO, B::BitsFmpz) + print(io, "bit iterator for:", B.L.a) +end + +length(B::BitsFmpz) = nbits(B.L.a) + +Nemo.bits(a::ZZRingElem) = BitsFmpz(a) +#= wrong order, thus disabled + +function getindex(B::BitsFmpz, i::Int) +return ccall((:fmpz_tstbit, libflint), Int, (Ref{ZZRingElem}, Int), B.L.a, i) != 0 +end +=# +end + +using .BitsMod From 82042ed65adf755b3d9476791d4e0397fad170ba Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 14:45:25 +0200 Subject: [PATCH 08/12] Move test around --- src/Nemo.jl | 1 - test/HeckeMiscInteger-test.jl | 60 ----------------------------------- test/Nemo-test.jl | 1 - test/flint/fmpz-test.jl | 58 +++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 62 deletions(-) delete mode 100644 test/HeckeMiscInteger-test.jl diff --git a/src/Nemo.jl b/src/Nemo.jl index b7593586c..70cc81226 100644 --- a/src/Nemo.jl +++ b/src/Nemo.jl @@ -456,7 +456,6 @@ include("matrix.jl") include("Infinity.jl") include("HeckeMiscFiniteField.jl") -include("HeckeMiscInteger.jl") include("HeckeMiscMatrix.jl") include("HeckeMiscPoly.jl") include("HeckeMoreStuff.jl") diff --git a/test/HeckeMiscInteger-test.jl b/test/HeckeMiscInteger-test.jl deleted file mode 100644 index bc2d54d9a..000000000 --- a/test/HeckeMiscInteger-test.jl +++ /dev/null @@ -1,60 +0,0 @@ -@testset "Integer" begin - @testset "range" begin - - r04 = ZZRingElem(0):ZZRingElem(4) - r026 = ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) - zlarge = ZZRingElem(13)^100 - zjump = ZZRingElem(11)^100 - rlarge = -zlarge:zlarge - rlargejumps = -zlarge:zjump:zlarge - - @test ZZRingElem(0):4 == 0:ZZRingElem(4) == r04 - @test ZZRingElem(0):2:6 == 0:2:ZZRingElem(6) == 0:ZZRingElem(2):6 == r026 - @test typeof(ZZRingElem(0):4) == typeof(0:ZZRingElem(4)) == typeof(r04) - @test typeof(ZZRingElem(0):ZZRingElem(2):6) == typeof(0:ZZRingElem(2):ZZRingElem(6)) == typeof(0:ZZRingElem(2):6) == typeof(r026) - - @test collect(r04) == ZZRingElem[0, 1, 2, 3, 4] - @test collect(r026) == ZZRingElem[0, 2, 4, 6] - @test collect(ZZRingElem(-2):ZZRingElem(5):ZZRingElem(21)) == ZZRingElem[-2, 3, 8, 13, 18] - @test collect(ZZRingElem(3):ZZRingElem(-2):ZZRingElem(-4)) == ZZRingElem[3, 1, -1, -3] - - @test length(r04) == 5 - @test length(r026) == 4 - @test length(ZZRingElem(-2):ZZRingElem(5):ZZRingElem(21)) == 5 - @test length(ZZRingElem(3):ZZRingElem(-2):ZZRingElem(-4)) == 4 - - # Julia assumes `length` to return an Integer, so if we want several base - # functions to work, that should better be the case. - @test length(r04) isa Integer - @test length(r026) isa Integer - @test length(rlarge) isa Integer - @test length(rlargejumps) isa Integer - - @test r04[4] == r04[end-1] == 3 - @test r026[3] == r026[end-1] == 4 - @test rlarge[2zlarge] == rlarge[end-1] == rlarge[end-ZZRingElem(1)] == zlarge - 1 - @test rlargejumps[2] == -zlarge + zjump - @test rlargejumps[end] == zlarge - 2zlarge % zjump - - @test range(ZZRingElem(0); step=2, stop=6) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) - @test range(0; step=ZZRingElem(2), stop=6) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) - @test range(ZZRingElem(0); step=2, length=4) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) - VERSION >= v"1.7" && @test range(; stop=ZZRingElem(0), length=3) == ZZRingElem(-2):ZZRingElem(0) - - @test 1 .+ r04 == ZZRingElem(1):ZZRingElem(5) - @test ZZRingElem(1) .+ r04 == ZZRingElem(1):ZZRingElem(5) - @test ZZRingElem(1) .+ (0:4) == ZZRingElem(1):ZZRingElem(5) - @test 2 .* r04 == ZZRingElem(0):ZZRingElem(2):ZZRingElem(8) - @test ZZRingElem(2) .* r04 == ZZRingElem(0):ZZRingElem(2):ZZRingElem(8) - @test 2 .* (0:4) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(8) - - @test mod(ZZRingElem(7), r04) == ZZRingElem(2) - @test mod(7, r04) == 2 - @test mod(7, ZZRingElem(10):ZZRingElem(14)) == 12 - - @test all(x -> x in r04, rand(r04, 5)) - @test all(x -> x in ZZRingElem(0):ZZRingElem(10):ZZRingElem(100), rand(ZZRingElem(0):ZZRingElem(10):ZZRingElem(100), 5)) - @test all(x -> x in rlarge, rand(rlarge, 20)) - @test all(x -> x in rlargejumps, rand(rlargejumps, 20)) - end -end diff --git a/test/Nemo-test.jl b/test/Nemo-test.jl index 4439ebb80..7de34fe48 100644 --- a/test/Nemo-test.jl +++ b/test/Nemo-test.jl @@ -5,6 +5,5 @@ include("Benchmark-test.jl") include("gaussiannumbers/continued_fraction-test.jl") include("Native-test.jl") include("Infinity-test.jl") -include("HeckeMiscInteger-test.jl") include("HeckeMiscLocalization-test.jl") include("matrix-test.jl") diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index 97f883754..66df7257f 100644 --- a/test/flint/fmpz-test.jl +++ b/test/flint/fmpz-test.jl @@ -1554,3 +1554,61 @@ end @test ZZ(-2):2:2 isa StepRange{ZZRingElem} @test BigInt(-2):2:ZZ(2) isa StepRange{ZZRingElem} end + +@testset "ZZRingElem.range" begin + r04 = ZZRingElem(0):ZZRingElem(4) + r026 = ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) + zlarge = ZZRingElem(13)^100 + zjump = ZZRingElem(11)^100 + rlarge = -zlarge:zlarge + rlargejumps = -zlarge:zjump:zlarge + + @test ZZRingElem(0):4 == 0:ZZRingElem(4) == r04 + @test ZZRingElem(0):2:6 == 0:2:ZZRingElem(6) == 0:ZZRingElem(2):6 == r026 + @test typeof(ZZRingElem(0):4) == typeof(0:ZZRingElem(4)) == typeof(r04) + @test typeof(ZZRingElem(0):ZZRingElem(2):6) == typeof(0:ZZRingElem(2):ZZRingElem(6)) == typeof(0:ZZRingElem(2):6) == typeof(r026) + + @test collect(r04) == ZZRingElem[0, 1, 2, 3, 4] + @test collect(r026) == ZZRingElem[0, 2, 4, 6] + @test collect(ZZRingElem(-2):ZZRingElem(5):ZZRingElem(21)) == ZZRingElem[-2, 3, 8, 13, 18] + @test collect(ZZRingElem(3):ZZRingElem(-2):ZZRingElem(-4)) == ZZRingElem[3, 1, -1, -3] + + @test length(r04) == 5 + @test length(r026) == 4 + @test length(ZZRingElem(-2):ZZRingElem(5):ZZRingElem(21)) == 5 + @test length(ZZRingElem(3):ZZRingElem(-2):ZZRingElem(-4)) == 4 + + # Julia assumes `length` to return an Integer, so if we want several base + # functions to work, that should better be the case. + @test length(r04) isa Integer + @test length(r026) isa Integer + @test length(rlarge) isa Integer + @test length(rlargejumps) isa Integer + + @test r04[4] == r04[end-1] == 3 + @test r026[3] == r026[end-1] == 4 + @test rlarge[2zlarge] == rlarge[end-1] == rlarge[end-ZZRingElem(1)] == zlarge - 1 + @test rlargejumps[2] == -zlarge + zjump + @test rlargejumps[end] == zlarge - 2zlarge % zjump + + @test range(ZZRingElem(0); step=2, stop=6) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) + @test range(0; step=ZZRingElem(2), stop=6) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) + @test range(ZZRingElem(0); step=2, length=4) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(6) + VERSION >= v"1.7" && @test range(; stop=ZZRingElem(0), length=3) == ZZRingElem(-2):ZZRingElem(0) + + @test 1 .+ r04 == ZZRingElem(1):ZZRingElem(5) + @test ZZRingElem(1) .+ r04 == ZZRingElem(1):ZZRingElem(5) + @test ZZRingElem(1) .+ (0:4) == ZZRingElem(1):ZZRingElem(5) + @test 2 .* r04 == ZZRingElem(0):ZZRingElem(2):ZZRingElem(8) + @test ZZRingElem(2) .* r04 == ZZRingElem(0):ZZRingElem(2):ZZRingElem(8) + @test 2 .* (0:4) == ZZRingElem(0):ZZRingElem(2):ZZRingElem(8) + + @test mod(ZZRingElem(7), r04) == ZZRingElem(2) + @test mod(7, r04) == 2 + @test mod(7, ZZRingElem(10):ZZRingElem(14)) == 12 + + @test all(x -> x in r04, rand(r04, 5)) + @test all(x -> x in ZZRingElem(0):ZZRingElem(10):ZZRingElem(100), rand(ZZRingElem(0):ZZRingElem(10):ZZRingElem(100), 5)) + @test all(x -> x in rlarge, rand(rlarge, 20)) + @test all(x -> x in rlargejumps, rand(rlargejumps, 20)) +end From 2f0517d5791eabfaa62b6901b86737628e32f025 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 15:42:52 +0200 Subject: [PATCH 09/12] Tests and fix for `bits(::ZZRingElem)` --- src/flint/fmpz.jl | 14 ++++++++++++-- test/flint/fmpz-test.jl | 7 +++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/flint/fmpz.jl b/src/flint/fmpz.jl index b73394d0d..63a9ce6c4 100644 --- a/src/flint/fmpz.jl +++ b/src/flint/fmpz.jl @@ -3311,6 +3311,7 @@ module BitsMod using ..Nemo import Base: ^ +import Base: eltype import Base: getindex import Base: iterate import Base: length @@ -3354,6 +3355,7 @@ end end function iterate(L::Limbs) + is_zero(L.a) && return nothing # nbits(ZZ()) == 0 L.len < 0 && return L[1], 1 return L[L.len], L.len @@ -3368,7 +3370,12 @@ function iterate(L::Limbs, i::Int) return L[i-1], i - 1 end -length(L::Limbs) = L.len + 1 +function length(L::Limbs) + is_zero(L.a) && return 0 # nbits(ZZ()) == 0 + return L.len + 1 +end + +eltype(L::Limbs) = UInt #= #from https://github.com/JuliaLang/julia/issues/11592 @@ -3392,6 +3399,7 @@ end function iterate(B::BitsFmpz) L = B.L + is_zero(L.a) && return nothing a = L[L.len] b = UInt(1) << (nbits(a) - 1) return true, (b, L.len) @@ -3412,11 +3420,13 @@ end end function show(io::IO, B::BitsFmpz) - print(io, "bit iterator for:", B.L.a) + print(io, "bit iterator for: ", B.L.a) end length(B::BitsFmpz) = nbits(B.L.a) +eltype(B::BitsFmpz) = Bool + Nemo.bits(a::ZZRingElem) = BitsFmpz(a) #= wrong order, thus disabled diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index 66df7257f..a9574cc62 100644 --- a/test/flint/fmpz-test.jl +++ b/test/flint/fmpz-test.jl @@ -1612,3 +1612,10 @@ end @test all(x -> x in rlarge, rand(rlarge, 20)) @test all(x -> x in rlargejumps, rand(rlargejumps, 20)) end + +@testset "ZZRingElem.bits" begin + @test @inferred collect(bits(ZZ(-4))) == [true, false, false] + @test @inferred collect(bits(ZZ(0))) == Bool[] + @test @inferred collect(bits(ZZ(5))) == Bool[true, false, true] + @test @inferred collect(bits(ZZ(2)^64)) == append!([true], falses(64)) +end From 8f87b0cfbb2b9cf8635bffa336faec3f610c13ef Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Tue, 17 Sep 2024 15:54:03 +0200 Subject: [PATCH 10/12] Add tests for `rand(::ZZRingElemUnitRange)` --- test/flint/fmpz-test.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index a9574cc62..dd736e2d4 100644 --- a/test/flint/fmpz-test.jl +++ b/test/flint/fmpz-test.jl @@ -106,6 +106,32 @@ end end @test_throws DomainError Nemo.randseed!(-rand(1:1234)) end + + @testset "ZZRingElemUnitRange.rand" begin + r = ZZ(-4):ZZ(2)^64 + for _ in 1:10 + s = @inferred rand(r) + @test s >= -4 && s <= ZZ(2)^64 + @test s isa ZZRingElem + end + + r = ZZ(-4):ZZ(2):ZZ(10) + rg = Random.RangeGenerator(r) + for _ in 1:10 + s = @inferred rand(rg) + @test s >= -4 && s <= 10 + @test s isa ZZRingElem + @test is_even(s) + end + + a = [one(ZZ) for _ in 1:10] + @inferred rand!(a, r) + for s in a + @test s >= -4 && s <= 10 + @test s isa ZZRingElem + @test is_even(s) + end + end end @testset "ZZRingElem.printing" begin From 31370e8e15c21d521501c5ea11fd30cd3b87fdcd Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 18 Sep 2024 08:55:43 +0200 Subject: [PATCH 11/12] Remove redundant `^(::ResElem, ::ZZRingElem)` --- src/HeckeMoreStuff.jl | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/HeckeMoreStuff.jl b/src/HeckeMoreStuff.jl index 654f84873..e30658c36 100644 --- a/src/HeckeMoreStuff.jl +++ b/src/HeckeMoreStuff.jl @@ -677,24 +677,6 @@ function divexact(a::ZZModRingElem, y::ZZRingElem; check::Bool=true) return divexact(a, parent(a)(y), check=check) end -function ^(a::ResElem, f::ZZRingElem) - f == 0 && return one(parent(a)) - f == 1 && return a - if f < 0 - f = -f - a = inv(a) - end - if f < (1 << 30) - return a^Int(f) - end - b = a^(div(f, 2)) - b = b^2 - if isodd(f) - b *= a - end - return b -end - characteristic(F::EuclideanRingResidueField{ZZRingElem}) = abs(F.modulus) #@doc raw""" From e0b74616df8b9bf3febc6cec804373c1bd8e61e7 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 18 Sep 2024 09:02:05 +0200 Subject: [PATCH 12/12] Test `generic_power` --- test/flint/nmod-test.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/flint/nmod-test.jl b/test/flint/nmod-test.jl index ee41b2067..9a8cfecb7 100644 --- a/test/flint/nmod-test.jl +++ b/test/flint/nmod-test.jl @@ -297,6 +297,13 @@ end end end end + + R, _ = residue_ring(ZZ, 10) + a = R(3) + @test a^ZZ(2) == R(9) + @test a^ZZ(-2) == R(9) + @test a^(ZZ(2)^64) == R(1) + @test a^(-ZZ(2)^64) == R(1) end @testset "zzModRingElem.comparison" begin