diff --git a/deps/build.jl b/deps/build.jl index 1b41acc1f..26c98ce8e 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -5,7 +5,7 @@ oldwdir = pwd() @show MPIR_VERSION = "3.0.0" @show MPFR_VERSION = "4.0.0" @show ANTIC_VERSION = "0063a41b6a75db801fddf562fa4a329c12ce0584" -@show FLINT_VERSION = "84ee095d0cdb66ca228cf3ff67f0f3a206184971" +@show FLINT_VERSION = "adf1583c6bd92a454f3f92a18adf9063d14637a0" @show ARB_VERSION = "232135f35eeebb74afcea5dd7be436142bee9227" pkgdir = dirname(dirname(@__FILE__)) diff --git a/src/Rings.jl b/src/Rings.jl index b40480177..36db1e4b6 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -59,6 +59,8 @@ include("Fields.jl") include("flint/fmpq_poly.jl") +include("flint/fmpq_mpoly.jl") + include("flint/padic.jl") include("flint/fmpq_rel_series.jl") diff --git a/src/flint/FlintTypes.jl b/src/flint/FlintTypes.jl index 55e439b5c..91a53a7e0 100644 --- a/src/flint/FlintTypes.jl +++ b/src/flint/FlintTypes.jl @@ -794,6 +794,174 @@ function _fmpz_mpoly_ctx_clear_fn(a::FmpzMPolyRing) (Ref{FmpzMPolyRing},), a) end +############################################################################### +# +# FmpqMPolyRing / fmpq_mpoly +# +############################################################################### + +const flint_orderings = [:lex, :deglex, :degrevlex] + +# S is a Symbol which can take the values: +# :lex +# :deglex +# :degrevlex + +mutable struct FmpqMPolyRing <: MPolyRing{fmpq} + nvars::Int + nfields::Cint + ord::Int + deg::Cint + rev::Cint + base_ring::FlintRationalField + S::Array{Symbol, 1} + + function FmpqMPolyRing(s::Array{Symbol, 1}, S::Symbol, cached::Bool = true) + if cached && haskey(FmpqMPolyID, (s, S)) + return FmpqMPolyID[s, S] + else + if S == :lex + ord = 0 + elseif S == :deglex + ord = 1 + elseif S == :degrevlex + ord = 2 + else + error("$S is not a valid ordering") + end + + z = new() + ccall((:fmpq_mpoly_ctx_init, :libflint), Void, + (Ref{FmpqMPolyRing}, Int, Int), + z, length(s), ord) + z.base_ring = FlintQQ + z.S = s + finalizer(z, _fmpq_mpoly_ctx_clear_fn) + if cached + FmpqMPolyID[s, S] = z + end + return z + end + end +end + +function _fmpq_mpoly_ctx_clear_fn(a::FmpqMPolyRing) + ccall((:fmpq_mpoly_ctx_clear, :libflint), Void, + (Ref{FmpqMPolyRing},), a) +end + +const FmpqMPolyID = Dict{Tuple{Array{Symbol, 1}, Symbol}, FmpqMPolyRing}() + +mutable struct fmpq_mpoly <: MPolyElem{fmpq} + content_num::Ptr{Void} + content_den::Ptr{Void} + coeffs::Ptr{Void} + exps::Ptr{Void} + alloc::Int + length::Int + bits::Int + + parent::FmpqMPolyRing + + function fmpq_mpoly(ctx::FmpqMPolyRing) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + return z + end + + function fmpq_mpoly(ctx::FmpqMPolyRing, a::Vector{fmpq}, b::Vector{Vector{UInt}}) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + + for i in 1:length(a) + ccall((:fmpq_mpoly_pushterm_fmpq_ui, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq}, Ptr{UInt}, Ref{FmpqMPolyRing}), + z, a[i], b[i], ctx) + end + + ccall((:fmpq_mpoly_sort, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), z, ctx) + ccall((:fmpq_mpoly_combine_like_terms, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), z, ctx) + return z + end + + function fmpq_mpoly(ctx::FmpqMPolyRing, a::Vector{fmpq}, b::Vector{Vector{fmpz}}) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + + for i in 1:length(a) + ccall((:fmpq_mpoly_pushterm_fmpq_fmpz, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq}, Ptr{Ref{fmpz}}, Ref{FmpqMPolyRing}), + z, a[i], b[i], ctx) + end + + ccall((:fmpq_mpoly_sort, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), z, ctx) + ccall((:fmpq_mpoly_combine_like_terms, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), z, ctx) + return z + end + + function fmpq_mpoly(ctx::FmpqMPolyRing, a::fmpz) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + ccall((:fmpq_mpoly_set_fmpz, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpz}, Ref{FmpqMPolyRing}), z, a, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + return z + end + + function fmpq_mpoly(ctx::FmpqMPolyRing, a::fmpq) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + ccall((:fmpq_mpoly_set_fmpq, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq}, Ref{FmpqMPolyRing}), z, a, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + return z + end + + function fmpq_mpoly(ctx::FmpqMPolyRing, a::Int) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + ccall((:fmpq_mpoly_set_si, :libflint), Void, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), z, a, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + return z + end + + function fmpq_mpoly(ctx::FmpqMPolyRing, a::UInt) + z = new() + ccall((:fmpq_mpoly_init, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing},), z, ctx) + ccall((:fmpq_mpoly_set_ui, :libflint), Void, + (Ref{fmpq_mpoly}, UInt, Ref{FmpqMPolyRing}), z, a, ctx) + z.parent = ctx + finalizer(z, _fmpq_mpoly_clear_fn) + return z + end +end + +function _fmpq_mpoly_clear_fn(a::fmpq_mpoly) + ccall((:fmpq_mpoly_clear, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, a.parent) +end + ############################################################################### # # FqNmodFiniteField / fq_nmod diff --git a/src/flint/fmpq_mpoly.jl b/src/flint/fmpq_mpoly.jl new file mode 100644 index 000000000..dd9add1b5 --- /dev/null +++ b/src/flint/fmpq_mpoly.jl @@ -0,0 +1,726 @@ +############################################################################### +# +# fmpq_mpoly.jl : Flint multivariate polynomials over fmpz +# +############################################################################### + +export FmpqMPolyRing, fmpq_mpoly, degrees + +############################################################################### +# +# Data type and parent object methods +# +############################################################################### + +parent_type(::Type{fmpq_mpoly}) = FmpqMPolyRing + +elem_type(::Type{FmpqMPolyRing}) = fmpq_mpoly + +elem_type(::FmpqMPolyRing) = fmpq_mpoly + +vars(a::FmpqMPolyRing) = a.S + +parent(a::fmpq_mpoly) = a.parent + +function check_parent(a::fmpq_mpoly, b::fmpq_mpoly) + parent(a) != parent(b) && + error("Incompatible polynomial rings in polynomial operation") +end + +nvars(a::FmpqMPolyRing) = ccall((:fmpq_mpoly_ctx_nvars, :libflint), Int, + (Ref{FmpqMPolyRing}, ), a) + +base_ring(a::FmpqMPolyRing) = a.base_ring + +function ordering(a::FmpqMPolyRing) + b = ccall((:fmpq_mpoly_ctx_ord, :libflint), Cint, (Ref{FmpqMPolyRing}, ), a) + return flint_orderings[b + 1] +end + +function gens(R::FmpqMPolyRing) + A = Array{fmpq_mpoly}(R.nvars) + for i = 1:R.nvars + z = R() + ccall((:fmpq_mpoly_gen, :libflint), Void, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), z, i - 1, R) + A[i] = z + end + return A +end + +function gen(R::FmpqMPolyRing, i::Int) + n = nvars(R) + (i <= 0 || i > n) && error("Index must be between 1 and $n") + z = R() + ccall((:fmpq_mpoly_gen, :libflint), Void, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), z, i - 1, R) + return z +end + +function isgen(a::fmpq_mpoly, i::Int) + n = nvars(parent(a)) + (i <= 0 || i > n) && error("Index must be between 1 and $n") + R = parent(a) + return Bool(ccall((:fmpq_mpoly_is_gen, :libflint), Cint, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + a, i - 1, a.parent)) +end + +function isgen(a::fmpq_mpoly) + n = nvars(parent(a)) + for i in 1:n + isgen(a, i) && return true + end + return false +end + +function deepcopy(a::fmpq_mpoly) + z = parent(a)() + ccall((:fmpq_mpoly_set, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, a.parent) + return z +end + +function length(a::fmpq_mpoly) + n = ccall((:fmpq_mpoly_length, :libflint), Int, (Ref{fmpq_mpoly}, ), a) + return n +end + +function one(R::FmpqMPolyRing) + z = R() + ccall((:fmpq_mpoly_one, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), z, R) + return z +end + +function zero(R::FmpqMPolyRing) + z = R() + ccall((:fmpq_mpoly_zero, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), z, R) + return z +end + +function isone(a::fmpq_mpoly) + b = ccall((:fmpq_mpoly_is_one, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, a.parent) + return Bool(b) +end + +function iszero(a::fmpq_mpoly) + b = ccall((:fmpq_mpoly_is_zero, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, a.parent) + return Bool(b) +end + +function ismonomial(a::fmpq_mpoly) + return length(a) == 1 +end + +function isconstant(a::fmpq_mpoly) + b = ccall((:fmpq_mpoly_is_fmpq, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, parent(a)) + return Bool(b) +end + +################################################################################ +# +# Getting coefficients +# +################################################################################ + +function coeff(a::fmpq_mpoly, i::Int) + z = fmpq() + n = length(a) + (i < 1 || i > n) && error("Index must be between 1 and $(length(a))") + ccall((:fmpq_mpoly_get_termcoeff_fmpq, :libflint), Void, + (Ref{fmpq}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + z, a, i - 1, a.parent) + return z +end + +function coeff(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + !isone(length(b)) && error("Second argument must be a monomial") + z = fmpq() + ccall((:fmpq_mpoly_get_coeff_fmpq_monomial, :libflint), Void, + (Ref{fmpq}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z +end + +############################################################################### +# +# Basic manipulation +# +############################################################################### + +function degree_int(a::fmpq_mpoly, i::Int) + n = nvars(parent(a)) + (i <= 0 || i > n) && error("Index must be between 1 and $n") + d = ccall((:fmpq_mpoly_degree_si, :libflint), Int, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), a, i - 1, a.parent) + return d +end + +function degree(a::fmpq_mpoly, i::Int) + n = nvars(parent(a)) + (i <= 0 || i > n) && error("Index must be between 1 and $n") + d = fmpz() + ccall((:fmpq_mpoly_degree_fmpz, :libflint), Void, + (Ref{fmpz}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + d, a, i - 1, a.parent) + return d +end + +function degrees_fit_int(a::fmpq_mpoly) + b = ccall((:fmpq_mpoly_degrees_fit_si, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, a.parent) + return Bool(b) +end + +function degrees_int(a::fmpq_mpoly) + degs = Vector{Int}(nvars(parent(a))) + ccall((:fmpq_mpoly_degrees_si, :libflint), Void, + (Ptr{Int}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + degs, a, a.parent) + return degs +end + +function degrees(a::fmpq_mpoly) + n = nvars(parent(a)) + degs = Vector{fmpz}(n) + for i in 1:n + degs[i] = fmpz() + end + ccall((:fmpq_mpoly_degrees_fmpz, :libflint), Void, + (Ptr{Ref{fmpz}}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + degs, a, a.parent) + return degs +end + +############################################################################### +# +# String I/O +# +############################################################################### + +function show(io::IO, x::fmpq_mpoly) + if length(x) == 0 + print(io, "0") + else + cstr = ccall((:fmpq_mpoly_get_str_pretty, :libflint), Ptr{UInt8}, + (Ref{fmpq_mpoly}, Ptr{Ptr{UInt8}}, Ref{FmpqMPolyRing}), + x, [string(s) for s in vars(parent(x))], x.parent) + print(io, unsafe_string(cstr)) + + ccall((:flint_free, :libflint), Void, (Ptr{UInt8},), cstr) + end +end + +function show(io::IO, p::FmpqMPolyRing) + const max_vars = 5 # largest number of variables to print + n = nvars(p) + print(io, "Multivariate Polynomial Ring in ") + if n > max_vars + print(io, nvars(p)) + print(io, " variables ") + end + for i = 1:min(n - 1, max_vars - 1) + print(io, string(p.S[i]), ", ") + end + if n > max_vars + print(io, "..., ") + end + print(io, string(p.S[n])) + print(io, " over ") + show(io, base_ring(p)) +end + +############################################################################### +# +# Basic arithmetic +# +############################################################################### + +function -(a::fmpq_mpoly) + z = parent(a)() + ccall((:fmpq_mpoly_neg, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, a.parent) + return z +end + +function +(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + z = parent(a)() + ccall((:fmpq_mpoly_add, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, b, a.parent) + return z +end + +function -(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + z = parent(a)() + ccall((:fmpq_mpoly_sub, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, b, a.parent) + return z +end + +function *(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + z = parent(a)() + ccall((:fmpq_mpoly_mul, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, b, a.parent) + return z +end + +############################################################################### +# +# Ad hoc arithmetic +# +############################################################################### + +for (jT, cN, cT) in ((fmpq, :fmpq, Ref{fmpq}), (fmpz, :fmpz, Ref{fmpz}), + (Int, :si, Int)) + @eval begin + function +(a::fmpq_mpoly, b::($jT)) + z = parent(a)() + ccall(($(string(:fmpq_mpoly_add_, cN)), :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, ($cT), Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z + end + + +(a::($jT), b::fmpq_mpoly) = b + a + + function -(a::fmpq_mpoly, b::($jT)) + z = parent(a)() + ccall(($(string(:fmpq_mpoly_sub_, cN)), :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, ($cT), Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z + end + + -(a::($jT), b::fmpq_mpoly) = - (b - a) + + function *(a::fmpq_mpoly, b::($jT)) + z = parent(a)() + ccall(($(string(:fmpq_mpoly_scalar_mul_, cN)), :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, ($cT), Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z + end + + *(a::($jT), b::fmpq_mpoly) = b * a + + function divexact(a::fmpq_mpoly, b::($jT)) + z = parent(a)() + ccall(($(string(:fmpq_mpoly_scalar_div_, cN)), :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, ($cT), Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z + end + + //(a::fmpq_mpoly, b::($jT)) = divexact(a, b) + end +end + ++(a::fmpq_mpoly, b::Integer) = a + fmpz(b) + ++(a::Integer, b::fmpq_mpoly) = b + a + ++(a::fmpq_mpoly, b::Rational{<:Integer}) = a + fmpq(b) + ++(a::Rational{<:Integer}, b::fmpq_mpoly) = b + a + +*(a::fmpq_mpoly, b::Integer) = a * fmpz(b) + +*(a::Integer, b::fmpq_mpoly) = b * a + +*(a::fmpq_mpoly, b::Rational{<:Integer}) = a * fmpq(b) + +*(a::Rational{<:Integer}, b::fmpq_mpoly) = b * a + +divexact(a::fmpq_mpoly, b::Integer) = divexact(a, fmpz(b)) + +divexact(a::fmpq_mpoly, b::Rational{<:Integer}) = divexact(a, fmpq(b)) + +//(a::fmpq_mpoly, b::Integer) = //(a, fmpz(b)) + +//(a::fmpq_mpoly, b::Rational{<:Integer}) = //(a, fmpq(b)) + +############################################################################### +# +# Powering +# +############################################################################### + +function ^(a::fmpq_mpoly, b::Int) + b < 0 && throw(DomainError()) + z = parent(a)() + ccall((:fmpq_mpoly_pow_si, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z +end + +function ^(a::fmpq_mpoly, b::fmpz) + b < 0 && throw(DomainError()) + z = parent(a)() + ccall((:fmpq_mpoly_pow_fmpz, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpz}, Ref{FmpqMPolyRing}), + z, a, b, parent(a)) + return z +end + +################################################################################ +# +# GCD +# +################################################################################ + +function gcd(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + z = parent(a)() + ccall((:fmpq_mpoly_gcd, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, b, a.parent) + return z +end + +############################################################################### +# +# Comparison +# +############################################################################### + +function ==(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + return Bool(ccall((:fmpq_mpoly_equal, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + a, b, a.parent)) +end + +############################################################################### +# +# Ad hoc comparison +# +############################################################################### + +function ==(a::fmpq_mpoly, b::fmpq) + return Bool(ccall((:fmpq_mpoly_equal_fmpq, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{fmpq}, Ref{FmpqMPolyRing}), + a, b, a.parent)) +end + +==(a::fmpq, b::fmpq_mpoly) = b == a + +function ==(a::fmpq_mpoly, b::fmpz) + return Bool(ccall((:fmpq_mpoly_equal_fmpz, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{fmpz}, Ref{FmpqMPolyRing}), + a, b, a.parent)) +end + +function ==(a::fmpq_mpoly, b::Int) + return Bool(ccall((:fmpq_mpoly_equal_si, :libflint), Cint, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + a, b, a.parent)) +end + +==(a::Int, b::fmpq_mpoly) = b == a + +==(a::fmpq_mpoly, b::Integer) = a == fmpz(b) + +==(a::Integer, b::fmpq_mpoly) = b == a + +==(a::fmpq_mpoly, b::Rational{<:Integer}) = a == fmpq(b) + +==(a::Rational{<:Integer}, b::fmpq_mpoly) = b == a + +############################################################################### +# +# Divisibility +# +############################################################################### + +function divides(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + iszero(b) && error("Cannot divide by zero") + z = parent(a)() + d = ccall((:fmpq_mpoly_divides, :libflint), Cint, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + z, a, b, a.parent) + return isone(d), z +end + +############################################################################### +# +# Division with remainder +# +############################################################################### + +function div(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + q = parent(a)() + ccall((:fmpq_mpoly_div, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, + Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + q, a, b, a.parent) + return q +end + +function divrem(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + q = parent(a)() + r = parent(a)() + ccall((:fmpq_mpoly_divrem, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, + Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), + q, r, a, b, a.parent) + return q, r +end + +function divrem(a::fmpq_mpoly, b::Array{fmpq_mpoly, 1}) + len = length(b) + q = [parent(a)() for i in 1:len] + r = parent(a)() + ccall((:fmpq_mpoly_divrem_ideal, :libflint), Void, + (Ptr{Ref{fmpq_mpoly}}, Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, + Ptr{Ref{fmpq_mpoly}}, Int, Ref{FmpqMPolyRing}), + q, r, a, b, len, a.parent) + return q, r +end + +############################################################################### +# +# Exact division +# +############################################################################### + +function divexact(a::fmpq_mpoly, b::fmpq_mpoly) + check_parent(a, b) + b, q = divides(a, b) + !b && error("Division is not exact in divexact") + return q +end + +############################################################################### +# +# Calculus +# +############################################################################### + +function derivative(a::fmpq_mpoly, i::Int) + n = nvars(parent(a)) + (i <= 0 || i > n) && error("Index must be between 1 and $n") + z = parent(a)() + ccall((:fmpq_mpoly_derivative, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + z, a, i - 1, parent(a)) + return z +end + +function integral(a::fmpq_mpoly, i::Int) + n = nvars(parent(a)) + (i <= 0 || i > n) && error("Index must be between 1 and $n") + z = parent(a)() + ccall((:fmpq_mpoly_integral, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + z, a, i - 1, parent(a)) + return z +end + +############################################################################### +# +# Unsafe functions +# +############################################################################### + +function addeq!(a::fmpq_mpoly, b::fmpq_mpoly) + ccall((:fmpq_mpoly_add, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, + Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, a, b, a.parent) + return a +end + +function mul!(a::fmpq_mpoly, b::fmpq_mpoly, c::fmpq_mpoly) + ccall((:fmpq_mpoly_mul, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq_mpoly}, + Ref{fmpq_mpoly}, Ref{FmpqMPolyRing}), a, b, c, a.parent) + return a +end + +function setcoeff!(a::fmpq_mpoly, i::Int, c::fmpq) + ccall((:fmpq_mpoly_set_coeff_fmpq, :libflint), Void, + (Ref{fmpq_mpoly}, Int, Ref{fmpq}, Ref{FmpqMPolyRing}), + a, i - 1, c, a.parent) + return a +end + +setcoeff!(a::fmpq_mpoly, i::Int, c::fmpz) = setcoeff!(a, i, fmpq(c)) + +setcoeff!(a::fmpq_mpoly, i::Int, c::Integer) = setcoeff!(a, i, fmpq(c)) + +setcoeff!(a::fmpq_mpoly, i::Int, c::Rational{<:Integer}) = + setcoeff!(a, i, fmpq(c)) + +############################################################################### +# +# Manipulating terms and monomials +# +############################################################################### + +function _termexp_fits_ui(a::fmpq_mpoly, i::Int) + b = ccall((:fmpq_mpoly_termexp_fits_ui, :libflint), Cint, + (Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), a, i - 1, a.parent) + return Bool(b) +end + +function _get_termexp_ui(a::fmpq_mpoly, i::Int) + z = Vector{UInt}(nvars(parent(a))) + ccall((:fmpq_mpoly_get_termexp_ui, :libflint), Void, + (Ptr{UInt}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + z, a, i - 1, parent(a)) + return z +end + +function _get_termexp_fmpz(a::fmpq_mpoly, i::Int) + n = nvars(parent(a)) + z = Vector{fmpz}(n) + for j in 1:n + z[j] = fmpz() + end + ccall((:fmpq_mpoly_get_termexp_fmpz, :libflint), Void, + (Ptr{Ref{fmpz}}, Ref{fmpq_mpoly}, Int, Ref{FmpqMPolyRing}), + z, a, i - 1, parent(a)) + return z +end + +function _set_termexp_ui!(a::fmpq_mpoly, i::Int, exps::Vector{UInt}) + ccall((:fmpq_mpoly_set_termexp_ui, :libflint), Void, + (Ref{fmpq_mpoly}, Int, Ptr{UInt}, Ref{FmpqMPolyRing}), + a, i - 1, exps, parent(a)) + return a +end + +function _set_termexp_fmpz!(a::fmpq_mpoly, i::Int, exps::Vector{fmpz}) + ccall((:fmpq_mpoly_set_termexp_fmpz, :libflint), Void, + (Ref{fmpq_mpoly}, Int, Ptr{Ref{fmpz}}, Ref{FmpqMPolyRing}), + a, i - 1, exps, parent(a)) + return a +end + +function _get_term(a::fmpq_mpoly, exps::Vector{UInt}) + z = fmpq() + ccall((:fmpq_mpoly_get_term_fmpq_ui, :libflint), Void, + (Ref{fmpq}, Ref{fmpq_mpoly}, Ptr{UInt}, Ref{FmpqMPolyRing}), + z, a, exps, parent(a)) + return z +end + +function _set_term!(a::fmpq_mpoly, exps::Vector{UInt}, b::fmpq) + ccall((:fmpq_mpoly_set_term_fmpq_ui, :libflint), Void, + (Ref{fmpq_mpoly}, Ref{fmpq}, Ptr{UInt}, Ref{FmpqMPolyRing}), + a, b, exps, parent(a)) + return a +end + +############################################################################### +# +# Promotion rules +# +############################################################################### + +promote_rule{V <: Integer}(::Type{fmpq_mpoly}, ::Type{V}) = fmpq_mpoly + +promote_rule{V <: Integer}(::Type{fmpq_mpoly}, ::Type{Rational{V}}) = fmpq_mpoly + +promote_rule(::Type{fmpq_mpoly}, ::Type{fmpz}) = fmpq_mpoly + +promote_rule(::Type{fmpq_mpoly}, ::Type{fmpq}) = fmpq_mpoly + +############################################################################### +# +# Parent object call overload +# +############################################################################### + +function (R::FmpqMPolyRing)() + z = fmpq_mpoly(R) + return z +end + +function (R::FmpqMPolyRing)(b::fmpq) + z = fmpq_mpoly(R, b) + return z +end + +function (R::FmpqMPolyRing)(b::fmpz) + z = fmpq_mpoly(R, b) + return z +end + +function (R::FmpqMPolyRing)(b::Int) + z = fmpq_mpoly(R, b) + return z +end + +function (R::FmpqMPolyRing)(b::UInt) + z = fmpq_mpoly(R, b) + return z +end + +function (R::FmpqMPolyRing)(b::Integer) + return R(fmpz(b)) +end + +function (R::FmpqMPolyRing)(b::Rational{<:Integer}) + return R(fmpq(b)) +end + +function (R::FmpqMPolyRing)(a::fmpq_mpoly) + parent(a) != R && error("Unable to coerce polynomial") + return a +end + +function (R::FmpqMPolyRing)(a::Vector{fmpq}, b::Vector{Vector{T}}) where {T <: Union{fmpz, UInt}} + length(a) != length(b) && error("Coefficient and exponent vector must have the same length") + + for i in 1:length(b) + length(b[i]) != nvars(R) && error("Exponent vector $i has length $(length(b[i])) (expected $(nvars(R))") + end + + z = fmpq_mpoly(R, a, b) + return z +end + +function (R::FmpqMPolyRing)(a::Vector{Any}, b::Vector{Vector{T}}) where T + n = nvars(R) + length(a) != length(b) && error("Coefficient and exponent vector must have the same length") + newa = map(FlintQQ, a) + newb = map(x -> map(FlintZZ, x), b) + newaa = convert(Vector{fmpq}, newa) + newbb = convert(Vector{Vector{fmpz}}, newb) + + for i in 1:length(newbb) + length(newbb[i]) != n && error("Exponent vector $i has length $(length(newbb[i])) (expected $(nvars(R)))") + end + + return R(newaa, newbb) +end + +############################################################################### +# +# PolynomialRing constructor +# +############################################################################### + +function PolynomialRing(R::FlintRationalField, s::Array{String, 1}; cached::Bool = true, ordering::Symbol = :lex) + U = [Symbol(x) for x in s] + parent_obj = FmpqMPolyRing(U, ordering, cached) + return tuple(parent_obj, gens(parent_obj)) +end diff --git a/test/Rings-test.jl b/test/Rings-test.jl index a246f8ec7..6e6fccb76 100644 --- a/test/Rings-test.jl +++ b/test/Rings-test.jl @@ -31,6 +31,8 @@ include("arb/acb_poly-test.jl") include("arb/arb_mat-test.jl") include("arb/acb_mat-test.jl") +include("flint/fmpq_mpoly-test.jl") + function test_rings() test_fmpz() test_fmpz_poly() @@ -65,4 +67,6 @@ function test_rings() test_fmpq_mat() test_arb_mat() test_acb_mat() + + test_fmpq_mpoly() end diff --git a/test/flint/fmpq_mpoly-test.jl b/test/flint/fmpq_mpoly-test.jl new file mode 100644 index 000000000..b3c7b7bec --- /dev/null +++ b/test/flint/fmpq_mpoly-test.jl @@ -0,0 +1,561 @@ +function test_fmpq_mpoly_constructors() + print("fmpq_mpoly.constructors...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + SS, varlist = PolynomialRing(R, var_names, ordering = ord) + + @test S === SS + + SSS, varlist = PolynomialRing(R, var_names, ordering = ord, cached = false) + SSSS, varlist = PolynomialRing(R, var_names, ordering = ord, cached = false) + + @test !(SSS === SSSS) + + @test nvars(S) == num_vars + + @test elem_type(S) == fmpq_mpoly + @test elem_type(FmpqMPolyRing) == fmpq_mpoly + @test parent_type(fmpq_mpoly) == FmpqMPolyRing + + @test typeof(S) <: FmpqMPolyRing + + isa(vars(S), Array{Symbol, 1}) + + for j = 1:num_vars + @test isa(varlist[j], fmpq_mpoly) + @test isa(gens(S)[j], fmpq_mpoly) + end + + f = rand(S, 0:5, 0:100, -100:100) + + @test isa(f, fmpq_mpoly) + + @test isa(S(2), fmpq_mpoly) + + @test isa(S(R(2)), fmpq_mpoly) + + @test isa(S(f), fmpq_mpoly) + + V = [R(rand(-100:100)) for i in 1:5] + + W0 = [ UInt[rand(0:100) for i in 1:num_vars] for j in 1:5] + + @test isa(S(V, W0), fmpq_mpoly) + + for i in 1:num_vars + f = gen(S, i) + @test isgen(f, i) + @test isgen(f) + @test !isgen(f + 1, i) + @test !isgen(f + 1) + end + end + + println("PASS") +end + +function test_fmpq_mpoly_manipulation() + print("fmpq_mpoly.manipulation...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + g = gens(S) + + @test !isgen(S(1)) + + for i = 1:num_vars + @test isgen(varlist[i]) + @test isgen(g[i]) + @test !isgen(g[i] + 1) + end + + f = rand(S, 0:5, 0:100, -100:100) + + @test f == deepcopy(f) + + if length(f) > 0 + i = rand(1:(length(f))) + @test isa(coeff(f, i), elem_type(R)) + end + + f = rand(S, 1:5, 0:100, -100:100) + + @test f == sum((coeff(f, i) * S(fmpq[1], [Nemo._get_termexp_fmpz(f, i)]) for i in 1:length(f))) + + deg = isdegree(ordering(S)) + rev = isreverse(ordering(S)) + + @test ord == ordering(S) + + @test isone(one(S)) + + @test iszero(zero(S)) + + @test isconstant(S(rand(-100:100))) + @test isconstant(S(zero(S))) + + g = rand(S, 1:1, 0:100, 1:100) + h = rand(S, 1:1, 0:100, 1:1) + h2 = rand(S, 2:2, 1:100, 1:1) + + @test ismonomial(h) + @test !ismonomial(h2 + 1 + gen(S, 1)) + + monomialexp = unique([UInt[rand(1:10) for j in 1:num_vars] for k in 1:10]) + coeffs = [rand(FlintQQ, 1:10) for k in 1:length(monomialexp)] + h = S(coeffs, monomialexp) + @test length(h) == length(monomialexp) + for k in 1:length(h) + @test coeff(h, S(fmpq[1], [monomialexp[k]])) == coeffs[k] + end + end + + println("PASS") +end + +function test_fmpq_mpoly_unary_ops() + print("fmpq_mpoly.unary_ops...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:10 + f = rand(S, 0:5, 0:100, -100:100) + + @test f == -(-f) + end + end + + println("PASS") +end + +function test_fmpq_mpoly_binary_ops() + print("fmpq_mpoly.binary_ops...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:10 + f = rand(S, 0:5, 0:100, -100:100) + g = rand(S, 0:5, 0:100, -100:100) + h = rand(S, 0:5, 0:100, -100:100) + + @test f + g == g + f + @test f - g == -(g - f) + @test f*g == g*f + @test f*g + f*h == f*(g + h) + @test f*g - f*h == f*(g - h) + end + end + + println("PASS") +end + +function test_fmpq_mpoly_adhoc_binary() + print("fmpq_mpoly.adhoc_binary...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:100 + f = rand(S, 0:5, 0:100, -100:100) + + d1 = rand(-20:20) + d2 = rand(-20:20) + g1 = rand(R, -10:10) + g2 = rand(R, -10:10) + + @test f*d1 + f*d2 == (d1 + d2)*f + @test f*BigInt(d1) + f*BigInt(d2) == (BigInt(d1) + BigInt(d2))*f + @test f*Rational{Int}(d1) + f*Rational{Int}(d2) == (Rational{Int}(d1) + Rational{Int}(d2))*f + @test f*g1 + f*g2 == (g1 + g2)*f + + @test f + d1 + d2 == d1 + d2 + f + @test f + BigInt(d1) + BigInt(d2) == BigInt(d1) + BigInt(d2) + f + @test f + Rational{Int}(d1) + Rational{Int}(d2) == Rational{Int}(d1) + Rational{Int}(d2) + f + @test f + g1 + g2 == g1 + g2 + f + + @test f - d1 - d2 == -((d1 + d2) - f) + @test f - BigInt(d1) - BigInt(d2) == -((BigInt(d1) + BigInt(d2)) - f) + @test f - Rational{Int}(d1) - Rational{Int}(d2) == -((Rational{Int}(d1) + Rational{Int}(d2)) - f) + @test f - g1 - g2 == -((g1 + g2) - f) + + @test f + d1 - d1 == f + @test f + BigInt(d1) - BigInt(d1) == f + @test f + Rational{Int}(d1) - Rational{Int}(d1) == f + @test f + g1 - g1 == f + end + end + + println("PASS") +end + +function test_fmpq_mpoly_adhoc_comparison() + print("fmpq_mpoly.adhoc_comparison...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:100 + d = rand(-100:100) + + @test S(d) == d + @test d == S(d) + @test S(fmpz(d)) == fmpz(d) + @test fmpz(d) == S(fmpz(d)) + @test S(fmpq(d)) == fmpq(d) + @test fmpq(d) == S(fmpq(d)) + @test S(d) == BigInt(d) + @test BigInt(d) == S(d) + @test S(d) == Rational{Int}(d) + @test Rational{Int}(d) == S(d) + end + end + + println("PASS") +end + +function test_fmpq_mpoly_powering() + print("fmpq_mpoly.powering...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:10 + f = rand(S, 0:5, 0:100, -100:100) + + expn = rand(0:10) + + r = S(1) + for i = 1:expn + r *= f + end + + @test (f == 0 && expn == 0 && f^expn == 0) || f^expn == r + end + end + + println("PASS") +end + +function test_fmpq_mpoly_divides() + print("fmpq_mpoly.divides...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:10 + f = S(0) + while iszero(f) + f = rand(S, 0:5, 0:100, -100:100) + end + g = rand(S, 0:5, 0:100, -100:100) + + p = f*g + + flag, q = divides(p, f) + + @test flag == true + @test q == g + + q = divexact(p, f) + + @test q == g + end + end + + println("PASS") +end + +function test_fmpq_mpoly_euclidean_division() + print("fmpq_mpoly.euclidean_division...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:10 + f = S(0) + while iszero(f) + f = rand(S, 0:5, 0:100, -100:100) + end + g = rand(S, 0:5, 0:100, -100:100) + + p = f*g + + q1, r = divrem(p, f) + q2 = div(p, f) + + @test q1 == g + @test q2 == g + @test f*q1 + r == p + + q3, r3 = divrem(g, f) + q4 = div(g, f) + flag, q5 = divides(g, f) + + @test q3*f + r3 == g + @test q3 == q4 + @test (r3 == 0 && flag == true && q5 == q3) || (r3 != 0 && flag == false) + end + end + + println("PASS") +end + +function test_fmpq_mpoly_ideal_reduction() + print("fmpq_mpoly.ideal_reduction...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:10 + f = S(0) + while iszero(f) + f = rand(S, 0:5, 0:100, -100:100) + end + g = rand(S, 0:5, 0:100, -100:100) + + p = f*g + + q1, r = divrem(p, [f]) + + @test q1[1] == g + @test r == 0 + end + + for iter = 1:10 + num = rand(1:5) + + V = Array{elem_type(S)}(num) + + for i = 1:num + V[i] = S(0) + while iszero(V[i]) + V[i] = rand(S, 0:5, 0:100, -100:100) + end + end + g = rand(S, 0:5, 0:100, -100:100) + + q, r = divrem(g, V) + + p = r + for i = 1:num + p += q[i]*V[i] + end + + @test p == g + end + end + + println("PASS") +end + +function test_fmpq_mpoly_gcd() + print("fmpq_mpoly.gcd...") + + for num_vars = 1:4 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(FlintQQ, var_names, ordering = ord) + + for iter = 1:10 + f = rand(S, 0:4, 0:5, -10:10) + g = rand(S, 0:4, 0:5, -10:10) + h = rand(S, 0:4, 0:5, -10:10) + + g1 = gcd(f, g) + g2 = gcd(f*h, g*h) + + if !iszero(h) && !iszero(g1) + b, q = divides(g2, g1 * h) + @test b + @test isconstant(q) + end + end + end + + println("PASS") +end + +function test_fmpq_mpoly_evaluation() + print("fmpq_mpoly.evaluation...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:100 + f = rand(S, 0:5, 0:100, -100:100) + g = rand(S, 0:5, 0:100, -100:100) + + V1 = [rand(-10:10) for i in 1:num_vars] + + r1 = evaluate(f, V1) + r2 = evaluate(g, V1) + r3 = evaluate(f + g, V1) + + @test r3 == r1 + r2 + + V2 = [BigInt(rand(-10:10)) for i in 1:num_vars] + + r1 = evaluate(f, V2) + r2 = evaluate(g, V2) + r3 = evaluate(f + g, V2) + + @test r3 == r1 + r2 + + V3 = [R(rand(-10:10)) for i in 1:num_vars] + + r1 = evaluate(f, V3) + r2 = evaluate(g, V3) + r3 = evaluate(f + g, V3) + + @test r3 == r1 + r2 + end + end + + println("PASS") +end + +function test_fmpq_mpoly_valuation() + print("fmpq_mpoly.valuation...") + + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for iter = 1:100 + f = S() + g = S() + while f == 0 || g == 0 || isconstant(g) + f = rand(S, 0:5, 0:100, -100:100) + g = rand(S, 0:5, 0:100, -100:100) + end + + d1 = valuation(f, g) + + expn = rand(1:5) + + d2 = valuation(f*g^expn, g) + + @test d2 == d1 + expn + + d3, q3 = remove(f, g) + + @test d3 == d1 + @test f == q3*g^d3 + + d4, q4 = remove(q3*g^expn, g) + + @test d4 == expn + @test q4 == q3 + end + end + + println("PASS") +end + +function test_fmpq_mpoly_derivative_integral() + print("fmpq_mpoly.derivative_integral...") + R = FlintQQ + + for num_vars = 1:10 + var_names = ["x$j" for j in 1:num_vars] + ord = rand_ordering() + + S, varlist = PolynomialRing(R, var_names, ordering = ord) + + for j in 1:100 + f = rand(S, 0:5, 0:100, -100:100) + + for i in 1:num_vars + @test degree((integral(derivative(f, i), i) - f), i) <= 0 # Constant or zero + @test derivative(integral(f, i), i) == f + end + end + end + + println("PASS") +end + + + +function test_fmpq_mpoly() + test_fmpq_mpoly_constructors() + test_fmpq_mpoly_manipulation() + test_fmpq_mpoly_unary_ops() + test_fmpq_mpoly_binary_ops() + test_fmpq_mpoly_adhoc_binary() + test_fmpq_mpoly_adhoc_comparison() + test_fmpq_mpoly_powering() + test_fmpq_mpoly_divides() + test_fmpq_mpoly_euclidean_division() + test_fmpq_mpoly_ideal_reduction() + test_fmpq_mpoly_gcd() + #test_fmpq_mpoly_evaluation() + test_fmpq_mpoly_valuation() + test_fmpq_mpoly_derivative_integral() + + println("") +end