using LGCPGradientMatching
using Test

@testset "LGCPGradientMatching.jl" begin
end

# sln = ScaledLogitNormal(0, 1, 0, 5)
# clvp = CompetitiveLotkaVolterraParams(3, sln, sln, sln)
@testset "ModelConstruction" begin
    # "odelgcp.jl"
    # "model/clvlgcp.jl"
    # times = ["20000101","20080711","20101212","20070303","20010817","20030101",
    #     "20121212", "20060321","20081115","20070303","20010817","20030101"]
    # competitors = ["B", "B", "B", "B", "B", "A", "A", "A", "A", "C", "C", "C"]
    # @test_nowarn data = CompetitiveLotkaVolterraLGCPData(times, competitors, dateformat="YYYYMMDD")
    # @test_nowarn ScaledLogitNormal(0., 2., 0., 10.)
    # ode_param_priors = [
    #     ScaledLogitNormal(0., 2., 0., 10.),
    #     ScaledLogitNormal(0., 2., 5., 15.),
    #     ScaledLogitNormal(0., 2., 0., 10.)]
    # @test_nowarn obj = CompetitiveLotkaVolterraLGCP(
    #         times, competitors, dateformat="YYYYMMDD",
    #         ode_param_priors=ode_param_priors,
    #         init_μ=0.5,
    #         # init_δ=0.01
    #         )
end

@testset "Likelihood" begin
    # "ll/common.jl"
    # "ll/clvlgcp.jl"
end

@testset "Sampler" begin
    # "sampler/ess.jl"
    # "sampler/hmc.jl"
    # "sampler/mh.jl"
    # "sampler/pt.jl"
    # "sampler/rmhmc.jl"
    # "sampler/sample.jl"
end

@testset "Distribution" begin
    # "dist.jl"
    "以下の実装のテスト
    - scaled_logit
    - scaled_logistic"
    # xin ,_min, grate, midp = randn(), randn(), abs(randn()), randn()
    # _max = _min + abs(randn())
    # xout = scaled_logit(
    #     scaled_logistic(xin, _min, _max, rate=grate, mid=midp),
    #     _min, _max, rate=grate, mid=midp)
    # @test isapprox(xin, xout)

    "以下の実装のテスト
    - adjustable_softplus
    - adjustable_inverse_softplus"
    # xin , grate, midp = randn(), abs(randn()), randn()
    # xout = adjustable_inverse_softplus(adjustable_softplus(xin, grate, midp), grate, midp)
    # @test isapprox(xin, xout)

    "以下の実装をテスト
    - pdf(HalfCaucy(), x)
    - logpdf(HalfCaucy(), x)"
    # @test isapprox(pdf(Cauchy(0., 2.), 1.) * 2, pdf(HalfCauchy(0., 2.), 1.))
    # @test isapprox(logpdf(Cauchy(0., 2.), 1.) + log(2), logpdf(HalfCauchy(0., 2.), 1.))
end

@testset "Kernel" begin
    # "kernel.jl"
    # α = 2
    # 𝓁 = 0.3
    # σ = 0.1
    # x = collect(0:0.2:1)
    # x′ = collect(0.25:0.1:0.75)
    # μ = 1.
    # # rbf kernel
    # @test_nowarn rbf_kernel(log(α), log(𝓁))
    # @test_nowarn cov_rbf(log(α), log(𝓁), x)
    # @test_nowarn cov_rbf(log(α), log(𝓁), x, x′)
    # @test_nowarn pdcov_rbf(log(α), log(𝓁), x)
    # # rbf kernel + white noise
    # @test_nowarn rbf_kernel(log(α), log(𝓁), log(σ))
    # @test_nowarn cov_rbf(log(α), log(𝓁), log(σ), x)
    # @test_nowarn cov_rbf(log(α), log(𝓁), log(σ), x, x′)
    # @test_nowarn pdcov_rbf(log(α), log(𝓁), log(σ), x)
    # # cross covariance
    # @test_nowarn crosscov_rbf(log(α), log(𝓁), x)
    # # cross covariance + white noise
    # @test_nowarn crosscov_rbf(log(α), log(𝓁), log(σ), x)
    # # auto covariance
    # @test_nowarn autocov_rbf(log(α), log(𝓁), x)
    # # auto covariance + white noise
    # @test_nowarn autocov_rbf(log(α), log(𝓁), log(σ), x)
    # # gradient mean of GP
    # @test_nowarn gp_gradmean(x, μ, crosscov_rbf(log(α), log(𝓁), x), cov_rbf(log(α), log(𝓁), x))
    # @test_nowarn gp_gradmean(x, μ, crosscov_rbf(log(α), log(𝓁), x),
    #     invcov=cov_rbf(log(α), log(𝓁), x) |> inv)
    # m1 = gp_gradmean(x, μ, crosscov_rbf(log(α), log(𝓁), x), cov_rbf(log(α), log(𝓁), x))
    # m2 = gp_gradmean(x, μ, crosscov_rbf(log(α), log(𝓁), x), invcov=cov_rbf(log(α), log(𝓁), x) |> inv)
    # @test isapprox(m1, m2, atol=1e-3)
    # # gradient mean of GP with RBF kernel
    # @test_nowarn gp_rbf_gradmean(log(α), log(𝓁), x, μ)
    # @test_nowarn gp_rbf_gradmean(log(α), log(𝓁), log(σ), x, μ)
    # # gradient covariance of GP

    # # gradient covariance of GP with RBF kernel
    # @test_nowarn gp_rbf_gradcov(log(α), log(𝓁), x)
    # @test_nowarn gp_rbf_gradcov(log(α), log(𝓁), log(σ), x)
    # # predictive mean of gaussian process regression
    # kxx = cov_rbf(log(α), log(𝓁), log(σ), x, x)
    # kxx′ = cov_rbf(log(α), log(𝓁), x, x′)
    # kx′x′ = cov_rbf(log(α), log(𝓁), x′, x′)
    # @test_nowarn gp_predmean(x′, μ, kxx′, kxx)
    # @test_nowarn gp_predmean(x′, μ, kxx′, invkxx=inv(kxx))
    # m_hat1 = gp_predmean(x′, μ, kxx′, kxx)
    # m_hat2 = gp_predmean(x′, μ, kxx′, invkxx=inv(kxx))
    # @test isapprox(m_hat1, m_hat2, atol=1e-3)
    # # predictive covariance of gaussian process regression
    # @test_nowarn gp_predcov(kxx′, kx′x′, kxx)
    # @test_nowarn gp_predcov(kxx′, kx′x′, invkxx=inv(kxx))
    # Σ_hat1 = gp_predcov(kxx′, kx′x′, kxx)
    # Σ_hat2 = gp_predcov(kxx′, kx′x′, invkxx=inv(kxx))
    # @test isapprox(Σ_hat1, Σ_hat2, atol=1e-3)
    # derivative of predictive mean w.r.t x for HMC gradient calculation
end

@testset "Utility" begin
    # "utils.jl"
end

@testset "Misc" begin
    # "plot.jl"

    # "posthoc.jl"

    # "record.jl"
end

@testset "FirstDerivatives" begin
    # "derivative/1st_derivative.jl"
    # using ForwardDiff
    # mod_ = _mod

    # v = LGCPGradientMatching.get_state_vector(mod_, [:y, :x, :θ])
    # lp2 = v -> mod_.gm.targ_yxθ.lp(mod_.gm.targ_yxθ, v)
    # g_num = v -> ForwardDiff.gradient(lp2, v)
    # @show mod_.gm.targ_yxθ.g(mod_.gm.targ_yxθ, v) ≈ g_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:x, :θ])
    # lp2 = v -> mod_.gm.targ_xθ.lp(mod_.gm.targ_xθ, v)
    # g_num = v -> ForwardDiff.gradient(lp2, v)
    # @show mod_.gm.targ_xθ.g(mod_.gm.targ_xθ, v) ≈ g_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:y])
    # lp2 = v -> mod_.gm.targ_y.lp(mod_.gm.targ_y, v)
    # g_num = v -> ForwardDiff.gradient(lp2, v)
    # @show mod_.gm.targ_y.g(mod_.gm.targ_y, v) ≈ g_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:x])
    # lp2 = v -> mod_.gm.targ_x.lp(mod_.gm.targ_x, v)
    # g_num = v -> ForwardDiff.gradient(lp2, v)
    # @show mod_.gm.targ_x.g(mod_.gm.targ_x, v) ≈ g_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:θ])
    # lp2 = v -> mod_.gm.targ_θ.lp(mod_.gm.targ_θ, v)
    # g_num = v -> ForwardDiff.gradient(lp2, v)
    # @show mod_.gm.targ_θ.g(mod_.gm.targ_θ, v) ≈ g_num(v)
end

@testset "SecondDerivatives" begin
    # "derivative/2nd_derivative.jl"
    # v = LGCPGradientMatching.get_state_vector(mod_, [:y, :x, :θ])
    # g2 = v -> mod_.gm.targ_yxθ.g(mod_.gm.targ_yxθ, v)
    # H_num = v -> ForwardDiff.jacobian(g2, v)
    # @show mod_.gm.targ_yxθ.H(mod_.gm.targ_yxθ, v) ≈ H_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:x, :θ])
    # g2 = v -> mod_.gm.targ_xθ.g(mod_.gm.targ_xθ, v)
    # H_num = v -> ForwardDiff.jacobian(g2, v)
    # @show mod_.gm.targ_xθ.H(mod_.gm.targ_xθ, v) ≈ H_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:y])
    # g2 = v -> mod_.gm.targ_y.g(mod_.gm.targ_y, v)
    # H_num = v -> ForwardDiff.jacobian(g2, v)
    # @show mod_.gm.targ_y.H(mod_.gm.targ_y, v) ≈ H_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:x])
    # g2 = v -> mod_.gm.targ_x.g(mod_.gm.targ_x, v)
    # H_num = v -> ForwardDiff.jacobian(g2, v)
    # @show mod_.gm.targ_x.H(mod_.gm.targ_x, v) ≈ H_num(v)

    # v = LGCPGradientMatching.get_state_vector(mod_, [:θ])
    # g2 = v -> mod_.gm.targ_θ.g(mod_.gm.targ_θ, v)
    # H_num = v -> ForwardDiff.jacobian(g2, v)
    # @show mod_.gm.targ_θ.H(mod_.gm.targ_θ, v) ≈ H_num(v)
end

@testset "ThirdDerivatives" begin
    # "derivative/3rd_derivative.jl"
end
