diff --git a/ext/TestExt/Rings-conformance-tests.jl b/ext/TestExt/Rings-conformance-tests.jl index f4ad629d79..7daf712b1a 100644 --- a/ext/TestExt/Rings-conformance-tests.jl +++ b/ext/TestExt/Rings-conformance-tests.jl @@ -47,6 +47,9 @@ function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) @test iszero(characteristic(R) * one(R)) @test iszero(one(R) * characteristic(R)) catch + # could not compute characteristic, so verify that is_known + # reflects this + @test is_known(characteristic, R) == false end end @@ -290,6 +293,13 @@ function test_Field_interface(R::AbstractAlgebra.Field; reps = 50) test_Ring_interface(R, reps = reps) + # We implicitly assume all genuine fields (i.e. of type `Field`, not + # just rings that happen to be fields) know their characteristic. So + # test for that. We may relax this in the future if we have need for it. + # But for now if the next tests fail this usually means someone forgot + # to implement `characteristic` for their ring type properly. + @test is_known(characteristic, R) == true + @test iszero(R(characteristic(R))) @test iszero(characteristic(R) * one(R)) @test iszero(one(R) * characteristic(R)) diff --git a/src/AbsMSeries.jl b/src/AbsMSeries.jl index 6da47919ff..b56f9b899a 100644 --- a/src/AbsMSeries.jl +++ b/src/AbsMSeries.jl @@ -54,9 +54,8 @@ end Return the characteristic of the base ring of the series `a`. If the characteristic is not known, an exception is raised. """ -function characteristic(a::MSeriesRing) - return characteristic(base_ring(a)) -end +characteristic(a::MSeriesRing) = characteristic(base_ring(a)) +is_known(::typeof(characteristic), R::MSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/Fraction.jl b/src/Fraction.jl index b72038b03d..b085d5d3c8 100644 --- a/src/Fraction.jl +++ b/src/Fraction.jl @@ -20,14 +20,8 @@ function is_exact_type(a::Type{T}) where {S <: RingElement, T <: FracElem{S}} return is_exact_type(S) end -@doc raw""" - characteristic(R::FracField{T}) where T <: RingElem - -Return the characteristic of the given field. -""" -function characteristic(R::FracField{T}) where T <: RingElem - return characteristic(base_ring(R)) -end +characteristic(R::FracField) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::FracField) = is_known(characteristic, base_ring(R)) @doc raw""" vars(a::FracElem{S}) where {S <: MPolyRingElem{<: RingElement}} diff --git a/src/FreeAssociativeAlgebra.jl b/src/FreeAssociativeAlgebra.jl index cf43afb015..ef84a839a3 100644 --- a/src/FreeAssociativeAlgebra.jl +++ b/src/FreeAssociativeAlgebra.jl @@ -21,8 +21,10 @@ function is_exact_type(a::Type{S}) where {T <: RingElement, S <: FreeAssociative end characteristic(R::FreeAssociativeAlgebra) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::FreeAssociativeAlgebra) = is_known(characteristic, base_ring(R)) is_finite(R::FreeAssociativeAlgebra) = is_trivial(base_ring(R)) || (nvars(R) == 0 && is_finite(base_ring(R))) +is_known(::typeof(is_finite), R::FreeAssociativeAlgebra) = is_known(is_trivial, base_ring(R)) || (nvars(R) == 0 && is_known(is_finite, base_ring(R))) ############################################################################### # diff --git a/src/LaurentMPoly.jl b/src/LaurentMPoly.jl index 180295fab9..e3b72f4f92 100644 --- a/src/LaurentMPoly.jl +++ b/src/LaurentMPoly.jl @@ -13,6 +13,7 @@ characteristic(R::LaurentMPolyRing) = characteristic(base_ring(R)) is_finite(R::LaurentMPolyRing) = is_trivial(base_ring(R)) || (nvars(R) == 0 && is_finite(base_ring(R))) +is_known(::typeof(is_finite), R::LaurentMPolyRing) = is_known(is_trivial, base_ring(R)) || (nvars(R) == 0 && is_known(is_finite, base_ring(R))) ############################################################################### # diff --git a/src/LaurentPoly.jl b/src/LaurentPoly.jl index c484c69f13..ef3dff0c3c 100644 --- a/src/LaurentPoly.jl +++ b/src/LaurentPoly.jl @@ -11,8 +11,10 @@ ############################################################################### characteristic(R::LaurentPolyRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::LaurentPolyRing) = is_known(characteristic, base_ring(R)) is_finite(R::LaurentPolyRing) = is_trivial(R) +is_known(::typeof(is_finite), R::LaurentPolyRing) = is_known(is_trivial, R) ############################################################################### # diff --git a/src/MPoly.jl b/src/MPoly.jl index dadafa08c8..3f05c1b562 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -141,6 +141,7 @@ end characteristic(R::MPolyRing) = characteristic(base_ring(R)) is_finite(R::MPolyRing) = is_trivial(base_ring(R)) || (nvars(R) == 0 && is_finite(base_ring(R))) +is_known(::typeof(is_finite), R::MPolyRing) = is_known(is_trivial, base_ring(R)) || (nvars(R) == 0 && is_known(is_finite, base_ring(R))) ############################################################################### diff --git a/src/MatRing.jl b/src/MatRing.jl index 9c2655bb76..963f7b6563 100644 --- a/src/MatRing.jl +++ b/src/MatRing.jl @@ -69,8 +69,10 @@ function characteristic(a::MatRing) iszero(nrows(a)) && return 1 return characteristic(base_ring(a)) end +is_known(::typeof(characteristic), R::MatRing) = is_known(characteristic, base_ring(R)) is_finite(R::MatRing) = iszero(nrows(a)) || is_finite(base_ring(R)) +is_known(::typeof(is_finite), R::MatRing) = iszero(nrows(R)) || is_known(is_trivial, base_ring(R)) ############################################################################### # diff --git a/src/Module.jl b/src/Module.jl index ed21c70b2a..21a318c7a6 100644 --- a/src/Module.jl +++ b/src/Module.jl @@ -26,6 +26,7 @@ function check_parent(M::FPModule{T}, N::FPModule{T}) where T <: RingElement end is_finite(M::FPModule{<:FinFieldElem}) = true +is_known(::typeof(is_finite), ::FPModule{<:FinFieldElem}) = true function is_sub_with_data(M::FPModule{T}, N::FPModule{T}) where T <: RingElement fl = is_submodule(N, M) diff --git a/src/NCPoly.jl b/src/NCPoly.jl index 2f036ad43b..027f2296a5 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -101,8 +101,10 @@ Return the number of variables of the polynomial ring, which is 1. number_of_variables(a::NCPolyRing) = 1 characteristic(a::NCPolyRing) = characteristic(base_ring(a)) +is_known(::typeof(characteristic), R::NCPolyRing) = is_known(characteristic, base_ring(R)) is_finite(a::NCPolyRing) = is_trivial(a) +is_known(::typeof(is_finite), R::NCPolyRing) = is_known(is_trivial, R) ############################################################################### # diff --git a/src/NCRings.jl b/src/NCRings.jl index 523f859d93..dae796f902 100644 --- a/src/NCRings.jl +++ b/src/NCRings.jl @@ -186,6 +186,13 @@ function characteristic(R::NCRing) error("Characteristic not known") end +# All rings are supposed to implement characteristic if they can. If they +# can not, or only can do it with an expensive computation (e.g. a Groebner +# basis), then instead they should implement an `is_known` method indicating +# this. To enforce this via the ring conformance tests, we add this default +# method for `is_known(characteristic, ::NCRing)` +is_known(::typeof(characteristic), ::NCRing) = true + ############################################################################### # # One and zero diff --git a/src/NumFields.jl b/src/NumFields.jl index 0c423f4232..e40909b256 100644 --- a/src/NumFields.jl +++ b/src/NumFields.jl @@ -21,6 +21,8 @@ canonical_unit(a::NumFieldElem) = a characteristic(F::NumField) = 0 +is_known(::typeof(characteristic), F::NumField) = true + promote_rule(::Type{T}, ::Type{S}) where {S<:NumFieldElem,T<:Integer} = S promote_rule(::Type{S}, ::Type{T}) where {S<:NumFieldElem,T<:Integer} = S diff --git a/src/Poly.jl b/src/Poly.jl index 938268060f..1c834f0b45 100644 --- a/src/Poly.jl +++ b/src/Poly.jl @@ -48,8 +48,10 @@ Return the number of variables of the polynomial ring, which is 1. number_of_variables(a::PolyRing) = 1 characteristic(a::PolyRing) = characteristic(base_ring(a)) +is_known(::typeof(characteristic), R::PolyRing) = is_known(characteristic, base_ring(R)) is_finite(a::PolyRing) = is_trivial(a) +is_known(::typeof(is_finite), R::PolyRing) = is_known(is_trivial, R) Base.copy(a::PolyRingElem) = deepcopy(a) diff --git a/src/Residue.jl b/src/Residue.jl index 50a203e3ea..155808da61 100644 --- a/src/Residue.jl +++ b/src/Residue.jl @@ -116,6 +116,7 @@ deepcopy_internal(a::ResElem, dict::IdDict) = function characteristic(a::ResidueRing{T}) where T <: Integer return modulus(a) end +is_known(::typeof(characteristic), R::ResidueRing{T}) where T <: Integer = true ############################################################################### # diff --git a/src/ResidueField.jl b/src/ResidueField.jl index a94219108e..0136918c47 100644 --- a/src/ResidueField.jl +++ b/src/ResidueField.jl @@ -69,9 +69,10 @@ end Return the characteristic of the residue field. """ -function characteristic(R::ResidueField) - return characteristic(base_ring(R)) -end +characteristic(R::ResidueField) = characteristic(base_ring(R)) +# FIXME: why is the above method correct in general??? Isn't it wrong if +# we e.g. start with ZZ[:x] and factor out ideal([x, 2]) ? +is_known(::typeof(characteristic), R::ResidueField) = is_known(characteristic, base_ring(R)) @doc raw""" characteristic(r::ResidueField{T}) where T <: Integer @@ -82,6 +83,7 @@ residue $r$ belongs to. function characteristic(r::ResidueField{T}) where T <: Integer return modulus(r) end +is_known(::typeof(characteristic), R::ResidueField{T}) where T <: Integer = true data(a::ResFieldElem) = a.data diff --git a/src/Rings.jl b/src/Rings.jl index 463a58d6af..3a12beb6d4 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -211,16 +211,21 @@ of a single element, or equivalently if its characteristic is 1. Such rings are also called zero rings. """ is_trivial(R::NCRing) = !is_domain_type(elem_type(R)) && iszero(one(R)) +is_known(::typeof(is_trivial), R::NCRing) = is_domain_type(elem_type(R)) @doc raw""" is_perfect(F::Field) Test whether the field $F$ is perfect. """ -is_perfect(F::Field) = characteristic(F) == 0 || F isa FinField || - throw(NotImplementedError(:is_perfect, F)) +is_perfect(F::Field) = characteristic(F) == 0 || throw(NotImplementedError(:is_perfect, F)) +is_known(::typeof(is_perfect), F::Field) = is_known(characteristic, F) && characteristic(F) == 0 + +is_perfect(F::FinField) = true +is_known(::typeof(is_perfect), F::FinField) = true is_finite(F::FinField) = true +is_known(::typeof(is_finite), F::FinField) = true function is_finite(R::NCRing) c = characteristic(R) @@ -228,3 +233,4 @@ function is_finite(R::NCRing) c == 1 && return true throw(NotImplementedError(:is_finite, R)) end +is_known(::typeof(is_finite), R::NCRing) = is_known(characteristic, R) && characteristic(R) <= 1 diff --git a/src/generic/AbsSeries.jl b/src/generic/AbsSeries.jl index c942953610..8fce21fb3a 100644 --- a/src/generic/AbsSeries.jl +++ b/src/generic/AbsSeries.jl @@ -68,9 +68,8 @@ function deepcopy_internal(a::AbsSeries{T}, dict::IdDict) where T <: RingElement return parent(a)(coeffs, length(a), precision(a)) end -function characteristic(a::AbsPowerSeriesRing{T}) where T <: RingElement - return characteristic(base_ring(a)) -end +characteristic(R::AbsPowerSeriesRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::AbsPowerSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/FactoredFraction.jl b/src/generic/FactoredFraction.jl index 38a5647616..43737771df 100644 --- a/src/generic/FactoredFraction.jl +++ b/src/generic/FactoredFraction.jl @@ -20,7 +20,8 @@ base_ring_type(::Type{FactoredFracField{T}}) where T <: NCRingElement = parent_t base_ring(F::FactoredFracField{T}) where T <: RingElement = F.base_ring::parent_type(T) -characteristic(F::FactoredFracField{T}) where T <: RingElement = characteristic(base_ring(F)) +characteristic(F::FactoredFracField) = characteristic(base_ring(F)) +is_known(::typeof(characteristic), R::FactoredFracField) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/FunctionField.jl b/src/generic/FunctionField.jl index 13033a4a88..8fef7a37fc 100644 --- a/src/generic/FunctionField.jl +++ b/src/generic/FunctionField.jl @@ -561,6 +561,7 @@ var(R::FunctionField) = R.S Return the characteristic of the underlying rational function field. """ characteristic(R::FunctionField) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::FunctionField) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/LaurentPoly.jl b/src/generic/LaurentPoly.jl index 6a83f5432c..7b07afbec6 100644 --- a/src/generic/LaurentPoly.jl +++ b/src/generic/LaurentPoly.jl @@ -30,6 +30,7 @@ symbols(R::LaurentPolyWrapRing) = symbols(R.polyring) number_of_variables(R::LaurentPolyWrapRing) = number_of_variables(R.polyring) characteristic(R::LaurentPolyWrapRing) = characteristic(R.polyring) +is_known(::typeof(characteristic), R::LaurentPolyWrapRing) = is_known(characteristic, base_ring(R)) ############################################################################### diff --git a/src/generic/LaurentSeries.jl b/src/generic/LaurentSeries.jl index 4532901d18..1b0503663a 100644 --- a/src/generic/LaurentSeries.jl +++ b/src/generic/LaurentSeries.jl @@ -365,9 +365,8 @@ function renormalize!(z::LaurentSeriesElem) return nothing end -function characteristic(a::LaurentSeriesRing{T}) where T <: RingElement - return characteristic(base_ring(a)) -end +characteristic(a::LaurentSeriesRing) = characteristic(base_ring(a)) +is_known(::typeof(characteristic), R::LaurentSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/PermGroups.jl b/src/generic/PermGroups.jl index db506ddef1..66a39749f3 100644 --- a/src/generic/PermGroups.jl +++ b/src/generic/PermGroups.jl @@ -672,6 +672,7 @@ gen(G::SymmetricGroup, i::Int) = gens(G)[i] number_of_generators(G::SymmetricGroup) = G.n == 1 ? 0 : G.n == 2 ? 1 : 2 is_finite(G::SymmetricGroup) = true +is_known(::typeof(is_finite), ::SymmetricGroup) = true order(::Type{T}, G::SymmetricGroup) where {T} = convert(T, factorial(T(G.n))) diff --git a/src/generic/PuiseuxSeries.jl b/src/generic/PuiseuxSeries.jl index 6e71d2fe78..041305821d 100644 --- a/src/generic/PuiseuxSeries.jl +++ b/src/generic/PuiseuxSeries.jl @@ -259,9 +259,8 @@ function deepcopy_internal(a::PuiseuxSeriesElem{T}, dict::IdDict) where {T <: Ri return parent(a)(deepcopy_internal(a.data, dict), a.scale) end -function characteristic(a::PuiseuxSeriesRing{T}) where T <: RingElement - return characteristic(base_ring(a)) -end +characteristic(R::PuiseuxSeriesRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::PuiseuxSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/RationalFunctionField.jl b/src/generic/RationalFunctionField.jl index 0fa5e5e9df..12d02e2122 100644 --- a/src/generic/RationalFunctionField.jl +++ b/src/generic/RationalFunctionField.jl @@ -34,9 +34,8 @@ function is_exact_type(a::Type{S}) where {T <: FieldElement, U <: Union{PolyRing return is_exact_type(T) end -function characteristic(R::RationalFunctionField) - return characteristic(base_ring(R)) -end +characteristic(R::RationalFunctionField) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::RationalFunctionField) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/RelSeries.jl b/src/generic/RelSeries.jl index e4b656372a..b9c812a240 100644 --- a/src/generic/RelSeries.jl +++ b/src/generic/RelSeries.jl @@ -58,9 +58,8 @@ function deepcopy_internal(a::RelSeries{T}, dict::IdDict) where T <: RingElement return parent(a)(coeffs, pol_length(a), precision(a), valuation(a)) end -function characteristic(a::RelPowerSeriesRing{T}) where T <: RingElement - return characteristic(base_ring(a)) -end +characteristic(a::RelPowerSeriesRing) = characteristic(base_ring(a)) +is_known(::typeof(characteristic), R::RelPowerSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/Residue.jl b/src/generic/Residue.jl index b0f0350786..d919fedfdb 100644 --- a/src/generic/Residue.jl +++ b/src/generic/Residue.jl @@ -109,9 +109,8 @@ function gens(R::Union{EuclideanRingResidueRing{T}, EuclideanRingResidueField{T} return r end -function characteristic(R::Union{EuclideanRingResidueRing{T}, EuclideanRingResidueField{T}}) where {T<:PolyRingElem} - return characteristic(base_ring(base_ring(R))) -end +characteristic(R::Union{EuclideanRingResidueRing, EuclideanRingResidueField}) = characteristic(base_ring(base_ring(R))) +is_known(::typeof(characteristic), R::Union{EuclideanRingResidueRing, EuclideanRingResidueField}) = is_known(characteristic, base_ring(R)) function size(R::Union{EuclideanRingResidueRing{T}, EuclideanRingResidueField{T}}) where {T<:PolyRingElem} return size(base_ring(base_ring(R)))^degree(modulus(R)) diff --git a/src/generic/SparsePoly.jl b/src/generic/SparsePoly.jl index 5f680859a5..f8eb679336 100644 --- a/src/generic/SparsePoly.jl +++ b/src/generic/SparsePoly.jl @@ -83,9 +83,8 @@ function Base.deepcopy_internal(a::SparsePoly{T}, dict::IdDict) where {T <: Ring return parent(a)(Rc, Re) end -function characteristic(a::SparsePolyRing{T}) where T <: RingElement - return characteristic(base_ring(a)) -end +characteristic(R::SparsePolyRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::SparsePolyRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/TotalFraction.jl b/src/generic/TotalFraction.jl index 83c001c09d..e3e39dd87c 100644 --- a/src/generic/TotalFraction.jl +++ b/src/generic/TotalFraction.jl @@ -34,9 +34,8 @@ function is_exact_type(a::Type{T}) where {S <: RingElement, T <: TotFrac{S}} return is_exact_type(S) end -function characteristic(R::TotFracRing{T}) where T <: RingElem - return characteristic(base_ring(R)) -end +characteristic(R::TotFracRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::TotFracRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/UnivPoly.jl b/src/generic/UnivPoly.jl index 7d00697b07..ad08c1c734 100644 --- a/src/generic/UnivPoly.jl +++ b/src/generic/UnivPoly.jl @@ -290,6 +290,7 @@ end canonical_unit(p::UnivPoly) = canonical_unit(data(p)) characteristic(R::UniversalPolyRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::UniversalPolyRing) = is_known(characteristic, base_ring(R)) function Base.hash(p::UnivPoly, h::UInt) b = 0xcf418d4529109236%UInt diff --git a/src/julia/GF.jl b/src/julia/GF.jl index a1d9cba767..727ed45f7a 100644 --- a/src/julia/GF.jl +++ b/src/julia/GF.jl @@ -80,9 +80,8 @@ is_unit(a::GFElem) = a.d != 0 Return the characteristic of the given finite field. """ -function characteristic(R::GFField) - return R.p -end +characteristic(R::GFField) = R.p +is_known(::typeof(characteristic), ::GFField) = true @doc raw""" order(R::GFField) diff --git a/src/julia/Integer.jl b/src/julia/Integer.jl index afe27b8841..a218b05374 100644 --- a/src/julia/Integer.jl +++ b/src/julia/Integer.jl @@ -46,7 +46,8 @@ is_zero_divisor(a::Integer) = is_zero(a) canonical_unit(a::T) where T <: Integer = a < 0 ? T(-1) : T(1) -characteristic(::Integers{T}) where T <: Integer = 0 +characteristic(::Integers) = 0 +is_known(::typeof(characteristic), ::Integers) = true ############################################################################### # diff --git a/src/julia/Rational.jl b/src/julia/Rational.jl index 84ff6014ee..12aa9f0f84 100644 --- a/src/julia/Rational.jl +++ b/src/julia/Rational.jl @@ -52,7 +52,8 @@ function denominator(a::Rational, canonicalise::Bool=true) return Base.denominator(a) # all other types ignore canonicalise end -characteristic(a::Rationals{T}) where T <: Integer = 0 +characteristic(a::Rationals) = 0 +is_known(::typeof(characteristic), ::Rationals) = true ############################################################################### #