diff --git a/src/HeckeMiscInteger.jl b/src/HeckeMiscInteger.jl deleted file mode 100644 index fcc7ea2a9..000000000 --- a/src/HeckeMiscInteger.jl +++ /dev/null @@ -1,255 +0,0 @@ -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 - -Integer(a::ZZRingElem) = BigInt(a) - -^(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 -# -################################################################################ - -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 -# 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 - -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/HeckeMoreStuff.jl b/src/HeckeMoreStuff.jl index 5dceb8ca9..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""" @@ -998,14 +980,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/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/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/src/flint/fmpz.jl b/src/flint/fmpz.jl index 031784c68..63a9ce6c4 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 @@ -975,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) @@ -2242,6 +2278,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) @@ -2990,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 ############################################################################### # @@ -3044,6 +3144,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) @@ -3195,3 +3297,143 @@ 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: eltype +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) + is_zero(L.a) && return nothing # nbits(ZZ()) == 0 + 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 + +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 +#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 + is_zero(L.a) && return nothing + 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) + +eltype(B::BitsFmpz) = Bool + +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/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/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 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 diff --git a/test/flint/fmpz-test.jl b/test/flint/fmpz-test.jl index 44fe32b9e..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 @@ -124,6 +150,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 @@ -893,6 +923,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 @@ -1108,6 +1149,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 @@ -1538,3 +1580,68 @@ 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 + +@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 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