Skip to content

Commit 1c605fb

Browse files
authored
Add conformance tests for MPolyRing, and fix bugs in ^ and is_unit for MPoly (#1950)
1 parent 6be03b7 commit 1c605fb

File tree

4 files changed

+146
-6
lines changed

4 files changed

+146
-6
lines changed

src/MPoly.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,14 +433,16 @@ function is_unit(f::T) where {T <: MPolyRingElem}
433433
# A polynomial over a commutative ring is a unit iff its
434434
# constant term is a unit and all other coefficients are nilpotent:
435435
# see e.g. <https://kconrad.math.uconn.edu/blurbs/ringtheory/polynomial-properties.pdf> for a proof.
436+
constant_term_is_unit = false
436437
for (c, expv) in zip(coefficients(f), exponent_vectors(f))
437438
if is_zero(expv)
438439
is_unit(c) || return false
440+
constant_term_is_unit = true
439441
else
440442
is_nilpotent(c) || return false
441443
end
442444
end
443-
return true
445+
return constant_term_is_unit
444446
end
445447

446448
function content(a::MPolyRingElem{T}) where T <: RingElement

src/generic/MPoly.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2316,6 +2316,8 @@ function ^(a::MPoly{T}, b::Int) where {T <: RingElement}
23162316
return zero(a)
23172317
end
23182318
elseif length(a) == 1
2319+
c = coeff(a, 1)^b
2320+
is_zero(c) && return zero(a)
23192321
N = size(a.exps, 1)
23202322
exps = zeros(UInt, N, 1)
23212323
monomial_mul!(exps, 1, a.exps, 1, b, N)
@@ -2324,7 +2326,7 @@ function ^(a::MPoly{T}, b::Int) where {T <: RingElement}
23242326
error("Exponent overflow in powering")
23252327
end
23262328
end
2327-
return parent(a)([coeff(a, 1)^b], exps)
2329+
return parent(a)([c], exps)
23282330
elseif b == 0
23292331
return one(a)
23302332
elseif b == 1

test/Rings-conformance-tests.jl

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -719,15 +719,15 @@ function test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps = 30)
719719
@test symbols(Rx) isa Vector{Symbol}
720720
@test length(symbols(Rx)) == 1
721721
@test is_gen(gen(Rx))
722+
@test is_gen(x)
722723
@test is_monic(x)
724+
@test is_trivial(Rx) || !is_gen(x^2)
723725
for i in 1:reps
724726
a = test_elem(Rx)
725727
@test iszero(a) || degree(a) >= 0
726728
@test equality(a, leading_coefficient(a)*x^max(0, degree(a)) + tail(a))
727729
@test constant_coefficient(a) isa elem_type(R)
728730
@test trailing_coefficient(a) isa elem_type(R)
729-
@test is_gen(x)
730-
@test iszero(one(Rx)) || !is_gen(x^2)
731731
@test is_monic(a) == isone(leading_coefficient(a))
732732
end
733733
end
@@ -737,6 +737,140 @@ function test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps = 30)
737737
end
738738

739739

740+
function test_MPoly_interface(Rxy::AbstractAlgebra.MPolyRing; reps = 30)
741+
742+
# for simplicity, these tests for now assume exactly two generators
743+
@assert ngens(Rxy) == 2
744+
745+
T = elem_type(Rxy)
746+
747+
@testset "MPoly interface for $(Rxy) of type $(typeof(Rxy))" begin
748+
749+
test_Ring_interface(Rxy; reps = reps)
750+
751+
@testset "Basic functionality" begin
752+
@test symbols(Rxy) isa Vector{Symbol}
753+
@test length(symbols(Rxy)) == ngens(Rxy)
754+
@test length(gens(Rxy)) == ngens(Rxy)
755+
@test gens(Rxy) == [gen(Rxy, i) for i in 1:ngens(Rxy)]
756+
@test all(is_gen, gens(Rxy)) || is_trivial(Rxy)
757+
end
758+
759+
@testset "Polynomial Constructors" begin
760+
for i in 1:reps
761+
a = test_elem(Rxy)::T
762+
for b in coefficients(a)
763+
@assert Rxy(b) isa T
764+
end
765+
766+
# test MPolyBuildCtx
767+
B = MPolyBuildCtx(Rxy)
768+
for (c, e) in zip(AbstractAlgebra.coefficients(a), AbstractAlgebra.exponent_vectors(a))
769+
push_term!(B, c, e)
770+
end
771+
@test finish(B) == a
772+
end
773+
x, y = gens(Rxy)
774+
f = 13*x^3*y^4 + 2*x - 7
775+
#@test Rxy([2,-7,13], [[1,0],[0,0],[3,4]]) == f # FIXME: interface spec does not say this is required?
776+
777+
R = base_ring(Rxy)
778+
@test Rxy(R.([2,-7,13]), [[1,0],[0,0],[3,4]]) == f
779+
end
780+
781+
# skip trivial rings after this, it is not worth the bother
782+
is_trivial(Rxy) && return
783+
784+
@testset "Element properties" begin
785+
R = base_ring(Rxy)
786+
x, y = gens(Rxy)
787+
788+
a = zero(Rxy)
789+
@test !is_monomial(a)
790+
@test !is_term(a)
791+
@test is_constant(a)
792+
@test !is_gen(a)
793+
@test !is_unit(a)
794+
@test is_nilpotent(a)
795+
@test length(a) == 0
796+
@test total_degree(a) < 0
797+
@test all(is_negative, degrees(a))
798+
799+
a = one(Rxy)
800+
@test is_monomial(a)
801+
@test is_term(a)
802+
@test is_constant(a)
803+
@test !is_gen(a)
804+
@test is_unit(a)
805+
@test !is_nilpotent(a)
806+
@test length(a) == 1
807+
@test total_degree(a) == 0
808+
@test degrees(a) == [0, 0]
809+
810+
a = x
811+
@test is_monomial(a)
812+
@test is_term(a)
813+
@test !is_constant(a)
814+
@test is_gen(a)
815+
@test !is_unit(a)
816+
@test !is_nilpotent(a)
817+
@test length(a) == 1
818+
@test total_degree(a) == 1
819+
@test degrees(a) == [1, 0]
820+
821+
a = x^2
822+
@test is_monomial(a)
823+
@test is_term(a)
824+
@test !is_constant(a)
825+
@test !is_gen(a)
826+
@test !is_unit(a)
827+
@test !is_nilpotent(a)
828+
@test length(a) == 1
829+
@test total_degree(a) == 2
830+
@test degrees(a) == [2, 0]
831+
832+
if !is_zero(R(2))
833+
a = 2*x
834+
@test !is_monomial(a)
835+
@test is_term(a)
836+
@test !is_constant(a)
837+
@test !is_gen(a)
838+
@test !is_unit(a)
839+
@test is_nilpotent(a) == is_nilpotent(R(2))
840+
@test length(a) == 1
841+
@test total_degree(a) == 1
842+
@test degrees(a) == [1, 0]
843+
end
844+
845+
a = x^3 + y^4
846+
@test !is_monomial(a)
847+
@test !is_term(a)
848+
@test !is_constant(a)
849+
@test !is_gen(a)
850+
@test !is_unit(a)
851+
@test !is_nilpotent(a)
852+
@test length(a) == 2
853+
@test total_degree(a) == 4
854+
@test degrees(a) == [3, 4]
855+
856+
for i in 1:reps
857+
a = test_elem(Rxy)
858+
iszero(a) && continue
859+
@test length(a) >= 0
860+
@test sum(degrees(a)) >= total_degree(a)
861+
end
862+
863+
end
864+
865+
# TODO: add more tests, covering everything described in the manual, see
866+
# https://nemocas.github.io/AbstractAlgebra.jl/dev/mpoly_interface/
867+
# https://nemocas.github.io/AbstractAlgebra.jl/dev/mpolynomial/
868+
end
869+
870+
return nothing
871+
end
872+
873+
740874
function test_MatSpace_interface(S::MatSpace; reps = 20)
741875

742876
ST = elem_type(S)
@@ -891,8 +1025,10 @@ end
8911025

8921026
function test_Ring_interface_recursive(R::AbstractAlgebra.Ring; reps = 50)
8931027
test_Ring_interface(R; reps = reps)
894-
Rx, _ = polynomial_ring(R, "x")
1028+
Rx, _ = polynomial_ring(R, :x)
8951029
test_Poly_interface(Rx, reps = 2 + fld(reps, 2))
1030+
Rxy, _ = polynomial_ring(R, [:x, :y])
1031+
test_MPoly_interface(Rxy, reps = 2 + fld(reps, 2))
8961032
S = matrix_ring(R, rand(0:3))
8971033
test_MatAlgebra_interface(S, reps = 2 + fld(reps, 2))
8981034
S = matrix_space(R, rand(0:3), rand(0:3))

test/generic/Residue-test.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
test_Ring_interface_recursive(T)
1313

1414
#
15-
S, x = polynomial_ring(ZZ, "x")
15+
S, x = polynomial_ring(QQ, "x")
1616
T, = residue_ring(S, x^2 + 1)
1717
test_Ring_interface_recursive(T)
1818

0 commit comments

Comments
 (0)