From 660ff397032ad4196b06d7ad42a8c08f16bbe67b Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 15 Dec 2025 22:11:09 +0100 Subject: [PATCH 1/5] Add test for LocalizedEuclideanRing Based on Nemo's test/HeckeMiscLocalization-test.jl --- test/Rings-test.jl | 1 + test/generic/Localization-test.jl | 133 ++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 test/generic/Localization-test.jl diff --git a/test/Rings-test.jl b/test/Rings-test.jl index 0c54c39f1d..2084e27500 100644 --- a/test/Rings-test.jl +++ b/test/Rings-test.jl @@ -29,6 +29,7 @@ include("generic/Matrix-test.jl") include("generic/MPoly-test.jl") include("generic/LaurentMPoly-test.jl") include("generic/UnivPoly-test.jl") +include("generic/Localization-test.jl") include("algorithms/MPolyEvaluate-test.jl") include("algorithms/MPolyFactor-test.jl") include("algorithms/MPolyNested-test.jl") diff --git a/test/generic/Localization-test.jl b/test/generic/Localization-test.jl new file mode 100644 index 0000000000..54f2df5d08 --- /dev/null +++ b/test/generic/Localization-test.jl @@ -0,0 +1,133 @@ +@testset "Localization" begin + + Qx, x = QQ["x"] + + @testset "Constructor" begin + + @test parent_type(LocalizedEuclideanRingElem{elem_type(Qx)}) == LocalizedEuclideanRing{elem_type(Qx)} + + L = localization(Qx, x^2 + 1) + @test elem_type(L) == LocalizedEuclideanRingElem{elem_type(Qx)} + @test base_ring(L) == Qx + @test base_ring(L()) == Qx + + L = localization(Qx, 4 * x^2 + x + 1) + @test elem_type(L) == LocalizedEuclideanRingElem{elem_type(Qx)} + @test base_ring(L) == Qx + @test base_ring(L()) == Qx + + L = localization(Qx, [3 * x^4 + x + 18, x^9 + 3]) + @test elem_type(L) == LocalizedEuclideanRingElem{elem_type(Qx)} + @test base_ring(L) == Qx + @test base_ring(L()) == Qx + end + + @testset "Canonicalisation" begin + + L = localization(Qx, 5x^2 + 3x + 2) + @test canonical_unit(L(5x^2 + 3x + 2)) == L(5) + @test canonical_unit(L(28)) == L(28) + @test canonical_unit(L(5 * x^4 + 3 * x^3 + 12 * x^2 + 6 * x + 4)) == L(5 * (x^2 + 2)) + + L = localization(Qx, [5x^2 + 3x + 2, x^16 + x + 3]) + @test canonical_unit(L(x^16 + x + 3)) == one(L) + @test canonical_unit(L(89)) == L(89) + @test canonical_unit(L((5 * x^4 + 3 * x^3 + 12 * x^2 + 6 * x + 4) // (x^3 + 9))) == inv(L((1 // 5 * x^3 + 9 // 5) // (x^2 + 2))) + end + + @testset "Parent object call overloading" begin + + L1 = localization(Qx, 5x^2 + 3x + 2) + L2 = localization(Qx, 7x^3 + 2) + @test_throws ErrorException L1(L2(5)) + @test parent(L1(4x // 7)) == L1 + + L = localization(Qx, [x^2 + 2, 3x^3 + 5]) + @test parent(L(3x^2 + 6)) == L + @test_throws ErrorException L(15 // (3x^2 + 6)) + @test_throws ErrorException L(17 // (9 * x^5 + 18 * x^3 + 15 * x^2 + 30)) + @test_throws ErrorException L(19 // (9 * x^6 + 54 * x^5 + 18 * x^4 + 123 * x^3 + 90 * x^2 + 30 * x + 180)) + end + + @testset "GCDX" begin + + L = localization(Qx, x^6 + 108) + a = L(x^10 + 2 * x^8 + 14 * x^7 + 49 * x^5 + 42 * x^3 + 294 * x^2 + 588) + b = L(x^11 + 14 * x^8 + 21 * x^6 + 294 * x^3 + 6) + (g, u, v) = gcdx(a, b) + @test g == u * a + v * b + + L = localization(Qx, [x^6 + 108, x^8 + 12x^3 + 3]) + a = L(x^10 + 2 * x^8 + 14 * x^7 + 49 * x^5 + 42 * x^3 + 294 * x^2 + 588) + b = L(x^11 + 14 * x^8 + 21 * x^6 + 294 * x^3 + 6) + (g, u, v) = gcdx(a, b) + @test g == u * a + v * b + end + + @testset "GCD and LCM" begin + + L = localization(Qx, x^6 + 108) + a = L(x^10 + 2 * x^8 + 14 * x^7 + 49 * x^5 + 42 * x^3 + 294 * x^2 + 588) + b = L(x^11 + 14 * x^8 + 21 * x^6 + 294 * x^3 + 6) + g = gcd(a, b) + @test g == L(1) + a = L(x^16 + 2 * x^14 + 14 * x^13 + 49 * x^11 + 108 * x^10 + 42 * x^9 + 510 * x^8 + 1512 * x^7 + 588 * x^6 + 5292 * x^5 + 4536 * x^3 + 31752 * x^2 + 63504) + b = L(x^17 + 14 * x^14 + 21 * x^12 + 108 * x^11 + 294 * x^9 + 1512 * x^8 + 2274 * x^6 + 31752 * x^3 + 648) + g = gcd(a, b) + @test g == L(x^6 + 108) + + L = localization(Qx, [x^6 + 108, x^8 + 12x^3 + 3]) + a = L(x^10 + 2 * x^8 + 14 * x^7 + 49 * x^5 + 42 * x^3 + 294 * x^2 + 588) + b = L(x^13 + 2 * x^11 + 14 * x^10 + 49 * x^8 + 42 * x^6 + 294 * x^5 + 588 * x^3 + 6 * x^2 + 12) + g = gcd(a, b) + @test g == L(1) + end + + @testset "Exact division" begin + L = localization(Qx, x^6 + 108) + @test_throws ErrorException divexact(L(9), L(2x^6 + 216)) + a = L(x^10 + 2 * x^8 + 14 * x^7 + 49 * x^5 + 42 * x^3 + 294 * x^2 + 588) + b = L(x^11 + 14 * x^8 + 21 * x^6 + 294 * x^3 + 6) + g = divides(a, b) + @test a == b * g[2] + @test g[1] == true + a = L(x^16 + 2 * x^14 + 14 * x^13 + 49 * x^11 + 108 * x^10 + 42 * x^9 + 510 * x^8 + 1512 * x^7 + 588 * x^6 + 5292 * x^5 + 4536 * x^3 + 31752 * x^2 + 63504) + b = L(x^17 + 14 * x^14 + 21 * x^12 + 108 * x^11 + 294 * x^9 + 1512 * x^8 + 2274 * x^6 + 31752 * x^3 + 648) + @test a == b * g[2] + @test g[1] == true + end + + @testset "Inversion" begin + L = localization(Qx, x^6 + 108) + @test_throws ErrorException inv(L(2x^6 + 216)) + @test inv(L(x^2 + 108)) == L(1 // (x^2 + 108)) + + L = localization(Qx, [x^6 + 3, x^3 + 5]) + @test_throws ErrorException inv(L(2x^6 + 6)) + @test inv(L(x^2 + 108)) == L(1 // (x^2 + 108)) + end + + @testset "Binary operators" begin + L = localization(Qx, x^6 + 108) + @test L(18x // 2) + L(2x // 1) == L(11x) + @test L(18x // 3) - L(1x) == L(5x) + @test L(32x) * L(4x) == L(128x^2) + + L = localization(Qx, [x^6 + 3, x^3 + 5]) + @test L(18x // 2) + L(2x // 1) == L(11x) + @test L(18x // 3) - L(1x) == L(5x) + @test L(32x) * L(4x) == L(128x^2) + end + + @testset "Basic manipulation" begin + L = localization(Qx, x^6 + 108) + @test iszero(L(x^2 + x)) == false + @test isone(L(x^2 // x^2)) == true + @test is_unit(L(x)) == true + + L = localization(Qx, [x^6 + 3, x^3 + 5]) + @test iszero(L(x - x)) == true + @test isone(L(x^2 + 3x)) == false + @test is_unit(L((x^3 + 5) // (x^3 + 5))) + end +end From e249d382735241463eb409742ca2c62c725597e3 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 15 Dec 2025 22:20:29 +0100 Subject: [PATCH 2/5] Add characteristic(::LocalizedEuclideanRing) --- src/generic/Misc/Localization.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generic/Misc/Localization.jl b/src/generic/Misc/Localization.jl index e38a45b454..8586276ae1 100644 --- a/src/generic/Misc/Localization.jl +++ b/src/generic/Misc/Localization.jl @@ -113,6 +113,8 @@ base_ring(L::LocalizedEuclideanRing) = L.base_ring::base_ring_type(L) parent(a::LocalizedEuclideanRingElem) = a.parent +characteristic(L::LocalizedEuclideanRing) = characteristic(base_ring(L)) + ############################################################################### # # Basic manipulation From 53510f109d82b3acbc041fe1e26408bc0c07b434 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 15 Dec 2025 22:23:27 +0100 Subject: [PATCH 3/5] Fix is_unit for LocalizedEuclideanRingElem --- src/generic/Misc/Localization.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generic/Misc/Localization.jl b/src/generic/Misc/Localization.jl index 8586276ae1..6db03290f2 100644 --- a/src/generic/Misc/Localization.jl +++ b/src/generic/Misc/Localization.jl @@ -138,7 +138,8 @@ iszero(a::LocalizedEuclideanRingElem) = iszero(data(a)) isone(a::LocalizedEuclideanRingElem) = isone(data(a)) function is_unit(a::LocalizedEuclideanRingElem{T}) where {T <: RingElem} - return isin(inv(a.data), parent(a)) + is_unit(data(a)) || return false + return isin(inv(data(a)), parent(a)) end deepcopy_internal(a::LocalizedEuclideanRingElem, dict::IdDict) = parent(a)(deepcopy_internal(data(a), dict)) From 6a7dc8347fd0f01ae3555b6888837acc66ddd453 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 15 Dec 2025 22:23:58 +0100 Subject: [PATCH 4/5] Use data(::LocalizedEuclideanRingElem) some more --- src/generic/Misc/Localization.jl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/generic/Misc/Localization.jl b/src/generic/Misc/Localization.jl index 6db03290f2..b4709646b2 100644 --- a/src/generic/Misc/Localization.jl +++ b/src/generic/Misc/Localization.jl @@ -224,7 +224,7 @@ If 'checked = false' the invertibility of $a$ is not checked and the correspondi of the Fraction Field is returned. """ function Base.inv(a::LocalizedEuclideanRingElem{T}, checked::Bool = true) where {T} - b = inv(a.data) + b = inv(data(a)) checked && (isin(b, parent(a)) || error("no unit")) return LocalizedEuclideanRingElem{T}(b, parent(a), false) end @@ -258,8 +258,8 @@ function Base.divrem(a::LocalizedEuclideanRingElem{T}, b::LocalizedEuclideanRing check_parent(a, b) L = parent(a) if L.comp - a1, s1 = ppio(numerator(a.data), L.prime) - a2, s2 = ppio(numerator(b.data), L.prime) + a1, s1 = ppio(numerator(data(a)), L.prime) + a2, s2 = ppio(numerator(data(b)), L.prime) b1 = denominator(a) b2 = denominator(b) q, r = divrem(a1 * s1 * b2, s2) @@ -277,9 +277,9 @@ end function euclid(a::LocalizedEuclideanRingElem{T}) where {T <: RingElem} L = parent(a) if L.comp - return ppio(numerator(a.data), L.prime)[1] + return ppio(numerator(data(a)), L.prime)[1] else - return ppio(numerator(a.data), L.prime)[2] + return ppio(numerator(data(a)), L.prime)[2] end end @@ -295,9 +295,9 @@ function gcd(a::LocalizedEuclideanRingElem{T}, b::LocalizedEuclideanRingElem{T}) iszero(b) && return inv(canonical_unit(a)) * a par = parent(a) if par.comp - elem = ppio(gcd(numerator(a.data), numerator(b.data)), parent(a).prime)[2] + elem = ppio(gcd(numerator(data(a)), numerator(data(b))), parent(a).prime)[2] else - elem = ppio(gcd(numerator(a.data), numerator(b.data)), parent(a).prime)[1] + elem = ppio(gcd(numerator(data(a)), numerator(data(b))), parent(a).prime)[1] end return par(elem) end @@ -307,9 +307,9 @@ function lcm(a::LocalizedEuclideanRingElem{T}, b::LocalizedEuclideanRingElem{T}) par = parent(a) (iszero(a) || iszero(b)) && return par() if par.comp - elem = ppio(lcm(numerator(a.data), numerator(b.data)), parent(a).prime)[2] + elem = ppio(lcm(numerator(data(a)), numerator(data(b))), parent(a).prime)[2] else - elem = ppio(lcm(numerator(a.data), numerator(b.data)), parent(a).prime)[1] + elem = ppio(lcm(numerator(data(a)), numerator(data(b))), parent(a).prime)[1] end return par(elem) end @@ -323,9 +323,9 @@ end function gcdx(a::LocalizedEuclideanRingElem{T}, b::LocalizedEuclideanRingElem{T}) where {T <: RingElement} check_parent(a,b) L = parent(a) - g, u, v = gcdx(numerator(a.data), numerator(b.data)) + g, u, v = gcdx(numerator(data(a)), numerator(data(b))) c = inv(canonical_unit(L(g))) - return c*L(g), c*L(u*denominator(a.data)), c*L(v*denominator(b.data)) + return c*L(g), c*L(u*denominator(data(a))), c*L(v*denominator(data(b))) end ############################################################################### @@ -401,11 +401,11 @@ Returns unit `b`::LocalizedEuclideanRingElem{T} s.th. $a$ * `b` only consists of """ function canonical_unit(a::LocalizedEuclideanRingElem{T}) where {T <: RingElem} if parent(a).comp - b = ppio(numerator(a.data), parent(a).prime)[1] + b = ppio(numerator(data(a)), parent(a).prime)[1] else - b = ppio(numerator(a.data), parent(a).prime)[2] + b = ppio(numerator(data(a)), parent(a).prime)[2] end - return parent(a)(b//denominator(a.data)) + return parent(a)(b//denominator(data(a))) end ############################################################################### From 3d2c79c9991a32a558169be863e99ddbc786a289 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 15 Dec 2025 22:20:45 +0100 Subject: [PATCH 5/5] Run conformance tests on LocalizedEuclideanRing --- src/generic/Misc/Localization.jl | 11 +++++++++++ test/generic/Localization-test.jl | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/src/generic/Misc/Localization.jl b/src/generic/Misc/Localization.jl index b4709646b2..cd11eaf03f 100644 --- a/src/generic/Misc/Localization.jl +++ b/src/generic/Misc/Localization.jl @@ -338,6 +338,17 @@ function ^(a::LocalizedEuclideanRingElem, b::Int) return parent(a)(data(a)^b) end +############################################################################### +# +# Conformance test element generation +# +############################################################################### + +function ConformanceTests.generate_element(R::LocalizedEuclideanRing) + # TODO: should sometimes create something with a denominator + return R(ConformanceTests.generate_element(base_ring(R))) +end + ############################################################################### # # Promotion rules diff --git a/test/generic/Localization-test.jl b/test/generic/Localization-test.jl index 54f2df5d08..f849589443 100644 --- a/test/generic/Localization-test.jl +++ b/test/generic/Localization-test.jl @@ -2,6 +2,11 @@ Qx, x = QQ["x"] + @testset "Conformance" begin + L = localization(Qx, x^2 + 1) + ConformanceTests.test_Ring_interface(L) + end + @testset "Constructor" begin @test parent_type(LocalizedEuclideanRingElem{elem_type(Qx)}) == LocalizedEuclideanRing{elem_type(Qx)}