From 8947539ec80fdc80a71b361c7f1774f0a57e1b61 Mon Sep 17 00:00:00 2001 From: JohnAAbbott <124266874+JohnAAbbott@users.noreply.github.com> Date: Sat, 21 Dec 2024 20:53:41 +0100 Subject: [PATCH] Add generic is_unit and is_nilpotent (#1933) --- src/MPoly.jl | 36 ++++--- src/NCPoly.jl | 31 ++++++ src/NCRings.jl | 23 ++++- src/Poly.jl | 14 +-- src/Residue.jl | 13 +++ src/algorithms/LaurentPoly.jl | 24 ++++- src/generic/LaurentMPoly.jl | 25 +++-- src/generic/LaurentPoly.jl | 9 -- src/generic/imports.jl | 1 + src/julia/Float.jl | 2 +- src/julia/Rational.jl | 2 +- test/generic/LaurentMPoly-test.jl | 155 +++++++++++++++++++++++++++--- test/generic/LaurentPoly-test.jl | 117 +++++++++++++++++++--- test/generic/MPoly-test.jl | 137 +++++++++++++++++++++++--- test/generic/UnivPoly-test.jl | 87 +++++++++++++++++ 15 files changed, 593 insertions(+), 83 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index 52998d17af..8bb557debf 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -425,16 +425,22 @@ end iszero(x::MPolyRingElem{T}) where T <: RingElement = length(x) == 0 -function is_unit(a::MPolyRingElem{T}) where T <: RingElement - if is_constant(a) - return is_unit(leading_coefficient(a)) - elseif is_domain_type(elem_type(coefficient_ring(a))) - return false - elseif length(a) == 1 - return false - else - throw(NotImplementedError(:is_unit, a)) - end +function is_unit(f::T) where {T <: MPolyRingElem} + # for constant polynomials we delegate to the coefficient ring: + is_constant(f) && return is_unit(constant_coefficient(f)) + # Here deg(f) > 0; over an integral domain, non-constant polynomials are never units: + is_domain_type(T) && return false + # A polynomial over a commutative ring is a unit iff its + # constant term is a unit and all other coefficients are nilpotent: + # see e.g. for a proof. + for (c, expv) in zip(coefficients(f), exponent_vectors(f)) + if is_zero(expv) + is_unit(c) || return false + else + is_nilpotent(c) || return false + end + end + return true end function content(a::MPolyRingElem{T}) where T <: RingElement @@ -445,8 +451,16 @@ function content(a::MPolyRingElem{T}) where T <: RingElement return z end + +function is_nilpotent(f::T) where {T <: MPolyRingElem} + is_domain_type(T) && return is_zero(f) + return all(is_nilpotent, coefficients(f)) +end + + function is_zero_divisor(x::MPolyRingElem{T}) where T <: RingElement - return is_zero_divisor(content(x)) + is_domain_type(T) && return is_zero(x) + return is_zero_divisor(content(x)) end function is_zero_divisor_with_annihilator(a::MPolyRingElem{T}) where T <: RingElement diff --git a/src/NCPoly.jl b/src/NCPoly.jl index f83a34ed46..2f036ad43b 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -783,3 +783,34 @@ polynomial_ring_only(R::T, s::Symbol; cached::Bool=true) where T<:NCRing = # Simplified constructor PolyRing(R::NCRing) = polynomial_ring_only(R, :x; cached=false) + + + +############################################################################### +# +# is_unit & is_nilpotent +# +############################################################################### + +# ASSUMES structural interface is analogous to that for univariate polynomials + +# This function handles both PolyRingElem & NCPolyRingElem +function is_unit(f::T) where {T <: PolynomialElem} + # constant coeff must itself be a unit + is_unit(constant_coefficient(f)) || return false + is_constant(f) && return true + # Here deg(f) > 0; over an integral domain, non-constant polynomials are never units: + is_domain_type(T) && return false + for i in 1:degree(f) # we have already checked coeff(f,0) + if !is_nilpotent(coeff(f, i)) + return false + end + end + return true +end + +# This function handles both PolyRingElem & NCPolyRingElem +function is_nilpotent(f::T) where {T <: PolynomialElem} + is_domain_type(T) && return is_zero(f) + return all(is_nilpotent, coefficients(f)) +end diff --git a/src/NCRings.jl b/src/NCRings.jl index b070d16695..523f859d93 100644 --- a/src/NCRings.jl +++ b/src/NCRings.jl @@ -137,7 +137,7 @@ Base.literal_pow(::typeof(^), x::NCRingElem, ::Val{p}) where {p} = x^p ############################################################################### @doc raw""" - is_unit(a::T) where {T <: NCRingElem} + is_unit(a::T) where {T <: NCRingElement} Return true if $a$ is invertible, else return false. @@ -155,6 +155,27 @@ julia> is_unit(ZZ(-1)), is_unit(ZZ(4)) """ function is_unit end +@doc raw""" + is_nilpotent(a::T) where {T <: NCRingElement} + +Return true iff $a$ is nilpotent, i.e. a^k == 0 for some k. + +# Examples +```jldoctest +julia> R, _ = residue_ring(ZZ,720); + +julia> S, x = polynomial_ring(R, :x); + +julia> is_nilpotent(30*x), is_nilpotent(30+90*x), is_nilpotent(S(15)) +(true, true, false) +``` +""" +function is_nilpotent(a::T) where {T <: NCRingElement} + is_domain_type(T) && return is_zero(a) + throw(NotImplementedError(:is_nilpotent, a)) +end + + ############################################################################### # # Characteristic diff --git a/src/Poly.jl b/src/Poly.jl index a612c17f77..deed00fe3a 100644 --- a/src/Poly.jl +++ b/src/Poly.jl @@ -222,17 +222,9 @@ function is_monic(a::PolynomialElem) return isone(leading_coefficient(a)) end -function is_unit(a::PolynomialElem) - if length(a) <= 1 - return is_unit(coeff(a, 0)) - elseif is_domain_type(elem_type(coefficient_ring(a))) - return false - elseif !is_unit(coeff(a, 0)) || is_unit(coeff(a, length(a) - 1)) - return false - else - throw(NotImplementedError(:is_unit, a)) - end -end +# function is_unit(...) see NCPoly.jl +# function is_nilpotent(...) see NCPoly.jl + is_zero_divisor(a::PolynomialElem) = is_zero_divisor(content(a)) diff --git a/src/Residue.jl b/src/Residue.jl index 033e4ed8dd..50a203e3ea 100644 --- a/src/Residue.jl +++ b/src/Residue.jl @@ -85,6 +85,19 @@ function is_unit(a::ResElem) return isone(g) end +function is_nilpotent(res::ResElem) + m = modulus(res) + r = data(res) + while true + g = gcd(r, m) + (g == m) && return true + is_one(g) && return false + m = divexact(m, g) + g = mod(g, m); r = g^2 # if computation domain is limited precision integer then g = mod(g,m) guarantees that g^2 will not overflow! + end +end + + # currently residue rings are only allowed over domains # otherwise this function would be more complicated is_zero_divisor(a::ResElem) = !is_unit(a) diff --git a/src/algorithms/LaurentPoly.jl b/src/algorithms/LaurentPoly.jl index 6f8d3effe0..ada0ca8822 100644 --- a/src/algorithms/LaurentPoly.jl +++ b/src/algorithms/LaurentPoly.jl @@ -138,12 +138,28 @@ function is_monomial_recursive(p::LaurentPolyRingElem) is_monomial_recursive(coeff(p, dr[])) end -function is_unit(p::LaurentPolyRingElem) - dr = degrees_range(p) - length(dr) == 1 || return false - is_unit(coeff(p, dr[])) +function is_unit(f::T) where {T <: LaurentPolyRingElem} + # **NOTE** f.poly is not normalized so that the degree 0 coeff is non-zero + is_trivial(parent(f)) && return true # coeffs in zero ring + unit_seen = false + for i in 0:degree(f.poly) + if is_nilpotent(coeff(f.poly, i)) + continue + end + if unit_seen || !is_unit(coeff(f.poly, i)) + return false + end + unit_seen = true + end + return unit_seen end + +function is_nilpotent(f::T) where {T <: LaurentPolyRingElem} + return is_nilpotent(f.poly); +end + + ############################################################################### # # Comparisons diff --git a/src/generic/LaurentMPoly.jl b/src/generic/LaurentMPoly.jl index 150606a6c2..89db476fc6 100644 --- a/src/generic/LaurentMPoly.jl +++ b/src/generic/LaurentMPoly.jl @@ -109,15 +109,28 @@ function Base.inv(a::LaurentMPolyWrap) return LaurentMPolyWrap(parent(a), inv(ap), neg!(ad, ad)) end -function is_unit(a::LaurentMPolyWrap) - (ap, ad) = _normalize(a) - if is_domain_type(elem_type(coefficient_ring(a))) || length(ap) <= 1 - return is_unit(ap) - else - throw(NotImplementedError(:is_unit, a)) +function is_unit(f::T) where {T <: LaurentMPolyRingElem} + # **NOTE** f.mpoly is not normalized in any way + is_trivial(parent(f)) && return true # coeffs in zero ring + unit_seen = false + for i in 1:length(f.mpoly) + if is_nilpotent(coeff(f.mpoly, i)) + continue end + if unit_seen || !is_unit(coeff(f.mpoly, i)) + return false + end + unit_seen = true + end + return unit_seen end + +function is_nilpotent(f::T) where {T <: LaurentMPolyRingElem} + return is_nilpotent(f.mpoly); +end + + is_zero_divisor(p::LaurentMPolyWrap) = is_zero_divisor(p.mpoly) function is_zero_divisor_with_annihilator(p::LaurentMPolyWrap) diff --git a/src/generic/LaurentPoly.jl b/src/generic/LaurentPoly.jl index ee6e960344..bb8ad7b090 100644 --- a/src/generic/LaurentPoly.jl +++ b/src/generic/LaurentPoly.jl @@ -201,15 +201,6 @@ function Base.inv(p::LaurentPolyWrap) return LaurentPolyWrap(parent(p), inv(g), -p.mindeg-v) end -function is_unit(p::LaurentPolyWrap) - v, g = _remove_gen(p) - if is_domain_type(elem_type(coefficient_ring(p))) || length(g) <= 1 - return is_unit(g) - else - throw(NotImplementedError(:is_unit, p)) - end -end - is_zero_divisor(p::LaurentPolyWrap) = is_zero_divisor(p.poly) function is_zero_divisor_with_annihilator(p::LaurentPolyWrap) diff --git a/src/generic/imports.jl b/src/generic/imports.jl index bddcd93507..39d968ccb8 100644 --- a/src/generic/imports.jl +++ b/src/generic/imports.jl @@ -149,6 +149,7 @@ import ..AbstractAlgebra: is_exact_type import ..AbstractAlgebra: is_finite import ..AbstractAlgebra: is_gen import ..AbstractAlgebra: is_monomial +import ..AbstractAlgebra: is_nilpotent import ..AbstractAlgebra: is_power import ..AbstractAlgebra: is_square import ..AbstractAlgebra: is_square_with_sqrt diff --git a/src/julia/Float.jl b/src/julia/Float.jl index bba5f34cdd..e47145f7a6 100644 --- a/src/julia/Float.jl +++ b/src/julia/Float.jl @@ -38,7 +38,7 @@ zero(::Floats{T}) where T <: AbstractFloat = T(0) one(::Floats{T}) where T <: AbstractFloat = T(1) -is_unit(a::AbstractFloat) = a != 0 +is_unit(a::AbstractFloat) = !is_zero(a) canonical_unit(a::AbstractFloat) = a diff --git a/src/julia/Rational.jl b/src/julia/Rational.jl index 2f0fbc9583..1a7152239e 100644 --- a/src/julia/Rational.jl +++ b/src/julia/Rational.jl @@ -38,7 +38,7 @@ zero(::Rationals{T}) where T <: Integer = Rational{T}(0) one(::Rationals{T}) where T <: Integer = Rational{T}(1) -is_unit(a::Rational) = a != 0 +is_unit(a::Rational) = !is_zero(a) is_zero_divisor(a::Rational) = is_zero(a) diff --git a/test/generic/LaurentMPoly-test.jl b/test/generic/LaurentMPoly-test.jl index ba31815509..213f5f20c9 100644 --- a/test/generic/LaurentMPoly-test.jl +++ b/test/generic/LaurentMPoly-test.jl @@ -50,19 +50,6 @@ end @test !occursin("\n", sprint(show, R)) end -@testset "Generic.LaurentMPoly.is_unit" begin - R, (x,) = laurent_polynomial_ring(residue_ring(ZZ, 6)[1], ["x"]) - - @test is_unit(x) - @test !is_unit(2*x) - try - res = is_unit(3 + 2*x) - @test res - catch e - @test e isa NotImplementedError - end -end - @testset "Generic.LaurentMPoly.derivative" begin L, (x, y) = laurent_polynomial_ring(ZZ, ["x", "y"]) @@ -134,3 +121,145 @@ end p = inv(inv(x)) @test constant_coefficient(p) == 0 end + + +# ------------------------------------------------------- + +# Coeff rings for the tests below +ZeroRing,_ = residue_ring(ZZ,1); +ZZmod720,_ = residue_ring(ZZ, 720); + +# [2024-12-12 laurent_polynomial_ring currently gives error when coeff ring is zero ring] +# ## LaurentMPoly over ZeroRing +# @testset "Nilpotent/unit for ZeroRing[x,y, x^(-1),y^(-1)]" begin +# P,(x,y) = laurent_polynomial_ring(ZeroRing, ["x","y"]); +# @test is_nilpotent(P(0)) +# @test is_nilpotent(P(1)) +# @test is_nilpotent(x) +# @test is_nilpotent(-x) +# @test is_nilpotent(x+y) +# @test is_nilpotent(x-y) +# @test is_nilpotent(x*y) + +# @test is_unit(P(0)) +# @test is_unit(P(1)) +# @test is_unit(x) +# @test is_unit(-x) +# @test is_unit(x+y) +# @test is_unit(x-y) +# @test is_unit(x*y) +# end + +## LaurentMPoly over ZZ +@testset "Nilpotent/unit for ZZ[x,y, x^(-1),y^(-1)]" begin + P,(x,y) = laurent_polynomial_ring(ZZ, ["x","y"]); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test !is_nilpotent(x+y) + @test !is_nilpotent(x-y) + @test !is_nilpotent(x*y) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(-2)) + @test !is_unit(P(-2)) + @test is_unit(x) + @test is_unit(-x) + @test is_unit(1/x) + @test is_unit(-1/x) + @test !is_unit(2/x) + @test !is_unit(-2/x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test !is_unit(x+y) + @test !is_unit(x-y) + @test is_unit(x*y) +end + +## LaurentMPoly over QQ +@testset "Nilpotent/unit for QQ[x,y, x^(-1),y^(-1)]" begin + P,(x,y) = laurent_polynomial_ring(QQ, ["x","y"]); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test !is_nilpotent(x+y) + @test !is_nilpotent(x-y) + @test !is_nilpotent(x*y) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test is_unit(P(2)) + @test is_unit(P(-2)) + @test is_unit(x) + @test is_unit(-x) + @test is_unit(2*x) + @test is_unit(-2*x) + @test is_unit(1/x) + @test is_unit(-1/x) + @test is_unit(2/x) + @test is_unit(-2/x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test !is_unit(x+y) + @test !is_unit(x-y) + @test is_unit(x*y) +end + +## LaurentMPoly over ZZ/720 +@testset "Nilpotent/unit for ZZ/(720)[x,y, x^(-1), y^(-1)]" begin + P,(x,y) = laurent_polynomial_ring(ZZmod720, ["x","y"]); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test is_nilpotent(P(30)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test is_nilpotent(30*x) + @test is_nilpotent(30/x) + @test is_nilpotent(30*x+120*y) + @test is_nilpotent(30*x-120*y) + @test !is_nilpotent(x*y) + @test is_nilpotent(30*x*y) + @test is_nilpotent(30*x/y) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(2)) + @test !is_unit(P(-2)) + @test is_unit(P(7)) + @test is_unit(P(-7)) + @test is_unit(x) + @test is_unit(-x) + @test !is_unit(35*x) + @test !is_unit(35/x) + @test !is_unit(30*x) + @test !is_unit(30/x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test is_unit(x+30) + @test is_unit(x-30) + @test is_unit(1+30*x) + @test is_unit(1-30*x) + @test is_unit(7+60*x) + @test is_unit(7-60*x) + @test is_unit(600+7*x+30*x^2) + @test is_unit(600-7*x+30*x^2) + @test is_unit(x+30/y) + @test is_unit(x-30/y) + @test is_unit(1+30*x/y) + @test is_unit(1-30*x*y) + @test is_unit(7+60*x+210/y) + @test is_unit(7-60*x+210/y) + @test is_unit(600+7*x/y+30*x^2) + @test is_unit(600-7*x*y+30*x^2) + @test !is_unit(30*x+120*y) + @test !is_unit(30*x-120*y) + @test is_unit(x*y) + @test !is_unit(30*x*y) + @test !is_unit(30*x/y) +end diff --git a/test/generic/LaurentPoly-test.jl b/test/generic/LaurentPoly-test.jl index 168dc1e261..98b3cd5708 100644 --- a/test/generic/LaurentPoly-test.jl +++ b/test/generic/LaurentPoly-test.jl @@ -232,19 +232,6 @@ end @test !is_divisible_by(3*y+4, 2+3*y^-1) end - @testset "Generic.LaurentMPoly.is_unit" begin - R, x = laurent_polynomial_ring(residue_ring(ZZ, 6)[1], "x") - - @test is_unit(x) - @test !is_unit(2*x) - try - res = is_unit(3 + 2*x) - @test res - catch e - @test e isa NotImplementedError - end - end - @testset "coercion" begin R, x = polynomial_ring(ZZ, "x") L, x1 = laurent_polynomial_ring(ZZ, "x") @@ -502,3 +489,107 @@ end test_Ring_interface(L) end end + + +# ------------------------------------------------------- + +# Coeff rings for the tests below +ZeroRing,_ = residue_ring(ZZ,1); +ZZmod720,_ = residue_ring(ZZ, 720); + + +# [2024-12-12 laurent_polynomial_ring currently gives error when coeff ring is zero ring] +# ## LaurentPoly over ZeroRing +# @testset "Nilpotent/unit for ZeroRing[x, x^(-1)]" begin +# P,x = laurent_polynomial_ring(ZeroRing, "x"); +# @test is_nilpotent(P(0)) +# @test is_nilpotent(P(1)) +# @test is_nilpotent(x) +# @test is_nilpotent(-x) + +# @test is_unit(P(0)) +# @test is_unit(P(1)) +# @test is_unit(x) +# @test is_unit(-x) +# end + +## LaurentPoly over ZZ +@testset "Nilpotent/unit for ZZ[x, x^(-1)]" begin + P,x = laurent_polynomial_ring(ZZ, "x"); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(-2)) + @test !is_unit(P(-2)) + @test is_unit(x) + @test is_unit(-x) + @test is_unit(1/x) + @test is_unit(-1/x) + @test !is_unit(2/x) + @test !is_unit(-2/x) + @test !is_unit(x+1) + @test !is_unit(x-1) +end + +## LaurentPoly over QQ +@testset "Nilpotent/unit for QQ[x, x^(-1)]" begin + P,x = laurent_polynomial_ring(QQ, "x"); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test is_unit(P(-2)) + @test is_unit(P(-2)) + @test is_unit(x) + @test is_unit(-x) + @test is_unit(2*x) + @test is_unit(-2*x) + @test is_unit(1/x) + @test is_unit(-1/x) + @test is_unit(2/x) + @test is_unit(-2/x) + @test !is_unit(x+1) + @test !is_unit(x-1) +end + +## LaurentPoly over ZZ/720 +@testset "Nilpotent/unit for ZZ/(720)[x, x^(-1)]" begin + P,x = laurent_polynomial_ring(ZZmod720, "x"); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test is_nilpotent(P(30)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test is_nilpotent(30*x) + @test is_nilpotent(30/x) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(2)) + @test !is_unit(P(-2)) + @test is_unit(P(7)) + @test is_unit(P(-7)) + @test is_unit(x) + @test is_unit(-x) + @test !is_unit(2*x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test is_unit(x+30) + @test is_unit(x-30) + @test is_unit(1+30*x) + @test is_unit(1-30*x) + @test is_unit(7+60*x) + @test is_unit(7-60*x) + @test is_unit(600+7*x+30*x^2) + @test is_unit(600-7*x+30*x^2) +end diff --git a/test/generic/MPoly-test.jl b/test/generic/MPoly-test.jl index b15ea5aa2d..f2e0f12c50 100644 --- a/test/generic/MPoly-test.jl +++ b/test/generic/MPoly-test.jl @@ -454,19 +454,6 @@ end @test !is_univariate(y^4 + 3x + 1) end -@testset "Generic.MPoly.is_unit" begin - R, (x,) = polynomial_ring(residue_ring(ZZ, 4)[1], ["x"]) - - @test !is_unit(x) - @test !is_unit(2*x) - try - res = is_unit(1 + 2*x) - @test res - catch e - @test e isa NotImplementedError - end -end - @testset "Generic.MPoly.multivariate_coeff" begin R = ZZ @@ -1727,3 +1714,127 @@ end @test is_zero(one(S)) test_Ring_interface_recursive(S) end + +# ------------------------------------------------------- + +# Coeff rings needed for the tests below +ZeroRing,_ = residue_ring(ZZ,1); +ZZmod720,_ = residue_ring(ZZ, 720); + +## MPoly over ZeroRing +@testset "Nilpotent/unit for ZeroRing[x,y]" begin + P,(x,y) = polynomial_ring(ZeroRing, ["x", "y"]); + @test is_nilpotent(P(0)) + @test is_nilpotent(P(1)) + @test is_nilpotent(x) + @test is_nilpotent(-x) + @test is_nilpotent(x+y) + @test is_nilpotent(x-y) + @test is_nilpotent(x*y) + + @test is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(x) + @test is_unit(-x) + @test is_unit(x+y) + @test is_unit(x-y) + @test is_unit(x*y) +end + +## MPoly over ZZ +@testset "Nilpotent/unit for ZZ[x,y]" begin + P,(x,y) = polynomial_ring(ZZ, ["x", "y"]); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test !is_nilpotent(x+y) + @test !is_nilpotent(x-y) + @test !is_nilpotent(x*y) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(-2)) + @test !is_unit(P(-2)) + @test !is_unit(x) + @test !is_unit(-x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test !is_unit(x+y) + @test !is_unit(x-y) + @test !is_unit(x*y) +end + +## MPoly over QQ +@testset "Nilpotent/unit for QQ[x,y]" begin + P,(x,y) = polynomial_ring(QQ, ["x", "y"]); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test !is_nilpotent(x+y) + @test !is_nilpotent(x-y) + @test !is_nilpotent(x*y) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test is_unit(P(-2)) + @test is_unit(P(-2)) + @test !is_unit(x) + @test !is_unit(-x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test !is_unit(x+y) + @test !is_unit(x-y) + @test !is_unit(x*y) +end + +## MPoly over ZZ/720 +@testset "Nilpotent/unit for ZZ/(720)[x,y]" begin + FactorsOf30 = [2, 3, 5, 6, 10, 15]; # non-nilpotent zero-divisors + P,(x,y) = polynomial_ring(ZZmod720, ["x", "y"]); + @test is_nilpotent(P(0)) + for ZeroDiv in FactorsOf30 + @test !is_nilpotent(P(ZeroDiv)) + end + @test is_nilpotent(P(30)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + for ZeroDiv in FactorsOf30 + @test !is_nilpotent(ZeroDiv*x) + end + @test is_nilpotent(30*x) + @test is_nilpotent(30*x+120*y) + @test is_nilpotent(30*x-120*y) + @test !is_nilpotent(x*y) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(2)) + @test !is_unit(P(-2)) + @test is_unit(P(7)) + @test is_unit(P(-7)) + @test !is_unit(x) + @test !is_unit(-x) + @test !is_unit(x+1) + for ZeroDiv in FactorsOf30 + @test !is_unit(x+ZeroDiv) + end + @test !is_unit(x+30) + @test !is_unit(x-30) + @test is_unit(1+30*x) + for ZeroDiv in FactorsOf30 + @test !is_unit(1+x*ZeroDiv) + end + @test is_unit(7+60*x) + @test is_unit(7-60*x) + @test is_unit(1+30*(x+y)) + for ZeroDiv in FactorsOf30 + @test !is_unit(1+ZeroDiv*(x+y)) + end + @test is_unit(7+60*x*y) + @test is_unit(7-60*x*y) +end diff --git a/test/generic/UnivPoly-test.jl b/test/generic/UnivPoly-test.jl index 03c323b0cb..23dfd47cec 100644 --- a/test/generic/UnivPoly-test.jl +++ b/test/generic/UnivPoly-test.jl @@ -1127,3 +1127,90 @@ end end end +# ------------------------------------------------------- + +# Coeff rings needed for the tests below +ZeroRing,_ = residue_ring(ZZ,1); +ZZmod720,_ = residue_ring(ZZ, 720); + +## UPoly over ZeroRing +@testset "Nilpotent/unit for ZeroRing[x]" begin + P,x = polynomial_ring(ZeroRing, "x"); + @test is_nilpotent(P(0)) + @test is_nilpotent(P(1)) + @test is_nilpotent(x) + @test is_nilpotent(-x) + + @test is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(x) + @test is_unit(-x) +end + +## UPoly over ZZ +@testset "Nilpotent/unit for ZZ[x]" begin + P,x = polynomial_ring(ZZ, "x"); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(-2)) + @test !is_unit(P(-2)) + @test !is_unit(x) + @test !is_unit(-x) + @test !is_unit(x+1) + @test !is_unit(x-1) +end + +# UPoly over QQ +@testset "Nilpotent/unit for QQ[x]" begin + P,x = polynomial_ring(QQ, "x"); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test is_unit(P(-2)) + @test is_unit(P(-2)) + @test !is_unit(x) + @test !is_unit(-x) + @test !is_unit(x+1) + @test !is_unit(x-1) +end + +## UPoly over ZZ/720 +@testset "Nilpotent/unit for ZZ/(720)[x]" begin + P,x = polynomial_ring(ZZmod720, "x"); + @test is_nilpotent(P(0)) + @test !is_nilpotent(P(1)) + @test is_nilpotent(P(30)) + @test !is_nilpotent(x) + @test !is_nilpotent(-x) + @test is_nilpotent(30*x) + + @test !is_unit(P(0)) + @test is_unit(P(1)) + @test is_unit(P(-1)) + @test !is_unit(P(2)) + @test !is_unit(P(-2)) + @test is_unit(P(7)) + @test is_unit(P(-7)) + @test !is_unit(x) + @test !is_unit(-x) + @test !is_unit(x+1) + @test !is_unit(x-1) + @test !is_unit(x+30) + @test !is_unit(x-30) + @test is_unit(1+30*x) + @test is_unit(1-30*x) + @test is_unit(7+60*x) + @test is_unit(7-60*x) +end +