diff --git a/src/number/NumberTypes.jl b/src/number/NumberTypes.jl index 42810c1d0..2c6aeec2f 100644 --- a/src/number/NumberTypes.jl +++ b/src/number/NumberTypes.jl @@ -241,12 +241,29 @@ mutable struct N_GField <: Field if cached && haskey(N_GFieldID, (p, n, S)) d = N_GFieldID[p, n, S]::N_GField else - gfinfo = GFInfo(Cint(p), Cint(n), pointer(Base.Vector{UInt8}(string(S)*"\0"))) - GC.@preserve gfinfo begin - ptr = libSingular.nInitChar(libSingular.n_GF, pointer_from_objref(gfinfo)) - d = new(ptr, n, libSingular.n_SetMap(ZZ.ptr, ptr), - libSingular.n_SetMap(ptr, ZZ.ptr), 1, S) + # NOTE: degree 1 special case + # A N_GField with n = 1 - when passed through Singular and to + # create_ring_from_singular_ring - will be recognized as a N_ZpField. + # This could cause some identity issue since we have the non identity + # N_GField(p, 1, ) != N_ZpField(p). There are two solutions: + # (1) accept this and discourage use of N_GField(p, 1, ) + # (2) Get singular to return distinct pointers for these two fields + # In any case, Singular as of Singular_jll v402.0.104+0 is still buggy + # in its treatment of N_GField(p, 1, ). Namely, if the n == 1 + # special treatment is removed here, the last test in caller.LibNormal + # fails because Singular returns the wrong coefficient field. This + # has nothing to do per se with the identity issue, it is a separate + # Singular bug. + if n == 1 + ptr = libSingular.nInitChar(libSingular.n_Zp, Ptr{Nothing}(p)) + else + gfinfo = GFInfo(Cint(p), Cint(n), pointer(Base.Vector{UInt8}(string(S)*"\0"))) + GC.@preserve gfinfo begin + ptr = libSingular.nInitChar(libSingular.n_GF, pointer_from_objref(gfinfo)) + end end + d = new(ptr, n, libSingular.n_SetMap(ZZ.ptr, ptr), + libSingular.n_SetMap(ptr, ZZ.ptr), 1, S) finalizer(_Ring_finalizer, d) if cached N_GFieldID[p, n, S] = d diff --git a/src/number/n_GF.jl b/src/number/n_GF.jl index eaaaebdba..c4b63993c 100644 --- a/src/number/n_GF.jl +++ b/src/number/n_GF.jl @@ -50,7 +50,14 @@ one(R::N_GField) = R(1) zero(R::N_GField) = R(0) -gen(R::N_GField) = R(libSingular.n_Param(Cint(1), R.ptr)) +function gen(R::N_GField) + if degree(R) == 1 + # NOTE: degree 1 special case + return zero(R) + else + return R(libSingular.n_Param(Cint(1), R.ptr)) + end +end function isone(n::n_GF) c = parent(n) @@ -62,10 +69,6 @@ function iszero(n::n_GF) return libSingular.n_IsZero(n.ptr, c.ptr) end -@doc Markdown.doc""" - isunit(n::n_GF) -Return `true` if $n$ is a unit in the field, i.e. nonzero. -""" isunit(n::n_GF) = !iszero(n) ############################################################################### @@ -93,7 +96,11 @@ end function AbstractAlgebra.expressify(a::n_GF; context = nothing)::Any F = parent(a) i = reinterpret(Int, a.ptr.cpp_object) - if 1 < i < characteristic(F)^degree(F) + p = Int(characteristic(parent(a))) + if degree(F) == 1 + # NOTE: degree 1 special case + return i > p - i ? i - p : i + elseif 1 < i < p^degree(F) return Expr(:call, :^, F.S, i) elseif i == 1 return F.S diff --git a/test/number/n_GF-test.jl b/test/number/n_GF-test.jl index 5c594d02e..dfb747d0c 100644 --- a/test/number/n_GF-test.jl +++ b/test/number/n_GF-test.jl @@ -12,6 +12,19 @@ @test F != F2 @test F1 != F2 + F, x = FiniteField(7, 1, "x") + F1, x = FiniteField(7, 1, "x") + F2, x = FiniteField(7, 1, "x", cached = false) + + @test isa(x, n_GF) + + @test F isa Singular.Field + @test F1 isa Singular.Field + @test F2 isa Singular.Field + @test F == F1 + @test F != F2 + @test F1 != F2 + @test_throws DomainError FiniteField(257, 1, "a") @test_throws DomainError FiniteField(2, 16, "a") @test_throws DomainError FiniteField(2, 64, "a") # errors even if 2^64 == 0 @@ -31,6 +44,21 @@ end @test sprint(show, "text/plain", x) == "x" @test sprint(show, "text/plain", x^2) == "x^2" @test sprint(show, "text/plain", x^11) == "x^11" + R, x = FiniteField(5, 1, "x") + + @test string(zero(R)) == "0" + @test string(one(R)) == "1" + @test string(R(2)) == "2" + @test occursin(r"\A\d+\Z", string(x)) + @test occursin(r"\A\d+\Z", string(x^2)) + @test occursin(r"\A\d+\Z", string(x^11)) + + @test sprint(show, "text/plain", zero(R)) == "0" + @test sprint(show, "text/plain", one(R)) == "1" + @test sprint(show, "text/plain", R(2)) == "2" + @test occursin(r"\A\d+\Z", sprint(show, "text/plain", x)) + @test occursin(r"\A\d+\Z", sprint(show, "text/plain", x^2)) + @test occursin(r"\A\d+\Z", sprint(show, "text/plain", x^11)) end @testset "n_GF.manipulation" begin @@ -47,6 +75,20 @@ end @test degree(R) == 2 @test deepcopy(R(2)) == R(2) + + R, x = FiniteField(5, 1, "x") + + @test isone(one(R)) + @test iszero(zero(R)) + @test isunit(R(1)) && isunit(R(2)) + @test !isunit(R(0)) + + @test gen(R) == x + + @test characteristic(R) == 5 + @test degree(R) == 1 + + @test deepcopy(R(2)) == R(2) end @testset "n_GF.unary_ops" begin @@ -57,6 +99,14 @@ end @test -R(3) == R(2) @test -R() == R() @test -a == 2x - 1 + + R, x = FiniteField(5, 1, "x") + + a = 3x + 1 + + @test -R(3) == R(2) + @test -R() == R() + @test -a == 2x - 1 end @testset "n_GF.binary_ops" begin @@ -68,6 +118,15 @@ end @test a + b == R(3) @test a - b == -x - 1 @test a*b == x^19 + + R, x = FiniteField(5, 1, "x") + + a = 2x + 1 + b = 3x + 2 + + @test a + b == R(3) + @test a - b == -x - 1 + @test a*b == 6*x^2 + 2*x + 2 end @testset "n_GF.comparison" begin @@ -77,6 +136,13 @@ end @test a == deepcopy(a) @test isequal(a, a) + + R, x = FiniteField(5, 1, "x") + + a = 2x + 3 + + @test a == deepcopy(a) + @test isequal(a, a) end @testset "n_GF.ad_hoc_comparison" begin @@ -88,6 +154,15 @@ end @test 2 != x @test isequal(R(2), 2) @test isequal(2, R(2)) + + R, x = FiniteField(5, 1, "x") + + @test R(2) == 2 + @test x != 2 || x == 2 + @test 2 == R(2) + @test 2 != x || 2 == x + @test isequal(R(2), 2) + @test isequal(2, R(2)) end @testset "n_GF.powering" begin @@ -95,6 +170,11 @@ end @test (x + 1)^2 == x^20 @test_throws DomainError x^-rand(1:99) + + R, x = FiniteField(5, 1, "x") + @test x^3 == x*x*x + @test R(2)^2 == 2^2 + @test R(2)^3 == 2^3 end @testset "n_GF.exact_division" begin @@ -105,6 +185,11 @@ end @test inv(a) == x^2 @test divexact(a, b) == x^14 + + R, x = FiniteField(5, 1, "x") + + @test 2*inv(R(2)) == 1 + @test divexact(R(2), R(3)) == R(4) end @testset "n_GF.gcd_lcm" begin @@ -115,6 +200,11 @@ end @test gcd(a, b) == R(1) @test gcd(R(0), R(0)) == R(0) + + R, x = FiniteField(5, 1, "x") + + @test gcd(R(0), R(0)) == R(0) + @test gcd(R(2), R(3)) == R(1) end @testset "n_GF.Polynomials" begin