From 05a717582a4e39014728b71128abc46a8764d50b Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 2 Jan 2026 22:46:41 +0100 Subject: [PATCH 1/3] Implement is_known(characteristic, ::NCRing) --- src/AbsMSeries.jl | 5 ++--- src/Fraction.jl | 5 ++--- src/FreeAssociativeAlgebra.jl | 1 + src/LaurentPoly.jl | 1 + src/MatRing.jl | 7 ++----- src/NCPoly.jl | 3 ++- src/NumFields.jl | 1 + src/Poly.jl | 3 ++- src/Residue.jl | 5 ++--- src/ResidueField.jl | 12 ++++++------ src/Rings.jl | 1 + src/generic/AbsSeries.jl | 5 ++--- src/generic/FactoredFraction.jl | 3 ++- src/generic/FunctionField.jl | 1 + src/generic/LaurentPoly.jl | 1 + src/generic/LaurentSeries.jl | 5 ++--- src/generic/PuiseuxSeries.jl | 5 ++--- src/generic/RationalFunctionField.jl | 5 ++--- src/generic/RelSeries.jl | 5 ++--- src/generic/Residue.jl | 3 ++- src/generic/SparsePoly.jl | 5 ++--- src/generic/TotalFraction.jl | 5 ++--- src/generic/UnivPoly.jl | 1 + src/julia/Float.jl | 3 ++- src/julia/GF.jl | 5 ++--- src/julia/Integer.jl | 3 ++- src/julia/Rational.jl | 3 ++- 27 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/AbsMSeries.jl b/src/AbsMSeries.jl index 6219632394..a56a6420be 100644 --- a/src/AbsMSeries.jl +++ b/src/AbsMSeries.jl @@ -40,9 +40,8 @@ $R$. """ symbols(R::MSeriesRing) -function characteristic(a::MSeriesRing) - return characteristic(base_ring(a)) -end +characteristic(R::MSeriesRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::MSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/Fraction.jl b/src/Fraction.jl index 97fb2a21cc..640307826c 100644 --- a/src/Fraction.jl +++ b/src/Fraction.jl @@ -20,9 +20,8 @@ function is_exact_type(a::Type{T}) where {S <: RingElement, T <: FracElem{S}} return is_exact_type(S) end -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 86e1aa9071..f1ae8179ac 100644 --- a/src/FreeAssociativeAlgebra.jl +++ b/src/FreeAssociativeAlgebra.jl @@ -22,6 +22,7 @@ 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))) diff --git a/src/LaurentPoly.jl b/src/LaurentPoly.jl index c484c69f13..ec8ea23158 100644 --- a/src/LaurentPoly.jl +++ b/src/LaurentPoly.jl @@ -11,6 +11,7 @@ ############################################################################### 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) diff --git a/src/MatRing.jl b/src/MatRing.jl index ff2ce83257..725ee35d57 100644 --- a/src/MatRing.jl +++ b/src/MatRing.jl @@ -65,11 +65,8 @@ function is_zero_divisor_with_annihilator(a::MatRingElem{T}) where T <: RingElem throw(NotImplementedError(:adj, a)) #return f, b*adj(A) end - -function characteristic(a::MatRing) - iszero(nrows(a)) && return 1 - return characteristic(base_ring(a)) -end +characteristic(R::MatRing) = iszero(nrows(R)) ? 1 : characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::MatRing) = iszero(nrows(R)) || is_known(characteristic, base_ring(R)) is_finite(R::MatRing) = iszero(nrows(R)) || is_finite(base_ring(R)) diff --git a/src/NCPoly.jl b/src/NCPoly.jl index 78cabff18b..af86d20c9d 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -102,7 +102,8 @@ 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)) +characteristic(R::NCPolyRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::NCPolyRing) = is_known(characteristic, base_ring(R)) is_finite(a::NCPolyRing) = is_trivial(a) diff --git a/src/NumFields.jl b/src/NumFields.jl index bc3c3eee5b..a0198697d5 100644 --- a/src/NumFields.jl +++ b/src/NumFields.jl @@ -8,6 +8,7 @@ end number_of_generators(L::SimpleNumField{T}) where {T} = 1 characteristic(F::NumField) = 0 +is_known(::typeof(characteristic), F::NumField) = true promote_rule(::Type{T}, ::Type{S}) where {S<:NumFieldElem,T<:Integer} = S diff --git a/src/Poly.jl b/src/Poly.jl index 1a16948169..bf838d8ea3 100644 --- a/src/Poly.jl +++ b/src/Poly.jl @@ -48,7 +48,8 @@ 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)) +characteristic(R::PolyRing) = characteristic(coefficient_ring(R)) +is_known(::typeof(characteristic), R::PolyRing) = is_known(characteristic, coefficient_ring(R)) is_finite(a::PolyRing) = is_trivial(a) diff --git a/src/Residue.jl b/src/Residue.jl index 1412afc434..2ce9670cde 100644 --- a/src/Residue.jl +++ b/src/Residue.jl @@ -113,9 +113,8 @@ annihilator(a::ResElem) = is_zero_divisor_with_annihilator(a)[2] deepcopy_internal(a::ResElem, dict::IdDict) = parent(a)(deepcopy_internal(data(a), dict)) -function characteristic(a::ResidueRing{T}) where T <: Integer - return modulus(a) -end +characteristic(R::ResidueRing{T}) where T <: Integer = modulus(R) +is_known(::typeof(characteristic), R::ResidueRing{T}) where T <: Integer = true ############################################################################### # diff --git a/src/ResidueField.jl b/src/ResidueField.jl index 69aec1607e..dcd4eea502 100644 --- a/src/ResidueField.jl +++ b/src/ResidueField.jl @@ -64,13 +64,13 @@ function modulus(r::ResFieldElem) return modulus(parent(r)) end -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)) -function characteristic(r::ResidueField{T}) where T <: Integer - return modulus(r) -end +characteristic(r::ResidueField{T}) where T <: Integer = modulus(r) +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 f25b47937c..1c14905d42 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -195,6 +195,7 @@ 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_zero(R::NCRing) diff --git a/src/generic/AbsSeries.jl b/src/generic/AbsSeries.jl index 498bf5e991..d7defc25c9 100644 --- a/src/generic/AbsSeries.jl +++ b/src/generic/AbsSeries.jl @@ -70,9 +70,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 0f152172b9..843c1f7931 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 5ac06ebd46..ac6c945865 100644 --- a/src/generic/FunctionField.jl +++ b/src/generic/FunctionField.jl @@ -556,6 +556,7 @@ symbol. var(R::FunctionField) = R.S characteristic(R::FunctionField) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::FunctionField) = is_known(characteristic, base_ring(R)) is_perfect(R::FunctionField) = characteristic(R) == 0 diff --git a/src/generic/LaurentPoly.jl b/src/generic/LaurentPoly.jl index 33ee568b0d..73024c5c2b 100644 --- a/src/generic/LaurentPoly.jl +++ b/src/generic/LaurentPoly.jl @@ -31,6 +31,7 @@ number_of_variables(R::LaurentPolyWrapRing) = 1 number_of_generators(R::LaurentPolyWrapRing) = number_of_variables(R) 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 19152f95ef..1edfc7231f 100644 --- a/src/generic/LaurentSeries.jl +++ b/src/generic/LaurentSeries.jl @@ -373,9 +373,8 @@ function renormalize!(z::LaurentSeriesElem) return nothing end -function characteristic(a::LaurentSeriesRing{T}) where T <: RingElement - return characteristic(base_ring(a)) -end +characteristic(R::LaurentSeriesRing) = characteristic(base_ring(R)) +is_known(::typeof(characteristic), R::LaurentSeriesRing) = is_known(characteristic, base_ring(R)) ############################################################################### # diff --git a/src/generic/PuiseuxSeries.jl b/src/generic/PuiseuxSeries.jl index cfd022081b..03c87c976d 100644 --- a/src/generic/PuiseuxSeries.jl +++ b/src/generic/PuiseuxSeries.jl @@ -267,9 +267,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 db7e5e4987..d1f291b7e7 100644 --- a/src/generic/RationalFunctionField.jl +++ b/src/generic/RationalFunctionField.jl @@ -132,9 +132,8 @@ function deepcopy_internal(a::RationalFunctionFieldElem, dict::IdDict) return R(deepcopy_internal(data(a), dict)) 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)) is_finite(R::RationalFunctionField) = is_finite(base_ring(underlying_fraction_field(R))) diff --git a/src/generic/RelSeries.jl b/src/generic/RelSeries.jl index 1f90aa2337..1ec83d6fa8 100644 --- a/src/generic/RelSeries.jl +++ b/src/generic/RelSeries.jl @@ -60,9 +60,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(R::RelPowerSeriesRing) = characteristic(base_ring(R)) +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 1094140bf2..f950346c20 100644 --- a/src/generic/Residue.jl +++ b/src/generic/Residue.jl @@ -114,8 +114,9 @@ function gens(R::Union{EuclideanRingResidueRing{T}, EuclideanRingResidueField{T} end function characteristic(R::Union{EuclideanRingResidueRing{T}, EuclideanRingResidueField{T}}) where {T<:PolyRingElem} - return characteristic(base_ring(base_ring(R))) + return characteristic(base_ring(R)) end +is_known(::typeof(characteristic), R::Union{EuclideanRingResidueRing{T}, EuclideanRingResidueField{T}}) where {T<:PolyRingElem} = 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 aaf5300465..5cb79894e8 100644 --- a/src/generic/SparsePoly.jl +++ b/src/generic/SparsePoly.jl @@ -83,9 +83,8 @@ function deepcopy_internal(a::SparsePoly{T}, dict::IdDict) where {T <: RingEleme 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 f70defc0b6..c7f0a117e0 100644 --- a/src/generic/TotalFraction.jl +++ b/src/generic/TotalFraction.jl @@ -34,9 +34,8 @@ function is_exact_type(a::Type{T}) where {S <: RingElem, 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 656aa9fd0a..a2b767e3c9 100644 --- a/src/generic/UnivPoly.jl +++ b/src/generic/UnivPoly.jl @@ -280,6 +280,7 @@ 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 diff --git a/src/julia/Float.jl b/src/julia/Float.jl index 53b02f099e..cf1c2a4b32 100644 --- a/src/julia/Float.jl +++ b/src/julia/Float.jl @@ -40,7 +40,8 @@ is_unit(a::AbstractFloat) = !is_zero(a) canonical_unit(a::AbstractFloat) = iszero(a) ? copysign(one(a), a) : a -characteristic(a::Floats{T}) where T <: AbstractFloat = 0 +characteristic(::Floats) = 0 +is_known(::typeof(characteristic), ::Floats) = true if VERSION < v"1.13.0-DEV.534" # https://github.com/JuliaLang/julia/pull/53677 is_negative(n::T) where T<:Real = n < zero(T) diff --git a/src/julia/GF.jl b/src/julia/GF.jl index 0ff695200a..03d20a3073 100644 --- a/src/julia/GF.jl +++ b/src/julia/GF.jl @@ -73,9 +73,8 @@ isone(a::GFElem{T}) where T <: Integer = a.d == 1 is_unit(a::GFElem) = a.d != 0 -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 1cb90d0307..a94fd1c4cb 100644 --- a/src/julia/Integer.jl +++ b/src/julia/Integer.jl @@ -44,7 +44,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 81a327eb26..20e930b59e 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(::Rationals) = 0 +is_known(::typeof(characteristic), ::Rationals) = true ############################################################################### # From dd2d950959cd32b2cb36eb92c44fd3279f7840d5 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 2 Jan 2026 22:46:44 +0100 Subject: [PATCH 2/3] Implement is_known(is_perfect, ::Field) --- src/Rings.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Rings.jl b/src/Rings.jl index 1c14905d42..25f8c7bcb7 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -224,8 +224,11 @@ is_zero(R::NCRing) = is_trivial(R) Test whether the field $F$ is perfect, that is, whether the characteristic is zero or else whether every element of $F$ admits a $p$-th root, where $p > 0$ is the characteristic of $F$. """ -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 From 74b7ac6b4f6f270852b77c3e8550031c7e87f239 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 3 Jan 2026 09:47:09 +0100 Subject: [PATCH 3/3] Remove incorrect characteristic(R::ResidueField) --- src/ResidueField.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ResidueField.jl b/src/ResidueField.jl index dcd4eea502..2355c8b810 100644 --- a/src/ResidueField.jl +++ b/src/ResidueField.jl @@ -64,11 +64,6 @@ function modulus(r::ResFieldElem) return modulus(parent(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)) - characteristic(r::ResidueField{T}) where T <: Integer = modulus(r) is_known(::typeof(characteristic), R::ResidueField{T}) where T <: Integer = true