diff --git a/docs/make.jl b/docs/make.jl index 50cc49d0b7..54a008e213 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -34,7 +34,7 @@ makedocs( "polynomial.md", "ncpolynomial.md", "mpolynomial.md", - "univpolynomial.md", + "universal_ring.md", "laurent_polynomial.md", "laurent_mpolynomial.md", "series.md", diff --git a/docs/src/universal_ring.md b/docs/src/universal_ring.md new file mode 100644 index 0000000000..2037fca973 --- /dev/null +++ b/docs/src/universal_ring.md @@ -0,0 +1,123 @@ +```@meta +CurrentModule = AbstractAlgebra +CollapsedDocStrings = true +DocTestSetup = AbstractAlgebra.doctestsetup() +``` + +# Universal ring + +AbstractAlgebra provides a module, implemented in `src/UniversalRing.jl` for +universal rings. They "universalize" rings which have a notion of generators (or +variables) and coefficients. The most important example of such a ring is a +multivariate polynomial ring. In contrast to the usual multivariate polynomial +ring, its universal pendant allows for variables to be added at any time. + +Another example is the ring of multivariate Laurent polynomials. + +The type of the universal ring is `UniversalRing{T, U}` where `T` is the type of +the elements of the underlying ring and `U` is the type of the coefficients. In +the case of multivariate polynomials `T` would be a subtype of `MPolyRingElem` +and `U` would be the type of the coefficients of the polynomials, for example +`BigInt`. + +## Universal polynomial ring + +To compensate for the fact that the number of variables may change, many of the +functions relax their restrictions on exponent vectors. For example, if one +creates a polynomial when the ring only has two variables, each exponent vector +would consist of two integers. Later, when the ring has more variable, these +exponent vectors will still be accepted. The exponent vectors are simply padded +out to the full number of variables behind the scenes. + +The universal polynomial ring behaves exactly like a multivariate polynomial +ring with the few differences noted above. + +The only functionality not implemented is the ability to do `divrem` by an +ideal of polynomials. + +The universal polynomial ring is very useful for doing symbolic manipulation. +However, it is important to understand that AbstractAlgebra is not a symbolic +system and the performance of the universal polynomial ring will closely match +that of a multivariate polynomial ring with the same number of variables. + +The disadvantage of this approach to symbolic manipulation is that some +manipulations that would be offered by a symbolic system are not available, +as variables are not identified by their names alone in AbstractAlgebra, as +would be the case symbolically, but by objects. + +The most powerful symbolic tools we offer are the generalized evaluation +functions, the multivariate coefficient functionality, the ability to +change coefficient ring and to map coefficients according to a supplied +function and the ability to convert a multivariate which happens to have +just one variable into a dense univariate polynomial. + +Further facilities may be added in future to ease symbolic manipulations. + +## Constructors + +In order to construct universal polynomials in AbstractAlgebra, one must first +construct the universal polynomial ring itself. This is unique given a +coefficient ring. + +The universal polynomial ring over a given coefficient ring `R` is constructed +with one of the following constructor functions. + +```@docs +universal_polynomial_ring +``` + +Similarly, on can construct universal Laurent polynomial rings. + +```@docs +universal_laurent_polynomial_ring +``` + +## Adding variables + +There are two ways to add variables to a universal ring `S`. + +```julia +gen(S::UniversalRing, var::VarName) +gens(S::UniversalRing, vars::Vector{VarName}) +``` + +**Examples** + +```jldoctest +julia> S = universal_polynomial_ring(ZZ) +Universal polynomial ring over Integers + +julia> x = gen(S, :x) +x + +julia> number_of_generators(S) +1 + +julia> y, z = gens(S, [:y, :z]) +2-element Vector{UniversalRingElem{AbstractAlgebra.Generic.MPoly{BigInt}, BigInt}}: + y + z + +julia> number_of_generators(S) +3 +``` + +## Implement universal rings + +To be able to use a ring as a base ring of an universal ring one has to +implement a few methods. In addition to the variable and coefficient related +methods like `gen`, `gens`, `symbols`, `coefficient_ring` there are the two +internal methods `_add_gens` and `_upgrade` used to extend the number of +variables. + +```@docs +AbstractAlgebra._add_gens +AbstractAlgebra._upgrade +``` + +### Hashing + +To be able to use subtypes of `UniversalRingElem` efficiently as keys of a +dictionary or similar applications, it is necessary to implement a custom `hash` +function. The results should not depend on the number of variables currently in +the parent. An example implementation can be found in `src/UnivPoly.jl` diff --git a/docs/src/univpolynomial.md b/docs/src/univpolynomial.md deleted file mode 100644 index 8a9e5daf70..0000000000 --- a/docs/src/univpolynomial.md +++ /dev/null @@ -1,101 +0,0 @@ -```@meta -CurrentModule = AbstractAlgebra -CollapsedDocStrings = true -DocTestSetup = AbstractAlgebra.doctestsetup() -``` - -# Universal polynomial - -AbstractAlgebra.jl provides a module, implemented in `src/generic/UnivPoly.jl` for -a universal polynomial ring. This is very similar to the multivariate polynomial -rings, except that variables can be added to the ring at any time. - -To compensate for the fact that the number of variables may change, many of the -functions relax their restrictions on exponent vectors. For example, if one -creates a polynomial when the ring only has two variables, each exponent vector -would consist of two integers. Later, when the ring has more variable, these -exponent vectors will still be accepted. The exponent vectors are simply padded -out to the full number of variables behind the scenes. - -## Generic sparse distributed universal multivariable polynomial types - -AbstractAlgebra provides a generic universal polynomial type `Generic.UnivPoly{T, U}` -where `T` is the type of elements of the coefficient ring and `U` is the type of -the elements of the underlying multivariate polynomial ring. Essentially, `U` can -be any type belonging to `MPolyRingElem{T}`. - -Parent objects of such polynomials have type `Generic.UniversalPolyRing{T, U}`. - -## Abstract types - -AbstractAlgebra also provides abstract types for universal polynomials and their -rings. These are `UniversalPolyRingElem{T, U}` and `UniversalPolyRing{T, U}` respectively. -These in turn belong to `Ring`. - -## Polynomial ring constructors - -In order to construct universal polynomials in AbstractAlgebra.jl, one must first -construct the universal polynomial ring itself. This is unique given a base ring. - -The universal polynomial ring over a given base ring `R` is constructed with -one of the following constructor functions. - -```@docs -universal_polynomial_ring -``` - -## Adding variables - -There are two ways to add variables to a universal polynomial ring `S`. - -```julia -gen(S::UniversalPolyRing, var::VarName) -gens(S::UniversalPolyRing, vars::Vector{VarName}) -``` - -**Examples** - -```jldoctest -julia> S = universal_polynomial_ring(ZZ) -Universal Polynomial Ring over Integers - -julia> x = gen(S, :x) -x - -julia> number_of_generators(S) -1 - -julia> y, z = gens(S, [:y, :z]) -2-element Vector{AbstractAlgebra.Generic.UnivPoly{BigInt}}: - y - z - -julia> number_of_generators(S) -3 -``` - -## Universal polynomial functionality - -The universal polynomial ring behaves exactly like a multivariate polynomial -ring with the few differences noted above. - -The only functionality not implemented is the ability to do `divrem` by an -ideal of polynomials. - -The universal polynomial ring is very useful for doing symbolic manipulation. -However, it is important to understand that AbstractAlgebra is not a symbolic -system and the performance of the universal polynomial ring will closely match -that of a multivariate polynomial ring with the same number of variables. - -The disadvantage of this approach to symbolic manipulation is that some -manipulations that would be offered by a symbolic system are not available, -as variables are not identified by their names alone in AbstractAlgebra, as -would be the case symbolically, but by objects. - -The most powerful symbolic tools we offer are the generalised evaluation -functions, the multivariate coefficient functionality, the ability to -change coefficient ring and to map coefficients according to a supplied -function and the ability to convert a multivariate which happens to have -just one variable into a dense univariate polynomial. - -Further facilities may be added in future to ease symbolic manipulations. diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index ec10a845e9..f28346af50 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -303,6 +303,8 @@ include("ResidueField.jl") include("Fraction.jl") include("TotalFraction.jl") include("MPoly.jl") +include("UniversalLaurentPoly.jl") +include("UniversalRing.jl") include("UnivPoly.jl") include("FreeAssociativeAlgebra.jl") include("LaurentMPoly.jl") diff --git a/src/AbstractTypes.jl b/src/AbstractTypes.jl index b1c1885090..06ff69c972 100644 --- a/src/AbstractTypes.jl +++ b/src/AbstractTypes.jl @@ -77,8 +77,6 @@ abstract type NCPolyRing{T} <: NCRing end abstract type MPolyRing{T} <: Ring end -abstract type UniversalPolyRing{T} <: Ring end - abstract type LaurentPolyRing{T} <: Ring end abstract type LaurentMPolyRing{T} <: Ring end @@ -116,8 +114,6 @@ abstract type NCPolyRingElem{T} <: NCRingElem end abstract type MPolyRingElem{T} <: RingElem end -abstract type UniversalPolyRingElem{T} <: RingElem end - abstract type LaurentPolyRingElem{T} <: RingElem end abstract type LaurentMPolyRingElem{T} <: RingElem end diff --git a/src/ConcreteTypes.jl b/src/ConcreteTypes.jl index b1dbdc4740..97fc889172 100644 --- a/src/ConcreteTypes.jl +++ b/src/ConcreteTypes.jl @@ -16,3 +16,44 @@ struct MatSpace{T <: NCRingElement} <: Module{T} return new{T}(R, r, c) end end + +############################################################################### +# +# Universal ring +# +############################################################################### + +@attributes mutable struct UniversalRing{T <: RingElem, U <: RingElement} <: Ring + base_ring::Ring + + function UniversalRing(R::Ring) + return new{elem_type(R), elem_type(coefficient_ring(R))}(R) + end +end + +mutable struct UniversalRingElem{T <: RingElem, U <: RingElement} <: RingElem + data::T + parent::UniversalRing{T, U} +end + +############################################################################### +# +# Universal polynomial ring +# +############################################################################### + +struct UnivPolyCoeffs{T <: RingElem} + poly::T +end + +struct UnivPolyExponentVectors{T <: RingElem} + poly::T +end + +struct UnivPolyTerms{T <: RingElem} + poly::T +end + +struct UnivPolyMonomials{T <: RingElem} + poly::T +end diff --git a/src/Deprecations.jl b/src/Deprecations.jl index 702f6b8ac3..1341b79359 100644 --- a/src/Deprecations.jl +++ b/src/Deprecations.jl @@ -16,6 +16,10 @@ @alias dense_poly_type poly_type @alias dense_poly_ring_type poly_ring_type +const UniversalPolyRing{T} = UniversalRing{<:MPolyRingElem, T} +const UniversalPolyRingElem{T} = UniversalRingElem{<:MPolyRingElem, T} +const UnivPoly{T} = UniversalPolyRingElem{T} + ############################################################################### # # Deprecated bindings diff --git a/src/Generic.jl b/src/Generic.jl index 3a461f0001..e88ee316d5 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -32,7 +32,7 @@ include("generic/NCPoly.jl") include("generic/MPoly.jl") -include("generic/UnivPoly.jl") +include("generic/UniversalRing.jl") include("generic/SparsePoly.jl") diff --git a/src/UnivPoly.jl b/src/UnivPoly.jl index ac03532625..0cb24aede1 100644 --- a/src/UnivPoly.jl +++ b/src/UnivPoly.jl @@ -1,13 +1,476 @@ ############################################################################### # -# UnivPoly.jl: Generic universal polynomial ring (variables can be added) +# UnivPoly.jl : Universal polynomial ring (variables can be added) # ############################################################################### -function content(a::UniversalPolyRingElem) +internal_ordering(p::UniversalRing{<:MPolyRingElem}) = internal_ordering(base_ring(p)) + +############################################################################### +# +# Manipulating terms and monomials +# +############################################################################### + +exponent_vector(p::UniversalRingElem{<:MPolyRingElem}, i::Int) = exponent_vector(data(p), i) + +function exponent(p::UniversalRingElem{<:MPolyRingElem}, i::Int, j::Int) + if j > nvars(parent(data(p))) && j <= nvars(parent(p)) + return 0 + end + return exponent(data(p), i, j) +end + +function set_exponent_vector!(p::UniversalRingElem{<:MPolyRingElem}, i::Int, exps::Vector{Int}) + S = parent(p) + len = length(exps) + upgrade!(p) + if len < nvars(S) + exps = vcat(exps, zeros(Int, nvars(S) - len)) + end + p.data = set_exponent_vector!(data(p), i, exps) + return p +end + +function coeff(p::UniversalRingElem{<:MPolyRingElem}, exps::Vector{Int}) + S = parent(p) + len = length(exps) + n = nvars(parent(data(p))) + if len > n + if !iszero(exps[n + 1:len]) + return coefficient_ring(S)() + end + return coeff(data(p), exps[1:n]) + end + n = nvars(parent(data(p))) + if len < n + exps = vcat(exps, zeros(Int, n - len)) + end + return coeff(data(p), exps) +end + +function setcoeff!(p::UniversalRingElem{<:MPolyRingElem}, exps::Vector{Int}, c::RingElement) + c = coefficient_ring(data(p))(c) + S = parent(p) + len = length(exps) + upgrade!(p) + if len < nvars(S) + exps = vcat(exps, zeros(Int, nvars(S) - len)) + end + p.data = setcoeff!(data(p), exps, c) + return p +end + +function setcoeff!(a::UniversalRingElem{<:MPolyRingElem}, i::Int, c::RingElement) + setcoeff!(data(a), i, c) + return a +end + +############################################################################### +# +# Basic manipulation +# +############################################################################### + +is_homogeneous(p::UniversalRingElem{<:MPolyRingElem}) = is_homogeneous(data(p)) + +is_monomial(p::UniversalRingElem{<:MPolyRingElem}) = is_monomial(data(p)) + +is_constant(p::UniversalRingElem{<:MPolyRingElem}) = is_constant(data(p)) + +is_term(p::UniversalRingElem{<:MPolyRingElem}) = is_term(data(p)) + +coeff(p::UniversalRingElem{<:MPolyRingElem}, i::Int) = coeff(data(p), i) + +function coeff(p::T, m::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + univ_promote!(p, m) + return coeff(data(p), data(m)) +end + +function content(a::UniversalRingElem{<:MPolyRingElem}) return content(data(a)) end +function monomial(p::UniversalRingElem{<:MPolyRingElem}, i::Int) + S = parent(p) + m = monomial(data(p), i) + return UniversalRingElem(m, S) +end + +function monomial!(m::T, p::T, i::Int) where {T <: UniversalRingElem{<:MPolyRingElem}} + parent(m) != parent(p) && error("Incompatible monomial") + if parent(data(m)) != parent(data(p)) + m.data = parent(data(p))() + end + m.data = monomial!(data(m), data(p), i) + return m +end + +function term(p::UniversalRingElem{<:MPolyRingElem}, i::Int) + S = parent(p) + t = term(data(p), i) + return UniversalRingElem(t, S) +end + +leading_coefficient(p::UniversalRingElem{<:MPolyRingElem}) = leading_coefficient(data(p)) + +trailing_coefficient(p::UniversalRingElem{<:MPolyRingElem}) = trailing_coefficient(data(p)) + +function tail(p::UniversalRingElem{<:MPolyRingElem}) + S = parent(p) + return UniversalRingElem(tail(data(p)), S) +end + +constant_coefficient(p::UniversalRingElem{<:MPolyRingElem}) = constant_coefficient(data(p)) + +function leading_monomial(p::UniversalRingElem{<:MPolyRingElem}) + S = parent(p) + return UniversalRingElem(leading_monomial(data(p)), S) +end + +function leading_term(p::UniversalRingElem{<:MPolyRingElem}) + S = parent(p) + return UniversalRingElem(leading_term(data(p)), S) +end + +max_fields(p::UniversalRingElem{<:MPolyRingElem}) = max_fields(data(p)) + +function degree(p::UniversalRingElem{<:MPolyRingElem}, i::Int) + if i <= nvars(parent(p)) && i > nvars(parent(data(p))) + return 0 + end + return degree(data(p), i) +end + +function degree(f::T, x::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + check_parent(f, x) + return degree(f, var_index(x)) +end + +function degrees(p::UniversalRingElem{<:MPolyRingElem}) + v = degrees(data(p)) + len = length(v) + num = nvars(parent(p)) + if len < num + v = vcat(v, zeros(Int, num - len)) + end + return v +end + +total_degree(p::UniversalRingElem{<:MPolyRingElem}) = total_degree(data(p)) + +length(p::UniversalRingElem{<:MPolyRingElem}) = length(data(p)) + +var_index(x::UniversalRingElem{<:MPolyRingElem}) = var_index(data(x)) + +var_indices(p::UniversalRingElem{<:MPolyRingElem}) = var_indices(data(p)) + +function vars(p::UniversalRingElem{<:MPolyRingElem}) + V = vars(data(p)) + S = parent(p) + return [UniversalRingElem(v, S) for v in V] +end + +function Base.hash(p::UniversalRingElem{<:MPolyRingElem}, h::UInt) + b = 0xcf418d4529109236%UInt + for (c, v) in zip(coefficients(data(p)), exponent_vectors(data(p))) + l = length(v) + while l > 0 && iszero(v[l]) + l -= 1 + end + b = xor(b, xor(Base.hash(v[1:l], h), h)) + b = xor(b, xor(hash(c, h), h)) + b = (b << 1) | (b >> (sizeof(Int)*8 - 1)) + end + return b +end + +############################################################################### +# +# Multivariate coefficients +# +############################################################################### + +function coeff(p::UniversalRingElem{<:MPolyRingElem}, vars::Vector{Int}, exps::Vector{Int}) + len = length(vars) + @req len == length(exps) "Number of variables does not match number of exponents" + S = parent(p) + n = nvars(S) + num = nvars(parent(data(p))) + vars2 = Vector{Int}(undef, 0) + exps2 = Vector{Int}(undef, 0) + for i = 1:len + @req 1 <= vars[i] <= nvars(S) "Variable index not in range" + if vars[i] <= num + push!(vars2, vars[i]) + push!(exps2, exps[i]) + elseif exps[i] > 0 + return zero(S) + end + end + return UniversalRingElem(coeff(data(p), vars2, exps2), S) +end + +function coeff(a::T, vars::Vector{T}, exps::Vector{Int}) where {T <: UniversalRingElem{<:MPolyRingElem}} + varidx = [var_index(x) for x in vars] + return coeff(a, varidx, exps) +end + +############################################################################### +# +# String I/O +# +############################################################################### + +universal_ring_name(R::UniversalRing{<:MPolyRingElem}) = "polynomial ring" + +############################################################################### +# +# Iterators +# +############################################################################### + +function Base.iterate(x::UnivPolyCoeffs) + if length(x.poly) >= 1 + return coeff(x.poly, 1), 1 + else + return nothing + end +end + +function Base.iterate(x::UnivPolyCoeffs, state) + state += 1 + if length(x.poly) >= state + return coeff(x.poly, state), state + else + return nothing + end +end + +function Base.iterate(x::UnivPolyExponentVectors) + if length(x.poly) >= 1 + return exponent_vector(x.poly, 1), 1 + else + return nothing + end +end + +function Base.iterate(x::UnivPolyExponentVectors, state) + state += 1 + if length(x.poly) >= state + return exponent_vector(x.poly, state), state + else + return nothing + end +end + +function Base.iterate(x::UnivPolyTerms) + if length(x.poly) >= 1 + return term(x.poly, 1), 1 + else + return nothing + end +end + +function Base.iterate(x::UnivPolyTerms, state) + state += 1 + if length(x.poly) >= state + return term(x.poly, state), state + else + return nothing + end +end + +function Base.iterate(x::UnivPolyMonomials) + if length(x.poly) >= 1 + return monomial(x.poly, 1), 1 + else + return nothing + end +end + +function Base.iterate(x::UnivPolyMonomials, state) + state += 1 + if length(x.poly) >= state + return monomial(x.poly, state), state + else + return nothing + end +end + +function Base.length(x::Union{UnivPolyCoeffs, UnivPolyExponentVectors, UnivPolyTerms, UnivPolyMonomials}) + return length(x.poly) +end + +function Base.eltype(::Type{UnivPolyCoeffs{UniversalRingElem{<:MPolyRingElem, T}}}) where T <: RingElement + return T +end + +function Base.eltype(::Type{UnivPolyExponentVectors{T}}) where T <: UniversalRingElem{<:MPolyRingElem} + return Vector{Int} +end + +function Base.eltype(::Type{UnivPolyMonomials{T}}) where T <: UniversalRingElem{<:MPolyRingElem} + return T +end + +function Base.eltype(::Type{UnivPolyTerms{T}}) where T <: UniversalRingElem{<:MPolyRingElem} + return T +end + +function coefficients(a::UniversalRingElem{<:MPolyRingElem}) + return UnivPolyCoeffs(a) +end + +function exponent_vectors(a::UniversalRingElem{<:MPolyRingElem}) + return UnivPolyExponentVectors(a) +end + +function monomials(a::UniversalRingElem{<:MPolyRingElem}) + return UnivPolyMonomials(a) +end + +function terms(a::UniversalRingElem{<:MPolyRingElem}) + return UnivPolyTerms(a) +end + +############################################################################### +# +# Comparison +# +############################################################################### + +function ==(a::T, b::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + check_parent(a, b) + + # quick check if underlying parents agree + if parent(data(a)) === parent(data(b)) + return data(a) == data(b) + end + + # check for "generators" + fl1, i1 = AbstractAlgebra._is_gen_with_index(data(a)) + fl2, i2 = AbstractAlgebra._is_gen_with_index(data(b)) + if fl1 && fl2 + return i1 == i2 + elseif fl1 != fl2 + return false + end + + if length(a) != length(b) + return false + end + n1 = nvars(parent(data(a))) + n2 = nvars(parent(data(b))) + if n1 == n2 + for (v1, v2) in zip(exponent_vectors(data(a)), exponent_vectors(data(b))) + if v1 != v2 + return false + end + end + elseif n1 > n2 + for (v1, v2) in zip(exponent_vectors(data(a)), exponent_vectors(data(b))) + if v1[1:n2] != v2 || !iszero(v1[n2 + 1:n1]) + return false + end + end + else # n2 > n1 + for (v1, v2) in zip(exponent_vectors(data(a)), exponent_vectors(data(b))) + if v1 != v2[1:n1] || !iszero(v2[n1 + 1:n2]) + return false + end + end + end + for (c1, c2) in zip(coefficients(data(a)), coefficients(data(b))) + if c1 != c2 + return false + end + end + return true +end + +function isless(a::T, b::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + univ_promote!(a, b) + return isless(data(a), data(b)) +end + +############################################################################### +# +# Inflation/deflation +# +############################################################################### + +deflation(p::UniversalRingElem{<:MPolyRingElem}) = deflation(data(p)) + +function deflate(p::T, shift::Vector{Int}, defl::Vector{Int}) where {T <: UniversalRingElem{<:MPolyRingElem}} + S = parent(p) + num = nvars(S) + vlen = length(shift) + vlen != length(defl) && error("Vector lengths do not match") + upgrade!(p) + if vlen < num + shift = vcat(shift, zeros(Int, num - vlen)) + defl = vcat(defl, ones(Int, num - vlen)) + end + return T(deflate(data(p), shift, defl), S) +end + +function deflate(p::UniversalRingElem{<:MPolyRingElem}, defl::Vector{Int}) + return deflate(p, zeros(Int, length(defl)), defl) +end + +function inflate(p::T, shift::Vector{Int}, defl::Vector{Int}) where {T <: UniversalRingElem{<:MPolyRingElem}} + S = parent(p) + num = nvars(S) + vlen = length(shift) + vlen != length(defl) && error("Vector lengths do not match") + upgrade!(p) + if vlen < num + shift = vcat(shift, zeros(Int, num - vlen)) + defl = vcat(defl, ones(Int, num - vlen)) + end + return T(inflate(data(p), shift, defl), S) +end + +function inflate(p::UniversalRingElem{<:MPolyRingElem}, defl::Vector{Int}) + return inflate(p, zeros(Int, length(defl)), defl) +end + +############################################################################### +# +# Derivative +# +############################################################################### + +function derivative(p::T, j::Int) where {T <: UniversalRingElem{<:MPolyRingElem}} + j > nvars(parent(p)) && error("No such variable") + if j > nvars(parent(data(p))) + return zero(parent(p)) + end + return T(derivative(data(p), j), parent(p)) +end + +function derivative(p::T, x::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + return derivative(p, var_index(x)) +end + +############################################################################### +# +# Remove and valuation +# +############################################################################### + +function remove(z::T, p::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + univ_promote!(z, p) + val, q = remove(data(z), data(p)) + return val, UniversalRingElem(q, parent(z)) +end + +function valuation(z::T, p::T) where {T <: UniversalRingElem{<:MPolyRingElem}} + v, _ = remove(z, p) + return v +end + ############################################################################### # # Data type and parent object methods @@ -21,11 +484,10 @@ end universal_poly_type(::S) where S<:Ring The type of universal polynomials with coefficients of type `T` respectively `elem_type(S)`. -Falls back to `Generic.UnivPoly{T}`. See also [`universal_poly_ring_type`](@ref), [`mpoly_type`](@ref) and [`mpoly_ring_type`](@ref). """ -universal_poly_type(::Type{T}) where T<:RingElement = Generic.UnivPoly{T} +universal_poly_type(::Type{T}) where T<:RingElement = UniversalRingElem{mpoly_type(T), T} universal_poly_type(::Type{S}) where S<:Ring = universal_poly_type(elem_type(S)) universal_poly_type(x) = universal_poly_type(typeof(x)) # to stop this method from eternally recursing on itself, we better add ... universal_poly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype of `RingElement`.")) @@ -50,7 +512,47 @@ universal_poly_ring_type(x) = parent_type(universal_poly_type(x)) # ############################################################################### -function (a::UniversalPolyRingElem)(;kwargs...) +function evaluate(a::UniversalRingElem{<:MPolyRingElem}, A::Vector{<:NCRingElement}) + isempty(A) && error("Evaluating at an empty list of values is not allowed") + a2 = data(a) + varidx = var_indices(a2) + isempty(varidx) && return constant_coefficient(a2) # TODO: this is weird + vals = [zero(parent(A[1])) for _ in 1:nvars(parent(a2))] + n = length(A) + for i in varidx + i <= n || error("Number of variables does not match number of values") + vals[i] = A[i] + end + return evaluate(a2, vals) +end + +function (a::UniversalRingElem{<:MPolyRingElem})(val::T, vals::T...) where T <: NCRingElement + return evaluate(a, [val, vals...]) +end + +function evaluate(a::UniversalRingElem{T, U}, vars::Vector{Int}, vals::Vector{<:RingElement}) where {T <: MPolyRingElem, U} + length(vars) != length(vals) && error("Numbers of variables and values do not match") + vars2 = Vector{Int}(undef, 0) + vals2 = Vector{T}(undef, 0) + num = nvars(parent(data(a))) + S = parent(a) + upgrade!(a) + a2 = data(a) + for i = 1:length(vars) + if vars[i] <= num + push!(vars2, vars[i]) + push!(vals2, data(S(vals[i]))) + end + end + return UniversalRingElem{T, U}(evaluate(a2, vars2, vals2), S) +end + +function evaluate(a::T, vars::Vector{T}, vals::Vector{<:RingElement}) where {T <: UniversalRingElem{<:MPolyRingElem}} + varidx = Int[var_index(x) for x in vars] + return evaluate(a, varidx, vals) +end + +function (a::UniversalRingElem{<:MPolyRingElem})(;kwargs...) ss = symbols(parent(a)) vars = Int[] vals = RingElement[] @@ -65,48 +567,102 @@ end ############################################################################### # -# Iterators +# Univariate polynomials # ############################################################################### -function coefficients(a::UniversalPolyRingElem) - return Generic.UnivPolyCoeffs(a) +function to_univariate(R::AbstractAlgebra.PolyRing{T}, p::UniversalRingElem{<:MPolyRingElem, T}) where T + return to_univariate(R, data(p)) +end + +to_univariate(p::UniversalRingElem{<:MPolyRingElem}) = to_univariate(data(p)) + +is_univariate(p::UniversalRingElem{<:MPolyRingElem}) = is_univariate(data(p)) + +is_univariate_with_data(p::UniversalRingElem{<:MPolyRingElem}) = is_univariate_with_data(data(p)) + +is_univariate(R::UniversalRing{<:MPolyRingElem}) = is_univariate(base_ring(R)) + +function coefficients_of_univariate(p::UniversalRingElem{<:MPolyRingElem}, check_univariate::Bool=true) + return coefficients_of_univariate(data(p), check_univariate) end -function exponent_vectors(a::UniversalPolyRingElem) - return Generic.UnivPolyExponentVectors(a) +################################################################################ +# +# Change base ring +# +################################################################################ + +_change_univ_poly_ring(R, Rx, cached::Bool) = universal_polynomial_ring(R, symbols(Rx); internal_ordering=internal_ordering(Rx), cached)[1] + +function change_base_ring(R::Ring, p::UniversalRingElem{<:MPolyRingElem, T}; cached::Bool=true, parent::UniversalRing{<:MPolyRingElem} = _change_univ_poly_ring(R, parent(p), cached)) where T + upgrade!(p) + return UniversalRingElem(change_base_ring(R, data(p); parent = base_ring(parent)), parent) end -function monomials(a::UniversalPolyRingElem) - return Generic.UnivPolyMonomials(a) +function change_coefficient_ring(R::Ring, p::UniversalRingElem{<:MPolyRingElem, T}; cached::Bool=true, parent::UniversalRing{<:MPolyRingElem} = _change_univ_poly_ring(R, parent(p), cached)) where T + return change_base_ring(R, p, cached = cached, parent = parent) end -function terms(a::UniversalPolyRingElem) - return Generic.UnivPolyTerms(a) +################################################################################ +# +# Map +# +################################################################################ + +function map_coefficients(f::Any, p::UniversalRingElem{<:MPolyRingElem}; cached::Bool=true, parent::UniversalRing{<:MPolyRingElem} = _change_univ_poly_ring(parent(f(zero(coefficient_ring(p)))), parent(p), cached)) + upgrade!(p) + return UniversalRingElem(map_coefficients(f, data(p); parent = base_ring(parent)), parent) end ############################################################################### # -# Factorization +# MPolyBuildCtx # ############################################################################### -function _wrap_factorization(f::Fac{<:MPolyRingElem}, S::UniversalPolyRing) - res = Fac{elem_type(S)}() - res.unit = Generic.UnivPoly(f.unit, S) - for (fact, expo) in f - mulpow!(res, Generic.UnivPoly(fact, S), expo) - end - return res +function sort_terms!(p::UniversalRingElem{<:MPolyRingElem}) + p.data = sort_terms!(data(p)) + return p +end + +function combine_like_terms!(p::UniversalRingElem{<:MPolyRingElem}) + p.data = combine_like_terms!(data(p)) + return p +end + +############################################################################### +# +# Unsafe functions +# +############################################################################### + +function fit!(a::UniversalRingElem{<:MPolyRingElem}, n::Int) + fit!(data(a), n) end -factor_squarefree(f::UniversalPolyRingElem) = _wrap_factorization(factor_squarefree(data(f)), parent(f)) +############################################################################### +# +# Parent object overload +# +############################################################################### -factor(f::UniversalPolyRingElem) = _wrap_factorization(factor(data(f)), parent(f)) +function (a::UniversalRing{<:MPolyRingElem, T})(b::Vector{T}, m::Vector{Vector{Int}}) where T + if length(m) != 0 + len = length(m[1]) + num = nvars(base_ring(a)) + if len != num + for i = 1:length(m) + m[i] = vcat(m[i], zeros(Int, num - len)) + end + end + end + return UniversalRingElem(base_ring(a)(b, m), a) +end ############################################################################### # -# universal_polynomial_ring constructors +# constructors # ############################################################################### @@ -120,11 +676,17 @@ a tuple `S, [x1, x2, ...]` of the universal polynomial ring `S = R[x1, x2, \dots If `varnames` is omitted, return an object representing the universal polynomial ring `S = R[\ldots]` with no variables in it initially. +By default (`cached=true`), the output `S` will be cached, i.e. if `universal_polynomial_ring` +is invoked again with the same coefficient ring `R` and monomial ordering `internal_ordering`, +the same (*identical*) ring is returned. Keep in mind that this ring might already have more +variables than supplied in `varnames`. Setting `cached` to `false` ensures a distinct new +ring is returned, and will also prevent it from being cached. + # Examples ```jldoctest julia> S, (x,y) = universal_polynomial_ring(ZZ, [:x,:y]) -(Universal Polynomial Ring over Integers, AbstractAlgebra.Generic.UnivPoly{BigInt}[x, y]) +(Universal polynomial ring over Integers, UniversalRingElem{AbstractAlgebra.Generic.MPoly{BigInt}, BigInt}[x, y]) julia> z = gen(S, :z) z @@ -133,13 +695,13 @@ julia> x*y - z x*y - z julia> S = universal_polynomial_ring(ZZ) -Universal Polynomial Ring over Integers +Universal polynomial ring over Integers julia> x = gen(S, :x) x julia> y, z = gens(S, [:y, :z]) -2-element Vector{AbstractAlgebra.Generic.UnivPoly{BigInt}}: +2-element Vector{UniversalRingElem{AbstractAlgebra.Generic.MPoly{BigInt}, BigInt}}: y z @@ -149,13 +711,15 @@ x*y - z """ function universal_polynomial_ring(R::Ring, varnames::Vector{Symbol}; cached::Bool=true, internal_ordering::Symbol=:lex) @req !is_trivial(R) "Zero rings are currently not supported as coefficient ring." - T = elem_type(R) - U = mpoly_type(R) - - S = Generic.UniversalPolyRing{T}(R, varnames, internal_ordering, cached) - return (S, gens(S)) + S = get_cached!(UniversalPolyRingID, (R, internal_ordering), cached) do + P = poly_ring(R, varnames; internal_ordering) + UniversalRing(P) + end + return (S, gens(S, varnames)) end +const UniversalPolyRingID = CacheDictType{Tuple{Ring, Symbol}, Ring}() + function universal_polynomial_ring(R::Ring; cached::Bool=true, internal_ordering::Symbol=:lex) return universal_polynomial_ring(R, Symbol[]; internal_ordering, cached)[1] end diff --git a/src/UniversalLaurentPoly.jl b/src/UniversalLaurentPoly.jl new file mode 100644 index 0000000000..6a4f0bfd3b --- /dev/null +++ b/src/UniversalLaurentPoly.jl @@ -0,0 +1,78 @@ +############################################################################### +# +# UniversalLaurentPoly.jl : Universal laurent polynomial ring (variables can be added) +# +############################################################################### + +############################################################################### +# +# String I/O +# +############################################################################### + +universal_ring_name(R::UniversalRing{<:LaurentMPolyRingElem}) = "Laurent polynomial ring" + +############################################################################### +# +# constructors +# +############################################################################### + +@doc raw""" + universal_laurent_polynomial_ring(R::Ring, varnames::Vector{Symbol}; cached::Bool=true) + universal_laurent_polynomial_ring(R::Ring; cached::Bool=true) + +Given a coefficient ring `R` and variable names, say `varnames = [:x1, :x2, ...]`, return +a tuple `S, [x1, x2, ...]` of the universal Laurent polynomial ring `S = R[x1, x2, \dots]` and its generators `x1, x2, \dots`. + +If `varnames` is omitted, return an object representing +the universal Laurent polynomial ring `S = R[\ldots]` with no variables in it initially. + +By default (`cached=true`), the output `S` will be cached, i.e. if `universal_laurent_polynomial_ring` +is invoked again with the same coefficient ring `R`, the same (*identical*) ring is returned. Keep in mind that +this ring might already have more variables than supplied in `varnames`. Setting `cached` to `false` +ensures a distinct new ring is returned, and will also prevent it from being cached. + +# Examples + +```jldoctest +julia> S, (x,y) = universal_laurent_polynomial_ring(ZZ, [:x,:y]) +(Universal Laurent polynomial ring over Integers, UniversalRingElem{AbstractAlgebra.Generic.LaurentMPolyWrap{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}, AbstractAlgebra.Generic.LaurentMPolyWrapRing{BigInt, AbstractAlgebra.Generic.MPolyRing{BigInt}}}, BigInt}[x, y]) + +julia> z = gen(S, :z) +z + +julia> x*y - z +x*y - z + +julia> S = universal_laurent_polynomial_ring(ZZ) +Universal Laurent polynomial ring over Integers + +julia> x = gen(S, :x) +x + +julia> y, z = gens(S, [:y, :z]) +2-element Vector{UniversalRingElem{AbstractAlgebra.Generic.LaurentMPolyWrap{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}, AbstractAlgebra.Generic.LaurentMPolyWrapRing{BigInt, AbstractAlgebra.Generic.MPolyRing{BigInt}}}, BigInt}}: + y + z + +julia> x^(-1)*y - z +-z + x^-1*y +``` +""" +function universal_laurent_polynomial_ring(R::Ring, varnames::Vector{Symbol}; cached::Bool=true) + @req !is_trivial(R) "Zero rings are currently not supported as coefficient ring." + S = get_cached!(UniversalLaurentPolyRingID, R, cached) do + P, _ = laurent_polynomial_ring(R, varnames; cached = false) + UniversalRing(P) + end + return (S, gens(S, varnames)) +end + +const UniversalLaurentPolyRingID = CacheDictType{Ring, Ring}() + +function universal_laurent_polynomial_ring(R::Ring; cached::Bool=true) + return universal_laurent_polynomial_ring(R, Symbol[]; cached)[1] +end + +@varnames_interface universal_laurent_polynomial_ring(R::Ring, s) diff --git a/src/UniversalRing.jl b/src/UniversalRing.jl new file mode 100644 index 0000000000..28dd064a7c --- /dev/null +++ b/src/UniversalRing.jl @@ -0,0 +1,505 @@ +############################################################################### +# +# UniversalRing.jl : Universal ring (variables can be added) +# +############################################################################### + +############################################################################### +# +# Data type and parent object methods +# +############################################################################### + +base_ring_type(::Type{UniversalRing{T, U}}) where {T, U} = parent_type(T) +base_ring(S::UniversalRing) = S.base_ring::base_ring_type(S) + +coefficient_ring_type(::Type{UniversalRing{T, U}}) where {T, U} = parent_type(U) +coefficient_ring(S::UniversalRing) = coefficient_ring(base_ring(S))::coefficient_ring_type(S) + +is_domain_type(::Type{<:UniversalRingElem{T}}) where T = is_domain_type(T) + +is_exact_type(::Type{<:UniversalRingElem{T}}) where T = is_exact_type(T) + +parent(p::UniversalRingElem) = p.parent::parent_type(p) + +elem_type(::Type{UniversalRing{T, U}}) where {T, U} = UniversalRingElem{T, U} + +parent_type(::Type{UniversalRingElem{T, U}}) where {T, U} = UniversalRing{T, U} + +number_of_variables(S::UniversalRing) = number_of_variables(base_ring(S)) + +number_of_generators(S::UniversalRing) = number_of_generators(base_ring(S)) + +symbols(S::UniversalRing) = symbols(base_ring(S)) + +data(p::UniversalRingElem) = p.data + +############################################################################### +# +# Basic manipulation +# +############################################################################### + +zero(R::UniversalRing{T, U}) where {T, U} = UniversalRingElem{T, U}(zero(base_ring(R)), R) + +one(R::UniversalRing{T, U}) where {T, U} = UniversalRingElem{T, U}(one(base_ring(R)), R) + +iszero(p::UniversalRingElem) = iszero(data(p)) + +isone(p::UniversalRingElem) = isone(data(p)) + +is_unit(p::UniversalRingElem) = is_unit(data(p)) + +is_zero_divisor(p::UniversalRingElem) = is_zero_divisor(data(p)) + +function is_zero_divisor_with_annihilator(p::UniversalRingElem{T, U}) where {T, U} + f, b = is_zero_divisor_with_annihilator(data(p)) + return f, UniversalRingElem{T, U}(b, parent(p)) +end + +is_gen(p::UniversalRingElem) = is_gen(data(p)) + +function _ensure_variables(S::UniversalRing, v::Vector{<:VarName}) + idx = Int[] + current_symbols = symbols(S) + n = length(current_symbols) + added_symbols = Symbol[] + for s_ in v + s = Symbol(s_) + i = findfirst(==(s), current_symbols) + if i === nothing + push!(added_symbols, s) + push!(idx, n+length(added_symbols)) + else + push!(idx, i) + end + end + if !isempty(added_symbols) + S.base_ring = AbstractAlgebra._add_gens(base_ring(S), added_symbols) + end + return idx +end + +function gen(S::UniversalRing, s::VarName) + i = findfirst(==(Symbol(s)), symbols(S)) + if i === nothing + S.base_ring = AbstractAlgebra._add_gens(base_ring(S), [Symbol(s)]) + i = length(symbols(S)) + end + return @inbounds gen(S, i) +end + +function gen(S::UniversalRing{T, U}, i::Int) where {T, U} + @boundscheck 1 <= i <= nvars(S) || throw(ArgumentError("generator index out of range")) + return UniversalRingElem{T, U}(gen(base_ring(S), i), S) +end + +function gens(S::UniversalRing{T, U}) where {T, U} + return [UniversalRingElem{T, U}(g, S) for g in gens(base_ring(S))] +end + +# HACK: we abuse the @varnames_interface macro to teach gens for UniversalRing +# some superpowers +function _univ_gens(S::UniversalRing{T, U}, vars::Vector{Symbol}) where {T, U} + idx = _ensure_variables(S, vars) + # TRICK: @varnames_interface expects two return values, but we only care + # for the second; so just return literally nothing for the first + return nothing, [UniversalRingElem{T, U}(gen(base_ring(S), i), S) for i in idx] +end + +AbstractAlgebra.@varnames_interface _univ_gens(R::UniversalRing{T, U}, s) where {T, U} + +function gens(S::UniversalRing, a::AbstractAlgebra.VarNames, as::AbstractAlgebra.VarNames...) + res = _univ_gens(S, a, as...) + length(res) == 2 && return res[2] # special case for improved backwards compatibility + return res[2:end] +end + +canonical_unit(p::UniversalRingElem) = canonical_unit(data(p)) + +characteristic(R::UniversalRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::UniversalRing) = is_known(characteristic, base_ring(R)) + +Base.hash(p::UniversalRingElem, h::UInt) = h + +function deepcopy_internal(p::UniversalRingElem, dict::IdDict) + return UniversalRingElem(deepcopy_internal(data(p), dict), parent(p)) +end + +Base.copy(f::UniversalRingElem) = UniversalRingElem(copy(data(f)), parent(f)) + +############################################################################### +# +# String I/O +# +############################################################################### + +universal_ring_name(R::UniversalRing) = "ring" + +function show(io::IO, R::UniversalRing) + @show_name(io, R) + @show_special(io, R) + print(io, "Universal $(universal_ring_name(R)) over ") + show(io, coefficient_ring(R)) +end + +function expressify(a::UniversalRingElem; context = nothing) + return expressify(data(a), context = context) +end + +@enable_all_show_via_expressify UniversalRingElem + +############################################################################### +# +# Unary operations +# +############################################################################### + +function -(p::UniversalRingElem) + return UniversalRingElem(-data(p), parent(p)) +end + +############################################################################### +# +# Binary operations +# +############################################################################### + +function univ_promote!(x::T, y::T) where {T <: UniversalRingElem} + check_parent(x, y) + nx = nvars(parent(data(x))) + ny = nvars(parent(data(y))) + if nx == ny + return x, y + end + return upgrade!(x), upgrade!(y) +end + +function +(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(data(a) + data(b), parent(a)) +end + +function -(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(data(a) - data(b), parent(a)) +end + +function *(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(data(a)*data(b), parent(a)) +end + +############################################################################### +# +# Square root +# +############################################################################### + +function Base.sqrt(p::T; check::Bool=true) where {T <: UniversalRingElem} + S = parent(p) + s = sqrt(data(p); check=check) + return T(s, S) +end + +is_square(p::UniversalRingElem) = is_square(data(p)) + +############################################################################### +# +# Ad hoc arithmetic functions +# +############################################################################### + +for op in (:+, :-, :*) + @eval begin + function $op(p::T, n::JuliaRingElement) where {T <: UniversalRingElem} + S = parent(p) + return T($op(data(p),n), S) + end + + function $op(p::UniversalRingElem{T, U}, n::U) where {T<:RingElem, U<:RingElem} + S = parent(p) + return UniversalRingElem{T, U}($op(data(p),n), S) + end + + function $op(n::JuliaRingElement, p::T) where {T <: UniversalRingElem} + S = parent(p) + return T($op(n,data(p)), S) + end + + function $op(n::U, p::UniversalRingElem{T, U}) where {T<:RingElem, U<:RingElem} + S = parent(p) + return UniversalRingElem{T, U}($op(n,data(p)), S) + end + end +end + +function divexact(p::T, n::JuliaRingElement; check::Bool=true) where {T <: UniversalRingElem} + S = parent(p) + return T(divexact(data(p), n; check=check), S) +end + +function divexact(p::UniversalRingElem{T, U}, n::U; check::Bool=true) where {T<:RingElem, U<:RingElem} + S = parent(p) + return UniversalRingElem{T, U}(divexact(data(p), n; check=check), S) +end + +############################################################################### +# +# Comparison +# +############################################################################### + +function ==(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return data(a) == data(b) +end + +############################################################################### +# +# Ad hoc comparison functions +# +############################################################################### + +==(p::UniversalRingElem, n::JuliaRingElement) = data(p) == n +==(p::UniversalRingElem{T, U}, n::U) where {T<:RingElem, U<:RingElem} = data(p) == n + +==(n::JuliaRingElement, p::UniversalRingElem) = data(p) == n +==(n::U, p::UniversalRingElem{T, U}) where {T<:RingElem, U<:RingElem} = data(p) == n + +############################################################################### +# +# Powering +# +############################################################################### + +function ^(p::T, b::Int) where {T <: UniversalRingElem} + S = parent(p) + return T(data(p)^b, S) +end + +############################################################################### +# +# Exact division +# +############################################################################### + +function divexact(a::T, b::T; check::Bool=true) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(divexact(data(a), data(b); check=check), parent(a)) +end + +function divides(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + flag, q = divides(data(a), data(b)) + return flag, T(q, parent(a)) +end + +############################################################################### +# +# Euclidean division +# +############################################################################### + +function Base.div(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(div(data(a), data(b)), parent(a)) +end + +function Base.divrem(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + q, r = divrem(data(a), data(b)) + return T(q, parent(a)), T(r, parent(a)) +end + +############################################################################### +# +# GCD +# +############################################################################### + +function gcd(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(gcd(data(a), data(b)), parent(a)) +end + +function lcm(a::T, b::T) where {T <: UniversalRingElem} + univ_promote!(a, b) + return T(lcm(data(a), data(b)), parent(a)) +end + +############################################################################### +# +# Random elements +# +############################################################################### + +RandomExtensions.maketype(S::UniversalRing, _...) = elem_type(S) + +function RandomExtensions.make(S::UniversalRing, vs...) + return Make(S, make(base_ring(S), vs...)) +end + +function rand(rng::AbstractRNG, sp::SamplerTrivial{<:Make2{ + <:RingElement, <:UniversalRing}}) + S, v = sp[][1:end] + return UniversalRingElem(rand(rng, v), S) +end + +function rand(rng::AbstractRNG, S::UniversalRing, v...) + return rand(rng, make(S, v...)) +end + +function rand(S::UniversalRing, v...) + return rand(Random.default_rng(), S, v...) +end + +############################################################################### +# +# Unsafe functions +# +############################################################################### + +function zero!(a::UniversalRingElem) + a.data = zero!(a.data) + return a +end + +function one!(a::UniversalRingElem) + a.data = one!(a.data) + return a +end + +function neg!(z::T, a::T) where {T <: UniversalRingElem} + if parent(data(z)) == parent(data(a)) + z.data = neg!(z.data, a.data) + else + z.data = -a.data + end + return z +end + +function add!(a::T, b::T, c::T) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(b)) == parent(data(c)) + a.data = add!(data(a), data(b), data(c)) + else + a.data = data(b + c) + end + return a +end + +function add!(a::T, b::T, c::RingElement) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(b)) + a.data = add!(data(a), data(b), c) + else + a.data = data(b + c) + end + return a +end + +add!(a::T, b::RingElement, c::T) where {T <: UniversalRingElem} = add!(a, c, b) + +function sub!(a::T, b::T, c::T) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(b)) == parent(data(c)) + a.data = sub!(data(a), data(b), data(c)) + else + a.data = data(b - c) + end + return a +end + +function sub!(a::T, b::T, c::RingElement) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(b)) + a.data = sub!(data(a), data(b), c) + else + a.data = data(b - c) + end + return a +end + +function sub!(a::T, b::RingElement, c::T) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(c)) + a.data = sub!(data(a), b, data(c)) + else + a.data = data(b - c) + end + return a +end + +function mul!(a::T, b::T, c::T) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(b)) == parent(data(c)) + a.data = mul!(data(a), data(b), data(c)) + else + a.data = data(b * c) + end + return a +end + +function mul!(a::T, b::T, c::RingElement) where {T <: UniversalRingElem} + if parent(data(a)) == parent(data(b)) + a.data = mul!(data(a), data(b), c) + else + a.data = data(b * c) + end + return a +end + +mul!(a::T, b::RingElement, c::T) where {T <: UniversalRingElem} = mul!(a, c, b) + +############################################################################### +# +# Promotion rules +# +############################################################################### + +promote_rule(::Type{T}, ::Type{T}) where {T <: UniversalRingElem} = T + +function promote_rule(::Type{UniversalRingElem{T, U}}, ::Type{V}) where {T, U, V <: RingElement} + promote_rule(T, V) == T ? UniversalRingElem{T, U} : Union{} +end + +############################################################################### +# +# Parent object overload +# +############################################################################### + +function upgrade!(p::UniversalRingElem) + R = base_ring(parent(p)) + if R != parent(p.data) + p.data = AbstractAlgebra._upgrade(p.data, R) + end + return p +end + +function (a::UniversalRing)() + return UniversalRingElem(base_ring(a)(), a) +end + +function (a::UniversalRing)(b::RingElement) + return UniversalRingElem(base_ring(a)(b), a) +end + +function (S::UniversalRing{T, U})(p::UniversalRingElem{T, U}) where {T<:RingElem, U<:RingElement} + parent(p) !== S && error("Unable to coerce") + return upgrade!(p) +end + +############################################################################### +# +# Factorization +# +############################################################################### + +function _wrap_factorization(f::Fac{T}, S::UniversalRing{T, U}) where {T, U} + res = Fac{UniversalRingElem{T, U}}() + res.unit = UniversalRingElem{T, U}(f.unit, S) + for (fact, expo) in f + mulpow!(res, UniversalRingElem{T, U}(fact, S), expo) + end + return res +end + +factor_squarefree(f::UniversalRingElem) = _wrap_factorization(factor_squarefree(data(f)), parent(f)) + +factor(f::UniversalRingElem) = _wrap_factorization(factor(data(f)), parent(f)) diff --git a/src/exports.jl b/src/exports.jl index eed919a0c9..fc1e5075cd 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -110,6 +110,9 @@ export Strassen export SymmetricGroup export UniversalPolyRing export UniversalPolyRingElem +export UniversalRing +export UniversalRingElem +export UnivPoly export VarName export YoungTableau export ZZ @@ -594,6 +597,7 @@ export truncate export typed_hcat export typed_hvcat export unit +export universal_laurent_polynomial_ring export universal_poly_ring_type export universal_poly_type export universal_polynomial_ring diff --git a/src/fundamental_interface.jl b/src/fundamental_interface.jl index 02652081f5..947de452ae 100644 --- a/src/fundamental_interface.jl +++ b/src/fundamental_interface.jl @@ -150,7 +150,7 @@ julia> coefficient_ring(x^2+1) == QQ true julia> S, (z,w) = universal_polynomial_ring(QQ, [:z,:w]) -(Universal Polynomial Ring over Rationals, AbstractAlgebra.Generic.UnivPoly{Rational{BigInt}}[z, w]) +(Universal polynomial ring over Rationals, UniversalRingElem{AbstractAlgebra.Generic.MPoly{Rational{BigInt}}, Rational{BigInt}}[z, w]) julia> coefficient_ring(S) == QQ true diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 6d447146bb..8336a91e51 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -460,50 +460,6 @@ mutable struct MPolyBuildCtx{T, S} end end -############################################################################### -# -# UniversalPolyRing / UnivPoly -# -############################################################################### - -@attributes mutable struct UniversalPolyRing{T <: RingElement} <: AbstractAlgebra.UniversalPolyRing{T} - base_ring::AbstractAlgebra.MPolyRing{T} - - function UniversalPolyRing{T}( - R::Ring, s::Vector{Symbol}, internal_ordering::Symbol, cached::Bool=true - ) where {T<:RingElement} - @assert elem_type(R) == T - return get_cached!( - UnivPolyID, (R, s, internal_ordering), cached - ) do - new{T}(AbstractAlgebra.poly_ring(R, s; internal_ordering)) - end::UniversalPolyRing{T} - end -end - -const UnivPolyID = CacheDictType{Tuple{Ring, Vector{Symbol}, Symbol}, Ring}() - -mutable struct UnivPoly{T <: RingElement} <: AbstractAlgebra.UniversalPolyRingElem{T} - p::MPolyRingElem{T} - parent::UniversalPolyRing{T} -end - -struct UnivPolyCoeffs{T <: AbstractAlgebra.RingElem} - poly::T -end - -struct UnivPolyExponentVectors{T <: AbstractAlgebra.RingElem} - poly::T -end - -struct UnivPolyTerms{T <: AbstractAlgebra.RingElem} - poly::T -end - -struct UnivPolyMonomials{T <: AbstractAlgebra.RingElem} - poly::T -end - ############################################################################### # # SparsePolyRing / SparsePoly diff --git a/src/generic/LaurentMPoly.jl b/src/generic/LaurentMPoly.jl index 0296424f32..307404f4ba 100644 --- a/src/generic/LaurentMPoly.jl +++ b/src/generic/LaurentMPoly.jl @@ -686,6 +686,34 @@ function inflate(f::LaurentMPolyWrap, vars::Vector{Int}, shifts::Vector{Int}, de return LaurentMPolyWrap(parent(f), inflate(f.mpoly, vars, zeros(Int, length(shifts)), defls), mindegs) end +############################################################################### +# +# Universal laurent polynomial ring methods +# +############################################################################### + +@doc raw""" + _upgrade(p::LaurentMPolyWrap{T}, R::LaurentMPolyRingWrap{T}) where {T} + +Return an element of `R` which is obtained from `p` by mapping the $i$-th variable +of `parent(p)` to the $i$-th variable of `R`. +For this to work, `R` needs to have at least as many variables as `parent(p)`. +""" +function _upgrade(p::LaurentMPolyWrap{T}, R::LaurentMPolyWrapRing{T}) where {T} + n = nvars(R) - nvars(parent(p)) + n < 0 && error("Too few variables") + return LaurentMPolyWrap(R, _upgrade(p.mpoly, R.mpolyring), vcat(p.mindegs, zeros(Int, n))) +end + +@doc raw""" + _add_gens(R::LaurentMPolyWrapRing, varnames::Vector{Symbol}) + +Return a new uncached multivariate Laurent polynomial ring which has the same properties +as `R` but `varnames` as additional generators. +""" +function _add_gens(R::LaurentMPolyWrapRing, varnames::Vector{Symbol}) + return LaurentMPolyWrapRing(_add_gens(R.mpolyring, varnames), false) +end ############################################################################### # diff --git a/src/generic/UnivPoly.jl b/src/generic/UnivPoly.jl deleted file mode 100644 index b1f035f8cc..0000000000 --- a/src/generic/UnivPoly.jl +++ /dev/null @@ -1,1065 +0,0 @@ -############################################################################### -# -# UnivPoly.jl : Universal polynomial ring (variables can be added) -# -############################################################################### - -############################################################################### -# -# Data type and parent object methods -# -############################################################################### - -base_ring_type(::Type{<:UniversalPolyRing{T}}) where T = mpoly_ring_type(T) -base_ring(S::UniversalPolyRing) = S.base_ring::base_ring_type(S) - -coefficient_ring_type(::Type{<:UniversalPolyRing{T}}) where T = parent_type(T) -coefficient_ring(S::UniversalPolyRing) = coefficient_ring(base_ring(S))::coefficient_ring_type(S) - -function is_domain_type(::Type{<:UnivPoly{S}}) where {S <: RingElement} - return is_domain_type(S) -end - -function is_exact_type(::Type{<:UnivPoly{S}}) where {S <: RingElement} - return is_exact_type(S) -end - -parent(p::UnivPoly) = p.parent::parent_type(p) - -elem_type(::Type{UniversalPolyRing{T}}) where {T<:RingElement} = UnivPoly{T} - -parent_type(::Type{UnivPoly{T}}) where {T<:RingElement} = UniversalPolyRing{T} - -number_of_variables(S::UniversalPolyRing) = number_of_variables(base_ring(S)) - -number_of_generators(S::UniversalPolyRing) = number_of_generators(base_ring(S)) - -symbols(S::UniversalPolyRing) = symbols(base_ring(S)) - -internal_ordering(p::UniversalPolyRing) = internal_ordering(base_ring(p)) - -data(p::UnivPoly{T}) where {T<:RingElement} = p.p::mpoly_type(T) - -############################################################################### -# -# Manipulating terms and monomials -# -############################################################################### - -exponent_vector(p::UnivPoly, i::Int) = exponent_vector(data(p), i) - -function exponent(p::UnivPoly, i::Int, j::Int) - if j > nvars(parent(data(p))) && j <= nvars(parent(p)) - return 0 - end - return exponent(data(p), i, j) -end - -function set_exponent_vector!(p::UnivPoly, i::Int, exps::Vector{Int}) - S = parent(p) - len = length(exps) - upgrade!(p) - if len < nvars(S) - exps = vcat(exps, zeros(Int, nvars(S) - len)) - end - p.p = set_exponent_vector!(data(p), i, exps) - return p -end - -function coeff(p::UnivPoly, exps::Vector{Int}) - S = parent(p) - len = length(exps) - n = nvars(parent(data(p))) - if len > n - if !iszero(exps[n + 1:len]) - return coefficient_ring(S)() - end - return coeff(data(p), exps[1:n]) - end - n = nvars(parent(data(p))) - if len < n - exps = vcat(exps, zeros(Int, n - len)) - end - return coeff(data(p), exps) -end - -function setcoeff!(p::UnivPoly, exps::Vector{Int}, c::T) where T <: RingElement - c = coefficient_ring(data(p))(c) - S = parent(p) - len = length(exps) - upgrade!(p) - if len < nvars(S) - exps = vcat(exps, zeros(Int, nvars(S) - len)) - end - p.p = setcoeff!(data(p), exps, c) - return p -end - -function setcoeff!(a::UnivPoly{T}, i::Int, c::RingElement) where {T} - setcoeff!(data(a), i, c) - return a -end - -############################################################################### -# -# Basic manipulation -# -############################################################################### - -zero(R::UniversalPolyRing{T}) where {T} = UnivPoly{T}(zero(base_ring(R)), R) - -one(R::UniversalPolyRing{T}) where {T} = UnivPoly{T}(one(base_ring(R)), R) - -iszero(p::UnivPoly) = iszero(data(p)) - -isone(p::UnivPoly) = isone(data(p)) - -is_unit(p::UnivPoly) = is_unit(data(p)) - -is_zero_divisor(p::UnivPoly) = is_zero_divisor(data(p)) - -function is_zero_divisor_with_annihilator(p::UnivPoly{T}) where {T} - f, b = is_zero_divisor_with_annihilator(data(p)) - return f, UnivPoly{T}(b, parent(p)) -end - -is_gen(p::UnivPoly) = is_gen(data(p)) - -is_homogeneous(p::UnivPoly) = is_homogeneous(data(p)) - -is_monomial(p::UnivPoly) = is_monomial(data(p)) - -is_constant(p::UnivPoly) = is_constant(data(p)) - -is_term(p::UnivPoly) = is_term(data(p)) - -coeff(p::UnivPoly, i::Int) = coeff(data(p), i) - -function coeff(p::UnivPoly{T}, m::UnivPoly{T}) where {T} - p, m = univ_promote(p, m) - return coeff(data(p), data(m)) -end - -function monomial(p::UnivPoly{T}, i::Int) where {T} - S = parent(p) - m = monomial(data(p), i) - return UnivPoly{T}(m, S) -end - -function monomial!(m::UnivPoly{T}, p::UnivPoly{T}, i::Int) where {T} - parent(m) != parent(p) && error("Incompatible monomial") - if parent(data(m)) != parent(data(p)) - m.p = parent(data(p))() - end - m.p = monomial!(data(m), data(p), i) - return m -end - -function term(p::UnivPoly{T}, i::Int) where {T} - S = parent(p) - t = term(data(p), i) - return UnivPoly{T}(t, S) -end - -leading_coefficient(p::UnivPoly) = leading_coefficient(data(p)) - -trailing_coefficient(p::UnivPoly) = trailing_coefficient(data(p)) - -function tail(p::UnivPoly{T}) where {T} - S = parent(p) - return UnivPoly{T}(tail(data(p)), S) -end - -constant_coefficient(p::UnivPoly) = constant_coefficient(data(p)) - -function leading_monomial(p::UnivPoly{T}) where {T} - S = parent(p) - return UnivPoly{T}(leading_monomial(data(p)), S) -end - -function leading_term(p::UnivPoly{T}) where {T} - S = parent(p) - return UnivPoly{T}(leading_term(data(p)), S) -end - -max_fields(p::UnivPoly) = max_fields(data(p)) - -function degree(p::UnivPoly, i::Int) - if i <= nvars(parent(p)) && i > nvars(parent(data(p))) - return 0 - end - return degree(data(p), i) -end - -function degree(f::UnivPoly{T}, x::UnivPoly{T}) where {T} - check_parent(f, x) - return degree(f, var_index(x)) -end - -function degrees(p::UnivPoly) - v = degrees(data(p)) - len = length(v) - num = nvars(parent(p)) - if len < num - v = vcat(v, zeros(Int, num - len)) - end - return v -end - -total_degree(p::UnivPoly) = total_degree(data(p)) - -length(p::UnivPoly) = length(data(p)) - -function _ensure_variables(S::UniversalPolyRing, v::Vector{<:VarName}) - idx = Int[] - current_symbols = symbols(S) - n = length(current_symbols) - added_symbols = Symbol[] - for s_ in v - s = Symbol(s_) - i = findfirst(==(s), current_symbols) - if i === nothing - push!(added_symbols, s) - push!(idx, n+length(added_symbols)) - else - push!(idx, i) - end - end - if !isempty(added_symbols) - S.base_ring = AbstractAlgebra._add_gens(base_ring(S), added_symbols) - end - return idx -end - -function gen(S::UniversalPolyRing, s::VarName) - i = findfirst(==(Symbol(s)), symbols(S)) - if i === nothing - S.base_ring = AbstractAlgebra._add_gens(base_ring(S), [Symbol(s)]) - i = length(symbols(S)) - end - return @inbounds gen(S, i) -end - -function gen(S::UniversalPolyRing{T}, i::Int) where {T} - @boundscheck 1 <= i <= nvars(S) || throw(ArgumentError("generator index out of range")) - return UnivPoly{T}(gen(base_ring(S), i), S) -end - -function gens(S::UniversalPolyRing{T}) where {T} - n = nvars(S) - return UnivPoly{T}[gen(S, i) for i in 1:n] -end - -# HACK: we abuse the @varnames_interface macro to teach gens for UniversalPolyRing -# some superpowers -function _univ_poly_gens(S::UniversalPolyRing{T}, vars::Vector{Symbol}) where {T} - idx = _ensure_variables(S, vars) - # TRICK: @varnames_interface expects two return values, but we only care - # for the second; so just return literally nothing for the first - return nothing, [UnivPoly{T}(gen(base_ring(S), i), S) for i in idx] -end - -AbstractAlgebra.@varnames_interface _univ_poly_gens(R::UniversalPolyRing{T}, s) where {T} - -function gens(S::UniversalPolyRing{T}, a::AbstractAlgebra.VarNames, as::AbstractAlgebra.VarNames...) where {T} - res = _univ_poly_gens(S, a, as...) - length(res) == 2 && return res[2] # special case for improved backwards compatibility - return res[2:end] -end - -var_index(x::UnivPoly) = var_index(data(x)) - -var_indices(p::UnivPoly) = var_indices(data(p)) - -function vars(p::UnivPoly{T}) where {T <: RingElement} - V = vars(data(p)) - S = parent(p) - return [UnivPoly{T}(v, S) for v in V] -end - -canonical_unit(p::UnivPoly) = canonical_unit(data(p)) - -characteristic(R::UniversalPolyRing) = characteristic(coefficient_ring(R)) -is_known(::typeof(characteristic), R::UniversalPolyRing) = is_known(characteristic, coefficient_ring(R)) - -function Base.hash(p::UnivPoly, h::UInt) - b = 0xcf418d4529109236%UInt - for (c, v) in zip(coefficients(data(p)), exponent_vectors(data(p))) - l = length(v) - while l > 0 && iszero(v[l]) - l -= 1 - end - b = xor(b, xor(Base.hash(v[1:l], h), h)) - b = xor(b, xor(hash(c, h), h)) - b = (b << 1) | (b >> (sizeof(Int)*8 - 1)) - end - return b -end - -function deepcopy_internal(p::UnivPoly{T}, dict::IdDict) where {T} - return UnivPoly{T}(deepcopy_internal(data(p), dict), parent(p)) -end - -Base.copy(f::UnivPoly{T}) where {T} = UnivPoly{T}(copy(data(f)), parent(f)) - -############################################################################### -# -# Multivariate coefficients -# -############################################################################### - -function coeff(p::UnivPoly{T}, vars::Vector{Int}, exps::Vector{Int}) where {T} - len = length(vars) - @req len == length(exps) "Number of variables does not match number of exponents" - S = parent(p) - n = nvars(S) - num = nvars(parent(data(p))) - vars2 = Vector{Int}(undef, 0) - exps2 = Vector{Int}(undef, 0) - for i = 1:len - @req 1 <= vars[i] <= nvars(S) "Variable index not in range" - if vars[i] <= num - push!(vars2, vars[i]) - push!(exps2, exps[i]) - elseif exps[i] > 0 - return zero(S) - end - end - return UnivPoly{T}(coeff(data(p), vars2, exps2), S) -end - -function coeff(a::T, vars::Vector{T}, exps::Vector{Int}) where {U, T <: UnivPoly{U}} - varidx = [var_index(x) for x in vars] - return coeff(a, varidx, exps) -end - -############################################################################### -# -# String I/O -# -############################################################################### - -function show(io::IO, R::UniversalPolyRing) - @show_name(io, R) - @show_special(io, R) - print(io, "Universal Polynomial Ring over ") - show(io, coefficient_ring(R)) -end - -function expressify(a::UnivPoly, x = symbols(parent(a)); context = nothing) - return expressify(data(a), x, context = context) -end - -@enable_all_show_via_expressify UnivPoly - -############################################################################### -# -# Unary operations -# -############################################################################### - -function -(p::UnivPoly{T}) where {T} - return UnivPoly{T}(-data(p), parent(p)) -end - -############################################################################### -# -# Binary operations -# -############################################################################### - -function univ_promote(x::UnivPoly{T}, y::UnivPoly{T}) where {T <: RingElement} - check_parent(x, y) - nx = nvars(parent(data(x))) - ny = nvars(parent(data(y))) - if nx == ny - return x, y - end - S = parent(x) - return S(x), S(y) -end - -function +(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - a, b = univ_promote(a, b) - return UnivPoly{T}(data(a) + data(b), parent(a)) -end - -function -(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - a, b = univ_promote(a, b) - return UnivPoly{T}(data(a) - data(b), parent(a)) -end - -function *(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - a, b = univ_promote(a, b) - return UnivPoly{T}(data(a)*data(b), parent(a)) -end - -############################################################################### -# -# Iterators -# -############################################################################### - -function Base.iterate(x::UnivPolyCoeffs) - if length(x.poly) >= 1 - return coeff(x.poly, 1), 1 - else - return nothing - end -end - -function Base.iterate(x::UnivPolyCoeffs, state) - state += 1 - if length(x.poly) >= state - return coeff(x.poly, state), state - else - return nothing - end -end - -function Base.iterate(x::UnivPolyExponentVectors) - if length(x.poly) >= 1 - return exponent_vector(x.poly, 1), 1 - else - return nothing - end -end - -function Base.iterate(x::UnivPolyExponentVectors, state) - state += 1 - if length(x.poly) >= state - return exponent_vector(x.poly, state), state - else - return nothing - end -end - -function Base.iterate(x::UnivPolyTerms) - if length(x.poly) >= 1 - return term(x.poly, 1), 1 - else - return nothing - end -end - -function Base.iterate(x::UnivPolyTerms, state) - state += 1 - if length(x.poly) >= state - return term(x.poly, state), state - else - return nothing - end -end - -function Base.iterate(x::UnivPolyMonomials) - if length(x.poly) >= 1 - return monomial(x.poly, 1), 1 - else - return nothing - end -end - -function Base.iterate(x::UnivPolyMonomials, state) - state += 1 - if length(x.poly) >= state - return monomial(x.poly, state), state - else - return nothing - end -end - -function Base.length(x::Union{UnivPolyCoeffs, UnivPolyExponentVectors, UnivPolyTerms, UnivPolyMonomials}) - return length(x.poly) -end - -function Base.eltype(::Type{UnivPolyCoeffs{T}}) where T <: AbstractAlgebra.UniversalPolyRingElem{S} where S <: RingElement - return S -end - -function Base.eltype(::Type{UnivPolyExponentVectors{T}}) where T <: AbstractAlgebra.UniversalPolyRingElem{S} where S <: RingElement - return Vector{Int} -end - -function Base.eltype(::Type{UnivPolyMonomials{T}}) where T <: AbstractAlgebra.UniversalPolyRingElem{S} where S <: RingElement - return T -end - -function Base.eltype(::Type{UnivPolyTerms{T}}) where T <: AbstractAlgebra.UniversalPolyRingElem{S} where S <: RingElement - return T -end - -############################################################################### -# -# Square root -# -############################################################################### - -function Base.sqrt(p::UnivPoly{T}; check::Bool=true) where {T} - S = parent(p) - s = sqrt(data(p); check=check) - return UnivPoly{T}(s, S) -end - -function is_square(p::UnivPoly) - return is_square(data(p)) -end - -############################################################################### -# -# Ad hoc arithmetic functions -# -############################################################################### - -for op in (:+, :-, :*) - @eval begin - function $op(p::UnivPoly{T}, n::JuliaRingElement) where {T} - S = parent(p) - return UnivPoly{T}($op(data(p),n), S) - end - - function $op(p::UnivPoly{T}, n::T) where {T <: RingElem} - S = parent(p) - return UnivPoly{T}($op(data(p),n), S) - end - - function $op(n::JuliaRingElement, p::UnivPoly{T}) where {T} - S = parent(p) - return UnivPoly{T}($op(n,data(p)), S) - end - - function $op(n::T, p::UnivPoly{T}) where {T <: RingElem} - S = parent(p) - return UnivPoly{T}($op(n,data(p)), S) - end - end -end - -function divexact(p::UnivPoly{T}, n::JuliaRingElement; check::Bool=true) where {T} - S = parent(p) - return UnivPoly{T}(divexact(data(p), n; check=check), S) -end - -function divexact(p::UnivPoly{T}, n::T; check::Bool=true) where {T <: RingElem} - S = parent(p) - return UnivPoly{T}(divexact(data(p), n; check=check), S) -end - -############################################################################### -# -# Comparison -# -############################################################################### - -function ==(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - check_parent(a, b) - - # quick check if underlying parents agree - if parent(data(a)) === parent(data(b)) - return data(a) == data(b) - end - - # check for "generators" - fl1, i1 = AbstractAlgebra._is_gen_with_index(data(a)) - fl2, i2 = AbstractAlgebra._is_gen_with_index(data(b)) - if fl1 && fl2 - return i1 == i2 - elseif fl1 != fl2 - return false - end - - if length(a) != length(b) - return false - end - n1 = nvars(parent(data(a))) - n2 = nvars(parent(data(b))) - if n1 == n2 - for (v1, v2) in zip(exponent_vectors(data(a)), exponent_vectors(data(b))) - if v1 != v2 - return false - end - end - elseif n1 > n2 - for (v1, v2) in zip(exponent_vectors(data(a)), exponent_vectors(data(b))) - if v1[1:n2] != v2 || !iszero(v1[n2 + 1:n1]) - return false - end - end - else # n2 > n1 - for (v1, v2) in zip(exponent_vectors(data(a)), exponent_vectors(data(b))) - if v1 != v2[1:n1] || !iszero(v2[n1 + 1:n2]) - return false - end - end - end - for (c1, c2) in zip(coefficients(data(a)), coefficients(data(b))) - if c1 != c2 - return false - end - end - return true -end - -function isless(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - check_parent(a, b) - return isless(data(upgrade!(a)), data(upgrade!(b))) -end - -############################################################################### -# -# Ad hoc comparison functions -# -############################################################################### - -==(p::UnivPoly, n::JuliaRingElement) = data(p) == n - -==(n::JuliaRingElement, p::UnivPoly) = data(p) == n - -==(p::UnivPoly{T}, n::T) where {T <: RingElem} = data(p) == n - -==(n::T, p::UnivPoly{T}) where {T <: RingElem} = data(p) == n - -############################################################################### -# -# Powering -# -############################################################################### - -function ^(p::UnivPoly{T}, b::Int) where {T} - S = parent(p) - return UnivPoly{T}(data(p)^b, S) -end - -############################################################################### -# -# Inflation/deflation -# -############################################################################### - -deflation(p::UnivPoly{T}) where {T} = deflation(data(p)) - -function deflate(p::UnivPoly{T}, shift::Vector{Int}, defl::Vector{Int}) where {T} - S = parent(p) - num = nvars(S) - vlen = length(shift) - vlen != length(defl) && error("Vector lengths do not match") - upgrade!(p) - if vlen < num - shift = vcat(shift, zeros(Int, num - vlen)) - defl = vcat(defl, ones(Int, num - vlen)) - end - return UnivPoly{T}(deflate(data(p), shift, defl), S) -end - -function deflate(p::UnivPoly{T}, defl::Vector{Int}) where {T} - return deflate(p, zeros(Int, length(defl)), defl) -end - -function inflate(p::UnivPoly{T}, shift::Vector{Int}, defl::Vector{Int}) where {T} - S = parent(p) - num = nvars(S) - vlen = length(shift) - vlen != length(defl) && error("Vector lengths do not match") - upgrade!(p) - if vlen < num - shift = vcat(shift, zeros(Int, num - vlen)) - defl = vcat(defl, ones(Int, num - vlen)) - end - return UnivPoly{T}(inflate(data(p), shift, defl), S) -end - -function inflate(p::UnivPoly{T}, defl::Vector{Int}) where {T} - return inflate(p, zeros(Int, length(defl)), defl) -end - -############################################################################### -# -# Exact division -# -############################################################################### - -function divexact(a::UnivPoly{T}, b::UnivPoly{T}; check::Bool=true) where {T} - a, b = univ_promote(a, b) - return UnivPoly{T}(divexact(data(a), data(b); check=check), parent(a)) -end - -function divides(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - a, b = univ_promote(a, b) - flag, q = divides(data(a), data(b)) - return flag, UnivPoly{T}(q, parent(a)) -end - -############################################################################### -# -# Euclidean division -# -############################################################################### - -function Base.div(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - a, b = univ_promote(a, b) - return UnivPoly{T}(div(data(a), data(b)), parent(a)) -end - -function Base.divrem(a::UnivPoly{T}, b::UnivPoly{T}) where {T} - a, b = univ_promote(a, b) - q, r = divrem(data(a), data(b)) - return UnivPoly{T}(q, parent(a)), UnivPoly{T}(r, parent(a)) -end - -############################################################################### -# -# Derivative -# -############################################################################### - -function derivative(p::UnivPoly{T}, j::Int) where {T} - j > nvars(parent(p)) && error("No such variable") - if j > nvars(parent(data(p))) - return zero(parent(p)) - end - return UnivPoly{T}(derivative(data(p), j), parent(p)) -end - -function derivative(p::UnivPoly{T}, x::UnivPoly{T}) where {T} - return derivative(p, var_index(x)) -end - -############################################################################### -# -# Remove and valuation -# -############################################################################### - -function remove(z::UnivPoly{T}, p::UnivPoly{T}) where {T} - z, p = univ_promote(z, p) - val, q = remove(data(z), data(p)) - return val, UnivPoly{T}(q, parent(z)) -end - -function valuation(z::UnivPoly{T}, p::UnivPoly{T}) where {T} - v, _ = remove(z, p) - return v -end - -############################################################################### -# -# Evaluation -# -############################################################################### - -function evaluate(a::UnivPoly, A::Vector{<:NCRingElement}) - isempty(A) && error("Evaluating at an empty list of values is not allowed") - a2 = data(a) - varidx = var_indices(a2) - isempty(varidx) && return constant_coefficient(a2) # TODO: this is weird - vals = [zero(parent(A[1])) for _ in 1:nvars(parent(a2))] - n = length(A) - for i in varidx - i <= n || error("Number of variables does not match number of values") - vals[i] = A[i] - end - return evaluate(a2, vals) -end - -function (a::UnivPoly)(val::T, vals::T...) where T <: NCRingElement - return evaluate(a, [val, vals...]) -end - -function (a::UnivPoly)() - return evaluate(a, Int[]) -end - -function evaluate(a::UnivPoly{T}, vars::Vector{Int}, vals::Vector{V}) where {T <: RingElement, V <: RingElement} - length(vars) != length(vals) && error("Numbers of variables and values do not match") - vars2 = Vector{Int}(undef, 0) - vals2 = Vector{mpoly_type(T)}(undef, 0) - num = nvars(parent(data(a))) - S = parent(a) - upgrade!(a) - a2 = data(a) - for i = 1:length(vars) - if vars[i] <= num - push!(vars2, vars[i]) - push!(vals2, data(S(vals[i]))) - end - end - return UnivPoly(evaluate(a2, vars2, vals2), S) -end - -function evaluate(a::S, vars::Vector{S}, vals::Vector{V}) where {S <: UnivPoly{T}, V <: RingElement} where {T <: RingElement} - varidx = Int[var_index(x) for x in vars] - return evaluate(a, varidx, vals) -end - -########S,(a,b)=QQ[:a,:b]####################################################################### -# -# GCD -# -############################################################################### - -function gcd(a::UnivPoly{T}, b::UnivPoly{T}) where {T <: RingElement} - a, b = univ_promote(a, b) - return UnivPoly{T}(gcd(data(a), data(b)), parent(a)) -end - -function lcm(a::UnivPoly{T}, b::UnivPoly{T}) where {T <: RingElement} - a, b = univ_promote(a, b) - return UnivPoly{T}(lcm(data(a), data(b)), parent(a)) -end - -############################################################################### -# -# Univariate polynomials -# -############################################################################### - -function to_univariate(R::AbstractAlgebra.PolyRing{T}, p::UnivPoly{T}) where {T <: RingElement} - return to_univariate(R, data(p)) -end - -to_univariate(p::UnivPoly) = to_univariate(data(p)) - -is_univariate(p::UnivPoly) = is_univariate(data(p)) - -is_univariate_with_data(p::UnivPoly) = is_univariate_with_data(data(p)) - -is_univariate(R::UniversalPolyRing) = is_univariate(base_ring(R)) - -function coefficients_of_univariate(p::UnivPoly, check_univariate::Bool=true) - return coefficients_of_univariate(data(p), check_univariate) -end - -################################################################################ -# -# Change base ring -# -################################################################################ - -_change_univ_poly_ring(R, Rx, cached::Bool) = universal_polynomial_ring(R, symbols(Rx); internal_ordering=internal_ordering(Rx), cached)[1] - -function change_base_ring(R::Ring, p::UnivPoly{T}; cached::Bool=true, parent::UniversalPolyRing = _change_univ_poly_ring(R, parent(p), cached)) where {T <: RingElement} - upgrade!(p) - return UnivPoly(change_base_ring(R, data(p); parent = base_ring(parent)), parent) -end - -function change_coefficient_ring(R::Ring, p::UnivPoly{T}; cached::Bool=true, parent::UniversalPolyRing = _change_univ_poly_ring(R, parent(p), cached)) where {T <: RingElement} - return change_base_ring(R, p, cached = cached, parent = parent) -end - -################################################################################ -# -# Map -# -################################################################################ - -function map_coefficients(f::T, p::UnivPoly; cached::Bool=true, parent::UniversalPolyRing = _change_univ_poly_ring(parent(f(zero(coefficient_ring(p)))), parent(p), cached)) where T - upgrade!(p) - return UnivPoly(map_coefficients(f, data(p); parent = base_ring(parent)), parent) -end - -############################################################################### -# -# MPolyBuildCtx -# -############################################################################### - -function sort_terms!(p::UnivPoly{T}) where {T} - p.p = sort_terms!(data(p)) - return p -end - -function combine_like_terms!(p::UnivPoly{T}) where {T} - p.p = combine_like_terms!(data(p)) - return p -end - -############################################################################### -# -# Random elements -# -############################################################################### - -RandomExtensions.maketype(S::AbstractAlgebra.UniversalPolyRing, _...) = elem_type(S) - -function RandomExtensions.make(S::AbstractAlgebra.UniversalPolyRing, vs...) - return Make(S, make(base_ring(S), vs...)) -end - -function rand(rng::AbstractRNG, sp::SamplerTrivial{<:Make2{ - <:RingElement, <:AbstractAlgebra.UniversalPolyRing}}) - S, v = sp[][1:end] - return UnivPoly(rand(rng, v), S) -end - -function rand(rng::AbstractRNG, S::AbstractAlgebra.UniversalPolyRing, v...) - return rand(rng, make(S, v...)) -end - -function rand(S::AbstractAlgebra.UniversalPolyRing, v...) - return rand(Random.default_rng(), S, v...) -end - -############################################################################### -# -# Conformance test element generation -# -############################################################################### - -function ConformanceTests.generate_element(R::UniversalPolyRing{EuclideanRingResidueRingElem{BigInt}}) - return rand(R, 0:4, 0:10, -10:10) -end - -############################################################################### -# -# Unsafe functions -# -############################################################################### - -function zero!(a::UnivPoly{T}) where {T <: RingElement} - a.p = zero!(a.p) - return a -end - -function one!(a::UnivPoly{T}) where {T <: RingElement} - a.p = one!(a.p) - return a -end - -function neg!(z::UnivPoly{T}, a::UnivPoly{T}) where {T <: RingElement} - if parent(data(z)) == parent(data(a)) - z.p = neg!(z.p, a.p) - else - z.p = -a.p - end - return z -end - -function fit!(a::UnivPoly, n::Int) - fit!(data(a), n) -end - -function add!(a::UnivPoly{T}, b::UnivPoly{T}, c::UnivPoly{T}) where {T <: RingElement} - if parent(data(a)) == parent(data(b)) == parent(data(c)) - a.p = add!(data(a), data(b), data(c)) - else - a.p = data(b + c) - end - return a -end - -function add!(a::UnivPoly{T}, b::UnivPoly{T}, c::RingElement) where {T <: RingElement} - if parent(data(a)) == parent(data(b)) - a.p = add!(data(a), data(b), c) - else - a.p = data(b + c) - end - return a -end - -add!(a::UnivPoly{T}, b::RingElement, c::UnivPoly{T}) where {T <: RingElement} = add!(a, c, b) - -function sub!(a::UnivPoly{T}, b::UnivPoly{T}, c::UnivPoly{T}) where {T <: RingElement} - if parent(data(a)) == parent(data(b)) == parent(data(c)) - a.p = sub!(data(a), data(b), data(c)) - else - a.p = data(b - c) - end - return a -end - -function sub!(a::UnivPoly{T}, b::UnivPoly{T}, c::RingElement) where {T <: RingElement} - if parent(data(a)) == parent(data(b)) - a.p = sub!(data(a), data(b), c) - else - a.p = data(b - c) - end - return a -end - -function sub!(a::UnivPoly{T}, b::RingElement, c::UnivPoly{T}) where {T <: RingElement} - if parent(data(a)) == parent(data(c)) - a.p = sub!(data(a), b, data(c)) - else - a.p = data(b - c) - end - return a -end - -function mul!(a::UnivPoly{T}, b::UnivPoly{T}, c::UnivPoly{T}) where {T <: RingElement} - if parent(data(a)) == parent(data(b)) == parent(data(c)) - a.p = mul!(data(a), data(b), data(c)) - else - a.p = data(b * c) - end - return a -end - -function mul!(a::UnivPoly{T}, b::UnivPoly{T}, c::RingElement) where {T <: RingElement} - if parent(data(a)) == parent(data(b)) - a.p = mul!(data(a), data(b), c) - else - a.p = data(b * c) - end - return a -end - -mul!(a::UnivPoly{T}, b::RingElement, c::UnivPoly{T}) where {T <: RingElement} = mul!(a, c, b) - -############################################################################### -# -# Promotion rules -# -############################################################################### - -promote_rule(::Type{UnivPoly{T}}, ::Type{UnivPoly{T}}) where {T <: RingElement} = UnivPoly{T} - -function promote_rule(::Type{UnivPoly{T}}, ::Type{V}) where {T <: RingElement, V <: RingElement} - promote_rule(T, V) == T ? UnivPoly{T} : Union{} -end - -############################################################################### -# -# Parent object overload -# -############################################################################### - -function upgrade!(p::UnivPoly) - R = base_ring(parent(p)) - if R != parent(p.p) - p.p = AbstractAlgebra._upgrade(p.p, R) - end - return p -end - -function (a::UniversalPolyRing{T})(b::RingElement) where {T <: RingElement} - return a(coefficient_ring(a)(b)) -end - -function (a::UniversalPolyRing{T})() where {T <: RingElement} - return UnivPoly{T}(base_ring(a)(), a) -end - -function (a::UniversalPolyRing{T})(b::JuliaRingElement) where {T <: RingElement} - return UnivPoly{T}(base_ring(a)(b), a) -end - -function (a::UniversalPolyRing{T})(b::T) where {T <: RingElem} - return UnivPoly{T}(base_ring(a)(b), a) -end - -function (S::UniversalPolyRing{T})(p::UnivPoly{T}) where {T <: RingElement} - parent(p) !== S && error("Unable to coerce") - return upgrade!(p) -end - -function (a::UniversalPolyRing{T})(b::Vector{T}, m::Vector{Vector{Int}}) where {T <: RingElement} - if length(m) != 0 - len = length(m[1]) - num = nvars(base_ring(a)) - if len != num - for i = 1:length(m) - m[i] = vcat(m[i], zeros(Int, num - len)) - end - end - end - return UnivPoly{T}(base_ring(a)(b, m), a) -end diff --git a/src/generic/UniversalRing.jl b/src/generic/UniversalRing.jl new file mode 100644 index 0000000000..f20664a3b6 --- /dev/null +++ b/src/generic/UniversalRing.jl @@ -0,0 +1,13 @@ +############################################################################### +# +# Conformance test element generation +# +############################################################################### + +function ConformanceTests.generate_element(R::UniversalRing{MPoly{EuclideanRingResidueRingElem{BigInt}}}) + return rand(R, 0:4, 0:10, -10:10) +end + +function ConformanceTests.generate_element(R::UniversalRing{<:LaurentMPolyWrap{EuclideanRingResidueRingElem{BigInt}}}) + return rand(R, 0:4, -10:10, -10:10) +end diff --git a/src/generic/imports.jl b/src/generic/imports.jl index 733dc82cfa..be2999f64b 100644 --- a/src/generic/imports.jl +++ b/src/generic/imports.jl @@ -95,8 +95,10 @@ import ..AbstractAlgebra: Perm import ..AbstractAlgebra: Ring import ..AbstractAlgebra: RingElem import ..AbstractAlgebra: RingElement +import ..AbstractAlgebra: _add_gens import ..AbstractAlgebra: _can_solve_with_solution_fflu import ..AbstractAlgebra: _can_solve_with_solution_lu +import ..AbstractAlgebra: _upgrade import ..AbstractAlgebra: add! import ..AbstractAlgebra: addmul! import ..AbstractAlgebra: base_ring @@ -113,6 +115,7 @@ import ..AbstractAlgebra: coefficient_ring import ..AbstractAlgebra: coefficient_ring_type import ..AbstractAlgebra: coefficients import ..AbstractAlgebra: coefficients_of_univariate +import ..AbstractAlgebra: combine_like_terms! import ..AbstractAlgebra: compose import ..AbstractAlgebra: constant_coefficient import ..AbstractAlgebra: content @@ -140,6 +143,7 @@ import ..AbstractAlgebra: exponent_vector! import ..AbstractAlgebra: expressify import ..AbstractAlgebra: factor import ..AbstractAlgebra: factor_squarefree +import ..AbstractAlgebra: fit! import ..AbstractAlgebra: gen import ..AbstractAlgebra: gens import ..AbstractAlgebra: get_cached! @@ -151,6 +155,7 @@ import ..AbstractAlgebra: image import ..AbstractAlgebra: image_fn import ..AbstractAlgebra: inflate import ..AbstractAlgebra: integral +import ..AbstractAlgebra: internal_ordering import ..AbstractAlgebra: inv import ..AbstractAlgebra: inv! import ..AbstractAlgebra: is_constant @@ -159,6 +164,7 @@ import ..AbstractAlgebra: is_domain_type import ..AbstractAlgebra: is_exact_type import ..AbstractAlgebra: is_finite import ..AbstractAlgebra: is_gen +import ..AbstractAlgebra: is_homogeneous import ..AbstractAlgebra: is_monomial import ..AbstractAlgebra: is_nilpotent import ..AbstractAlgebra: is_perfect @@ -183,9 +189,11 @@ import ..AbstractAlgebra: leading_term import ..AbstractAlgebra: log import ..AbstractAlgebra: map_coefficients import ..AbstractAlgebra: matrix +import ..AbstractAlgebra: max_fields import ..AbstractAlgebra: max_precision import ..AbstractAlgebra: minpoly import ..AbstractAlgebra: modulus +import ..AbstractAlgebra: monomial import ..AbstractAlgebra: monomials import ..AbstractAlgebra: monomial! import ..AbstractAlgebra: mul! @@ -218,12 +226,15 @@ import ..AbstractAlgebra: rels import ..AbstractAlgebra: remove import ..AbstractAlgebra: renormalize! import ..AbstractAlgebra: set_coefficient! +import ..AbstractAlgebra: set_exponent_vector! import ..AbstractAlgebra: set_length! import ..AbstractAlgebra: set_precision! import ..AbstractAlgebra: set_valuation! +import ..AbstractAlgebra: setcoeff! import ..AbstractAlgebra: shift_left import ..AbstractAlgebra: shift_right import ..AbstractAlgebra: snf +import ..AbstractAlgebra: sort_terms! import ..AbstractAlgebra: sqrt import ..AbstractAlgebra: sqrt_classical import ..AbstractAlgebra: sqrt_classical_char2 @@ -231,12 +242,14 @@ import ..AbstractAlgebra: sub! import ..AbstractAlgebra: submul! import ..AbstractAlgebra: symbols import ..AbstractAlgebra: tail +import ..AbstractAlgebra: term import ..AbstractAlgebra: term_degree import ..AbstractAlgebra: terms import ..AbstractAlgebra: term! import ..AbstractAlgebra: terms_degrees import ..AbstractAlgebra: terse import ..AbstractAlgebra: to_univariate +import ..AbstractAlgebra: total_degree import ..AbstractAlgebra: trailing_coefficient import ..AbstractAlgebra: transpose import ..AbstractAlgebra: transpose! diff --git a/test/Rings-test.jl b/test/Rings-test.jl index 2084e27500..15f05bd5a4 100644 --- a/test/Rings-test.jl +++ b/test/Rings-test.jl @@ -28,7 +28,7 @@ include("generic/AbsMSeries-test.jl") include("generic/Matrix-test.jl") include("generic/MPoly-test.jl") include("generic/LaurentMPoly-test.jl") -include("generic/UnivPoly-test.jl") +include("UniversalRing-test.jl") include("generic/Localization-test.jl") include("algorithms/MPolyEvaluate-test.jl") include("algorithms/MPolyFactor-test.jl") diff --git a/test/generic/UnivPoly-test.jl b/test/UniversalRing-test.jl similarity index 94% rename from test/generic/UnivPoly-test.jl rename to test/UniversalRing-test.jl index aa14f41ab1..7341d36aae 100644 --- a/test/generic/UnivPoly-test.jl +++ b/test/UniversalRing-test.jl @@ -1,4 +1,4 @@ -@testset "Generic.UnivPoly.constructors" begin +@testset "UnivPoly.constructors" begin for R in [ZZ, QQ] for iters = 1:5 ord = rand_ordering() @@ -25,9 +25,10 @@ y, z = gens(S, ["y", "z"]) @test y == S[2] - @test elem_type(S) == Generic.UnivPoly{elem_type(R)} - @test elem_type(Generic.UniversalPolyRing{elem_type(R)}) == Generic.UnivPoly{elem_type(R)} - @test parent_type(Generic.UnivPoly{elem_type(R)}) == Generic.UniversalPolyRing{elem_type(R)} + @test elem_type(S) == UniversalRingElem{Generic.MPoly{elem_type(R)}, elem_type(R)} + + @test elem_type(UniversalRing{Generic.MPoly{elem_type(R)}, elem_type(R)}) == UniversalRingElem{Generic.MPoly{elem_type(R)}, elem_type(R)} + @test parent_type(UniversalRingElem{Generic.MPoly{elem_type(R)}, elem_type(R)}) == UniversalRing{Generic.MPoly{elem_type(R)}, elem_type(R)} @test elem_type(S) == universal_poly_type(elem_type(R)) @test typeof(S) == universal_poly_ring_type(elem_type(R)) @@ -36,7 +37,8 @@ @test coefficient_ring(S) === R @test coefficient_ring_type(S) === typeof(R) - @test S isa Generic.UniversalPolyRing + @test S isa UniversalPolyRing + @test S isa UniversalRing{Generic.MPoly{elem_type(R)}} @test isa(x, UniversalPolyRingElem) @test isa(y, UniversalPolyRingElem) @@ -78,7 +80,7 @@ @test f1 == f3 - S2, x = universal_polynomial_ring(R, :x => 1:3; internal_ordering=ord) + S2, x = universal_polynomial_ring(R, :x => 1:3; internal_ordering=ord, cached = false) @test length(x) == 3 @test isa(x[1], UniversalPolyRingElem) @@ -103,7 +105,7 @@ @test y != z end -@testset "Generic.UnivPoly.parent_type" begin +@testset "UnivPoly.parent_type" begin for R in [ZZ, QQ] for iters = 1:100 ord = rand_ordering() @@ -154,16 +156,20 @@ end end end +@testset "UniversalLaurentPolynomial.conformance" begin + S = universal_laurent_polynomial_ring(residue_ring(ZZ, ZZ(6))[1]; cached = false) + gen(S, "x") + ConformanceTests.test_Ring_interface(S) +end - -@testset "Generic.UnivPoly.conformance" begin - S = universal_polynomial_ring(residue_ring(ZZ, ZZ(6))[1]) +@testset "UnivPoly.conformance" begin + S = universal_polynomial_ring(residue_ring(ZZ, ZZ(6))[1]; cached = false) gen(S, "x") ConformanceTests.test_Ring_interface(S) end -@testset "Generic.UnivPoly.printing" begin - R = universal_polynomial_ring(QQ) +@testset "UnivPoly.printing" begin + R = universal_polynomial_ring(QQ; cached = false) x = gen(R, "x") @test sprint(show, x+1) == "x + 1" @test sprint(show, -1 + x^2 - 2x + 1) == "x^2 - 2*x" @@ -175,7 +181,7 @@ end @test sprint(show, x*y*(z-1)) == "(x*z - x)*y" end -@testset "Generic.UnivPoly.term_monomial" begin +@testset "UnivPoly.term_monomial" begin for R in [ZZ, QQ] for iters = 1:100 ord = rand_ordering() @@ -237,7 +243,7 @@ end end end -@testset "Generic.UnivPoly.basic_manipulation" begin +@testset "UnivPoly.basic_manipulation" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false) @@ -356,7 +362,7 @@ end end end -@testset "Generic.UnivPoly.multivariate_coefficients" begin +@testset "UnivPoly.multivariate_coefficients" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false) @@ -383,7 +389,7 @@ end end end -@testset "Generic.UnivPoly.unary_operations" begin +@testset "UnivPoly.unary_operations" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -408,7 +414,7 @@ end end end -@testset "Generic.UnivPoly.binary_operations" begin +@testset "UnivPoly.binary_operations" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -434,7 +440,7 @@ end end end -@testset "Generic.UnivPoly.iterators" begin +@testset "UnivPoly.iterators" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false) @@ -461,7 +467,7 @@ end end end -@testset "Generic.UnivPoly.square_root" begin +@testset "UnivPoly.square_root" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -483,7 +489,7 @@ end end end -@testset "Generic.UnivPoly.adhoc_binary_operations" begin +@testset "UnivPoly.adhoc_binary_operations" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -539,7 +545,7 @@ end end end -@testset "Generic.UnivPoly.comparison" begin +@testset "UnivPoly.comparison" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -579,7 +585,7 @@ end end end -@testset "Generic.UnivPoly.adhoc_comparison" begin +@testset "UnivPoly.adhoc_comparison" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -630,7 +636,7 @@ end end end -@testset "Generic.UnivPoly.powering" begin +@testset "UnivPoly.powering" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -658,7 +664,7 @@ end end end -@testset "Generic.UnivPoly.inflation_deflation" begin +@testset "UnivPoly.inflation_deflation" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -703,7 +709,7 @@ end end end -@testset "Generic.UnivPoly.exact_division" begin +@testset "UnivPoly.exact_division" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -762,7 +768,7 @@ end end end -@testset "Generic.UnivPoly.euclidean_division" begin +@testset "UnivPoly.euclidean_division" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -815,7 +821,7 @@ end end end -@testset "Generic.UnivPoly.derivative" begin +@testset "UnivPoly.derivative" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false) @@ -842,7 +848,7 @@ end end end -@testset "Generic.UnivPoly.remove_valuation" begin +@testset "UnivPoly.remove_valuation" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -946,7 +952,7 @@ end end end -@testset "Generic.UnivPoly.evaluation" begin +@testset "UnivPoly.evaluation" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -1011,7 +1017,7 @@ end end end -@testset "Generic.UnivPoly.gcd" begin +@testset "UnivPoly.gcd" begin for R in [ZZ, QQ] for iters = 1:100 S = universal_polynomial_ring(R; cached=false) @@ -1044,14 +1050,14 @@ end @test lcm(h, h) == h*uh - @test content(f) == content(f.p) - @test content(g) == content(g.p) - @test content(h) == content(h.p) + @test content(f) == content(data(f)) + @test content(g) == content(data(g)) + @test content(h) == content(data(h)) end end end -@testset "Generic.UnivPoly.univariate_polynomials" begin +@testset "UnivPoly.univariate_polynomials" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false) @@ -1092,7 +1098,7 @@ end end end -@testset "Generic.UnivPoly.map" begin +@testset "UnivPoly.map" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false) @@ -1127,7 +1133,7 @@ end end end -@testset "Generic.UnivPoly.unsafe_operators" begin +@testset "UnivPoly.unsafe_operators" begin for R in [ZZ, QQ] S = universal_polynomial_ring(R; cached=false)