diff --git a/Project.toml b/Project.toml index 23a09fe..b6acb6f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "InverseFunctions" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.16" +version = "0.1.17" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/src/functions.jl b/src/functions.jl index 136389e..77ff910 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -13,18 +13,20 @@ function square(x) end + function invpow_arg2(x::Number, p::Real) if is_real_type(typeof(x)) x ≥ zero(x) ? x^inv(p) : # x > 0 - trivially invertible isinteger(p) && isodd(Integer(p)) ? copysign(abs(x)^inv(p), x) : # p odd - invertible even for x < 0 throw(DomainError(x, "inverse for x^$p is not defined at $x")) else - # complex x^p is invertible only for p = 1/n + # complex x^p is only invertible for p = 1/n isinteger(inv(p)) ? x^inv(p) : throw(DomainError(x, "inverse for x^$p is not defined at $x")) end end function invpow_arg1(b::Real, x::Real) + # b < 0 should never happen in actual use: this check is done in inverse(f) if b ≥ zero(b) && x ≥ zero(x) log(b, x) else @@ -33,15 +35,19 @@ function invpow_arg1(b::Real, x::Real) end function invlog_arg1(b::Real, x::Real) - if b ≥ zero(b) - b^x - else - throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) - end + # exception may happen here: check cannot be done in inverse(f) because of log(Real, Complex) + b > zero(b) && !isone(b) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) + b^x end invlog_arg1(b::Number, x::Number) = b^x -invlog_arg2(b::Number, x::Number) = x^inv(b) + +function invlog_arg2(b::Real, x::Real) + # exception may happen here: check cannot be done in inverse(f) because of log(Complex, Real) + x > zero(x) && !isone(x) || throw(DomainError(x, "inverse for log($b, x) is not defined at $x")) + x^inv(b) +end +invlog_arg2(b, x) = x^inv(b) function invdivrem((q, r)::NTuple{2,Number}, divisor::Number) diff --git a/src/inverse.jl b/src/inverse.jl index a3df662..81ee5a0 100644 --- a/src/inverse.jl +++ b/src/inverse.jl @@ -159,17 +159,24 @@ inverse(::typeof(sqrt)) = square inverse(::typeof(square)) = sqrt inverse(::typeof(cbrt)) = Base.Fix2(^, 3) + inverse(f::Base.Fix2{typeof(^)}) = iszero(f.x) ? throw(DomainError(f.x, "Cannot invert x^$(f.x)")) : Base.Fix2(invpow_arg2, f.x) +inverse(f::Base.Fix2{typeof(^), <:Integer}) = isodd(f.x) ? Base.Fix2(invpow_arg2, f.x) : throw(DomainError(f.x, "Cannot invert x^$(f.x)")) inverse(f::Base.Fix2{typeof(invpow_arg2)}) = Base.Fix2(^, f.x) + +inverse(f::Base.Fix1{typeof(^), <:Real}) = f.x > zero(f.x) ? Base.Fix1(invpow_arg1, f.x) : throw(DomainError(f.x, "Cannot invert $(f.x)^x")) inverse(f::Base.Fix1{typeof(^)}) = Base.Fix1(invpow_arg1, f.x) inverse(f::Base.Fix1{typeof(invpow_arg1)}) = Base.Fix1(^, f.x) -inverse(f::Base.Fix1{typeof(log)}) = Base.Fix1(invlog_arg1, f.x) +inverse(f::Base.Fix1{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert log($(f.x), x)")) : Base.Fix1(invlog_arg1, f.x) inverse(f::Base.Fix1{typeof(invlog_arg1)}) = Base.Fix1(log, f.x) -inverse(f::Base.Fix2{typeof(log)}) = Base.Fix2(invlog_arg2, f.x) + +inverse(f::Base.Fix2{typeof(log)}) = isone(f.x) ? throw(DomainError(f.x, "Cannot invert log(x, $(f.x))")) : Base.Fix2(invlog_arg2, f.x) inverse(f::Base.Fix2{typeof(invlog_arg2)}) = Base.Fix2(log, f.x) + inverse(f::Base.Fix2{typeof(divrem)}) = Base.Fix2(invdivrem, f.x) inverse(f::Base.Fix2{typeof(invdivrem)}) = Base.Fix2(divrem, f.x) + inverse(f::Base.Fix2{typeof(fldmod)}) = Base.Fix2(invfldmod, f.x) inverse(f::Base.Fix2{typeof(invfldmod)}) = Base.Fix2(fldmod, f.x) diff --git a/test/test_inverse.jl b/test/test_inverse.jl index 398b352..79cf725 100644 --- a/test/test_inverse.jl +++ b/test/test_inverse.jl @@ -49,7 +49,7 @@ end x = rand() for f in ( foo, inv_foo, log, log2, log10, log1p, sqrt, - Base.Fix2(^, rand()), Base.Fix2(^, rand([-10:-1; 1:10])), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix1(log, 1/rand()), Base.Fix2(log, rand()), + Base.Fix2(^, 3*rand() - 0.5), Base.Fix2(^, rand(float.([-10:-1; 1:10]))), Base.Fix1(^, rand()), Base.Fix1(log, rand()), Base.Fix1(log, 1/rand()), Base.Fix2(log, rand()), ) InverseFunctions.test_inverse(f, x) end @@ -92,6 +92,17 @@ end @test_throws DomainError inverse(Base.Fix2(^, 0.51))(complex(-5)) @test_throws DomainError inverse(Base.Fix2(^, 2))(complex(-5)) InverseFunctions.test_inverse(Base.Fix2(^, 0.5), complex(-5)) + @test_throws DomainError inverse(Base.Fix2(^, 2)) + @test_throws DomainError inverse(Base.Fix2(^, -4)) + InverseFunctions.test_inverse(Base.Fix2(^, 2.0), 4) + @test_throws DomainError inverse(Base.Fix1(^, 2.0))(-4) + @test_throws DomainError inverse(Base.Fix1(^, -2.0))(4) + @test_throws DomainError inverse(Base.Fix1(^, 0))(4) + @test_throws DomainError inverse(Base.Fix1(log, -2))(4) + @test_throws DomainError inverse(Base.Fix1(log, 1))(4) + @test_throws DomainError inverse(Base.Fix2(^, 0))(4) + @test_throws DomainError inverse(Base.Fix2(log, -2))(4) + @test_throws DomainError inverse(Base.Fix2(log, 1))(4) InverseFunctions.test_inverse(Base.Fix2(^, -1), complex(-5.)) @test_throws DomainError inverse(Base.Fix2(^, 2))(-5) @test_throws DomainError inverse(Base.Fix1(^, 2))(-5) @@ -143,11 +154,10 @@ end InverseFunctions.test_inverse(sqrt, x) @test_throws DomainError inverse(sqrt)(-x) - InverseFunctions.test_inverse(Base.Fix2(^, 2), x) - @test_throws DomainError inverse(Base.Fix2(^, 2))(-x) InverseFunctions.test_inverse(Base.Fix2(^, 3), x) InverseFunctions.test_inverse(Base.Fix2(^, 3), -x) InverseFunctions.test_inverse(Base.Fix2(^, -3.5), x) + @test_throws DomainError inverse(Base.Fix2(^, 2))(-x) end @testset "dates" begin