# This file is a part of ChangesOfVariables.jl, licensed under the MIT License (MIT).

if !isdefined(Main, :getjacobian)

import ForwardDiff

torv_and_back(V::AbstractVector{<:Real}) = V, identity
torv_and_back(x::Real) = [x], V -> V[1]
torv_and_back(x::Complex) = [real(x), imag(x)], V -> Complex(V[1], V[2])
torv_and_back(x::NTuple{N}) where N = [x...], V -> ntuple(i -> V[i], Val(N))

function torv_and_back(x::Ref)
    xval = x[]
    V, to_xval = torv_and_back(xval)
    back_to_ref(V) = Ref(to_xval(V))
    return (V, back_to_ref)
end

torv_and_back(A::AbstractArray{<:Real}) = vec(A), V -> reshape(V, size(A))

function torv_and_back(A::AbstractArray{Complex{T}, N}) where {T<:Real, N}
    RA = cat(real.(A), imag.(A), dims = N+1) 
    V, to_array = torv_and_back(RA)
    function back_to_complex(V)
        RA = to_array(V)
        Complex.(view(RA, map(_ -> :, size(A))..., 1), view(RA, map(_ -> :, size(A))..., 2))
    end
    return (V, back_to_complex)
end


function getjacobian(f, x)
    V, to_x = torv_and_back(x)
    vf(V) = torv_and_back(f(to_x(V)))[1]
    ForwardDiff.jacobian(vf, V)
end

end # !isdefined(Main, :getjacobian)


if !isdefined(Main, :foo)

foo(x) = inv(exp(-x) + 1)

end # !isdefined(Main, :foo)


if !isdefined(Main, :bar)

# with_logabsdet_jacobian for floats but not for integers 
bar(x) = x
ChangesOfVariables.with_logabsdet_jacobian(::typeof(bar), x::AbstractFloat) = (x, zero(x))

end # !isdefined(Main, :bar)
