diff --git a/src/AbsSeries.jl b/src/AbsSeries.jl index 991b66131b..58a9dc829d 100644 --- a/src/AbsSeries.jl +++ b/src/AbsSeries.jl @@ -764,13 +764,29 @@ end ############################################################################### @doc raw""" - compose(a::AbsPowerSeriesRingElem, b::AbsPowerSeriesRingElem) + compose(f::AbsPowerSeriesRingElem, g::AbsPowerSeriesRingElem; inner) -Compose the series $a$ with the series $b$ and return the result, -i.e. return $a\circ b$. The two series do not need to be in the same ring, -however the series $b$ must have positive valuation or an exception is raised. +Compose the series $a$ with the series $b$ and return the result. +- If `inner = :second`, then `f(g)` is returned and `g` must have positive valuation. +- If `inner = :first`, then `g(f)` is returned and `f` must have positive valuation. """ -function compose(a::AbsPowerSeriesRingElem, b::AbsPowerSeriesRingElem) +function compose(f::AbsPowerSeriesRingElem, g::AbsPowerSeriesRingElem; inner = nothing) + if inner === nothing + error("""compose(f, g) requires a keyword argument inner + - inner = :second yields f(g) + - inner = :first yields g(f) + Alternatively, use directly f(g) or g(f) + """) + end + + if inner == :second + return _compose_right(f, g) + else + return _compose_right(g, f) + end +end + +function _compose_right(a::AbsPowerSeriesRingElem, b::AbsPowerSeriesRingElem) valuation(b) == 0 && error("Series being substituted must have positive valuation") i = length(a) R = base_ring(a) diff --git a/src/Poly.jl b/src/Poly.jl index c0d40f7711..05cc23e894 100644 --- a/src/Poly.jl +++ b/src/Poly.jl @@ -2201,12 +2201,31 @@ function evaluate(a::PolyRingElem, b::T) where T <: RingElement end @doc raw""" - compose(a::PolyRingElem, b::PolyRingElem) + compose(f::PolyRingElem, g::PolyRingElem; inner) + +Compose the polynomial $a$ with the polynomial $b$ and return the result. +- If `inner = :right`, then `f(g)` is returned. +- If `inner = :left`, then `g(f)` is returned. +""" +function compose(f::PolyRingElem, g::PolyRingElem; inner = nothing) + if inner === nothing + error("""compose(f, g) requires a keyword argument inner + - inner = :second yields f(g) + - inner = :first yields g(f) + Alternatively, use directly f(g) or g(f) + """) + end + + if inner == :second + return _compose_right(f, g) + elseif inner == :first + return _compose_right(g, f) + else + error("Keyword inner must be :first or :second") + end +end -Compose the polynomial $a$ with the polynomial $b$ and return the result, -i.e. return $a\circ b$. -""" -function compose(a::PolyRingElem, b::PolyRingElem) +function _compose_right(a::PolyRingElem, b::PolyRingElem) i = length(a) R = base_ring(a) S = parent(b) diff --git a/src/RelSeries.jl b/src/RelSeries.jl index 853e331427..4e6c577345 100644 --- a/src/RelSeries.jl +++ b/src/RelSeries.jl @@ -991,13 +991,29 @@ end ############################################################################### @doc raw""" - compose(a::RelPowerSeriesRingElem, b::RelPowerSeriesRingElem) + compose(f::RelPowerSeriesRingElem, g::RelPowerSeriesRingElem; inner) -Compose the series $a$ with the series $b$ and return the result, -i.e. return $a\circ b$. The two series do not need to be in the same ring, -however the series $b$ must have positive valuation or an exception is raised. +Compose the series $a$ with the series $b$ and return the result. +- If `inner = :second`, then `f(g)` is returned and `g` must have positive valuation. +- If `inner = :first`, then `g(f)` is returned and `f` must have positive valuation. """ -function compose(a::RelPowerSeriesRingElem, b::RelPowerSeriesRingElem) +function compose(f::RelPowerSeriesRingElem, g::RelPowerSeriesRingElem; inner = nothing) + if inner === nothing + error("""compose(f, g) requires a keyword argument inner + - inner = :second yields f(g) + - inner = :first yields g(f) + Alternatively, use directly f(g) or g(f) + """) + end + + if inner == :second + return _compose_right(f, g) + else + return _compose_right(g, f) + end +end + +function _compose_right(a::RelPowerSeriesRingElem, b::RelPowerSeriesRingElem) valuation(b) == 0 && error("Series being substituted must have positive valuation") i = pol_length(a) R = base_ring(a) diff --git a/src/polysubst.jl b/src/polysubst.jl index e0d9eefbf5..b4500f4236 100644 --- a/src/polysubst.jl +++ b/src/polysubst.jl @@ -5,7 +5,7 @@ for T in subtypes(PolyRingElem) if parent(f) != parent(a) return subst(f, a) end - return compose(f, a) + return compose(f, a; inner = :second) end (f::T)(a::Integer) = evaluate(f, a) diff --git a/test/generic/AbsSeries-test.jl b/test/generic/AbsSeries-test.jl index a155e96a2b..a02a4cd474 100644 --- a/test/generic/AbsSeries-test.jl +++ b/test/generic/AbsSeries-test.jl @@ -783,10 +783,10 @@ end g = rand(R, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(x, g) == g - @test compose(x^2, g) == g^2 - @test compose(R(), g) == R() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(x, g; inner = :second) == g + @test compose(x^2, g; inner = :second) == g^2 + @test compose(R(), g; inner = :second) == R() end S, y = power_series_ring(ZZ, 10, "y", model=:capped_absolute) @@ -796,8 +796,8 @@ end g = rand(S, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(R(), g) == S() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(R(), g; inner = :second) == S() end # Inexact field @@ -808,10 +808,10 @@ end g = rand(R, 1:10, -10:10) - @test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g)) - @test isapprox(compose(x, g), g) - @test isapprox(compose(x^2, g), g^2) - @test isapprox(compose(R(), g), R()) + @test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second)) + @test isapprox(compose(x, g; inner = :second), g) + @test isapprox(compose(x^2, g; inner = :second), g^2) + @test isapprox(compose(R(), g; inner = :second), R()) end S, y = power_series_ring(RealField, 10, "y", model=:capped_absolute) @@ -820,8 +820,8 @@ end f2 = rand(R, 0:10, -10:10) g = rand(S, 1:10, -10:10) - @test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g)) - @test isapprox(compose(R(), g), S()) + @test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second)) + @test isapprox(compose(R(), g; inner = :second), S()) end # Non-integral domain @@ -833,10 +833,10 @@ end g = rand(R, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(x, g) == g - @test compose(x^2, g) == g^2 - @test compose(R(), g) == R() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(x, g; inner = :second) == g + @test compose(x^2, g; inner = :second) == g^2 + @test compose(R(), g; inner = :second) == R() end S, y = power_series_ring(T, 10, "y", model=:capped_absolute) @@ -846,8 +846,8 @@ end g = rand(S, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(R(), g) == S() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(R(), g; inner = :second) == S() end end diff --git a/test/generic/Poly-test.jl b/test/generic/Poly-test.jl index 99f39ee75f..8f04675095 100644 --- a/test/generic/Poly-test.jl +++ b/test/generic/Poly-test.jl @@ -1847,7 +1847,9 @@ end g = rand(R, -1:5, -10:10) h = rand(R, -1:5, -10:10) - @test compose(f, compose(g, h)) == compose(compose(f, g), h) + @test compose(f, compose(g, h; inner = :second); inner = :second) == compose(compose(f, g; inner = :second), h; inner = :second) + @test compose(f, g; inner = :first) == g(f) + @test compose(f, g; inner = :second) == f(g) end # Inexact field @@ -1858,7 +1860,7 @@ end g = rand(R, -1:5, 0:1) h = rand(R, -1:5, 0:1) - @test isapprox(compose(f, compose(g, h)), compose(compose(f, g), h)) + @test isapprox(compose(f, compose(g, h; inner = :second); inner = :second), compose(compose(f, g; inner = :second), h; inner = :second)) end # Non-integral domain @@ -1870,7 +1872,7 @@ end g = rand(R, -1:5, 0:5) h = rand(R, -1:5, 0:5) - @test compose(f, compose(g, h)) == compose(compose(f, g), h) + @test compose(f, compose(g, h; inner = :second); inner = :second) == compose(compose(f, g; inner = :second), h; inner = :second) end end diff --git a/test/generic/RelSeries-test.jl b/test/generic/RelSeries-test.jl index 3520a822d9..c0788880d5 100644 --- a/test/generic/RelSeries-test.jl +++ b/test/generic/RelSeries-test.jl @@ -806,10 +806,10 @@ end g = rand(R, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(x, g) == g - @test compose(x^2, g) == g^2 - @test compose(R(), g) == R() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(x, g; inner = :second) == g + @test compose(x^2, g; inner = :second) == g^2 + @test compose(R(), g; inner = :second) == R() end S, y = power_series_ring(ZZ, 10, "y") @@ -819,8 +819,8 @@ end g = rand(S, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(R(), g) == S() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(R(), g; inner = :second) == S() end # Inexact field @@ -831,10 +831,10 @@ end g = rand(R, 1:10, -10:10) - @test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g)) - @test isapprox(compose(x, g), g) - @test isapprox(compose(x^2, g), g^2) - @test isapprox(compose(R(), g), R()) + @test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second)) + @test isapprox(compose(x, g; inner = :second), g) + @test isapprox(compose(x^2, g; inner = :second), g^2) + @test isapprox(compose(R(), g; inner = :second), R()) end S, y = power_series_ring(RealField, 10, "y") @@ -843,8 +843,8 @@ end f2 = rand(R, 0:10, -10:10) g = rand(S, 1:10, -10:10) - @test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g)) - @test isapprox(compose(R(), g), S()) + @test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second)) + @test isapprox(compose(R(), g; inner = :second), S()) end # Non-integral domain @@ -856,10 +856,10 @@ end g = rand(R, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(x, g) == g - @test compose(x^2, g) == g^2 - @test compose(R(), g) == R() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(x, g; inner = :second) == g + @test compose(x^2, g; inner = :second) == g^2 + @test compose(R(), g; inner = :second) == R() end S, y = power_series_ring(T, 10, "y") @@ -869,8 +869,8 @@ end g = rand(S, 1:10, -10:10) - @test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g) - @test compose(R(), g) == S() + @test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second) + @test compose(R(), g; inner = :second) == S() end end