using LinearAlgebra
using Plots
using Plots.PlotMeasures
using LaTeXStrings
using ColorSchemes

export  eigval_img,
        eigval_img_2d,
        berry_img,
        nernst_img,
        T_img,
        μ_img,
        Tμ_img

"""
    eigval_img(d,M,Ham!)

    plot eigen values. You specify the plot area by `k`
"""

function eigval_img(k :: AbstractArray ,M,Ham!)
    plt = Array{Float64,3}(undef,M,length(k),length(k))
    @inbounds Threads.@threads for x in eachindex(k)
        H1 = zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
            Ham!(k[x],k[y],H1)
            plt[:,x,y] = eigvals!(H1)
        end
    end
    p1 = plot3d(fmt=:png)
    for i in 1:M
        plot3d!(k,k, plt[i,:,:] , st = :surface)
    end
    p2 = plot(k,plt[:,:,length(k)÷2+1]', xlabel = L"k_x")
    p3 = plot(k,plt[:,length(k)÷2+1,:]', xlabel = L"k_y")
    plot(
        p1,p2,p3,layout = @layout([a [b ; c]]),size = (900,500)
    )
end

"""
    eigval_img(d,M,Ham!)

    plot eigen values. You specify the span of `k` with d
"""

function eigval_img(d :: Int,M,Ham!)
    k = -1:1/(1<<d):1
    plt = Array{Float64,3}(undef,M,length(k),length(k))
    @inbounds Threads.@threads for x in eachindex(k)
        H1 = zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
            Ham!(k[x],k[y],H1)
            plt[:,x,y] = eigvals!(H1)
        end
    end
    p1 = plot3d(fmt=:png)
    for i in 1:M
        plot3d!(k,k, plt[i,:,:] , st = :surface)
    end
    p2 = plot(k,plt[:,:,1<<d+1]', xlabel = L"k_x")
    p3 = plot(k,plt[:,1<<d+1,:]', xlabel = L"k_y")
    plot(
        p1,p2,p3,layout = @layout([a [b ; c]]),size = (900,500)
    )
end

function eigval_img_2d(k :: AbstractArray ,M,Ham!)
    plt = Array{Float64,3}(undef,M,length(k),length(k))
    @inbounds Threads.@threads for x in eachindex(k)
        H1 = zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
            Ham!(k[x],k[y],H1)
            plt[:,x,y] = eigvals!(H1)
        end
    end

    plot(
        [heatmap(k,k,plt[n,:,:]') for n in 1:M]...,size = (900,500)
    )
end

function eigval_img_2d(d :: Int ,M,Ham!)
    k = -1:1/(1<<d):1
    plt = Array{Float64,3}(undef,M,length(k),length(k))
    @inbounds Threads.@threads for x in eachindex(k)
        H1 = zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
            Ham!(k[x],k[y],H1)
            plt[:,x,y] = eigvals!(H1)
        end
    end

    plot(
        [heatmap(k,k,plt[n,:,:]') for n in 1:M]...,size = (900,500)
    )
end

function berry_img(d :: Int,M,Ham!)
    k = -1:1/(1<<d):1
    plt = Array{Float64,3}(undef,M,length(k),length(k))
    @inbounds Threads.@threads for x in eachindex(k)
        H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
             plt[:,x,y]=Berry(k[x],k[y],Ham!,H1,H2,H3)
        end
    end

    @. plt /= 1<<(2d)

    plot(
        [ heatmap(k,k,plt[i,:,:]', title = "band $(i)" , c = cgrad(:bwr)) for i in 1:M]...,
        xlabel = L"k_x" , ylabel = L"k_y",titlefontsize=8
    )
end

function berry_img(k :: AbstractArray ,M,Ham!)
    plt = Array{Float64,3}(undef,M,length(k),length(k))
    dμ = step(k)^2
    @inbounds Threads.@threads for x in eachindex(k)
        H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
             plt[:,x,y]=Berry(k[x],k[y],Ham!,H1,H2,H3)
        end
    end

    plt *= dμ
    plot(
        [ heatmap(k,k,plt[i,:,:]', title = "band $(i) " , c = cgrad(:bwr)) for i in 1:M]...,
        xlabel = L"k_x" , ylabel = L"k_y",titlefontsize=8
    )
end
"""
    nernst_img(d :: Int,T,μ,M,Ham!)
    nernst_img(k :: AbstractArray ,T,μ,M,Ham!)
"""

function nernst_img(d :: Int,T,μ,M,Ham!)
    k = -1:1/(1<<d):1
    plt = Array{Float64}(undef,length(k),length(k))
    @inbounds Threads.@threads for x in eachindex(k)
        H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
             plt[x,y]=nernst(k[x],k[y],T,μ,Ham!,H1,H2,H3)
        end
    end
    @. plt /=1<<(2d) # dxdy
    println(sum(plt))
    heatmap(k,k,plt[:,:]',
        xlabel=L"k_x",
        ylabel=L"k_y"
    )
end

function nernst_img(k :: AbstractArray ,T,μ,M,Ham!)
    plt = Array{Float64}(undef,length(k),length(k))
    dμ = step(k)^2
    @inbounds Threads.@threads for x in eachindex(k)
        H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
        @inbounds for y in eachindex(k)
             plt[x,y]=nernst(k[x],k[y],T,μ,Ham!,H1,H2,H3)
        end
    end
    @. plt *= dμ
    println(sum(plt))

    heatmap(k,k,plt[:,:]',
        xlabel=L"k_x",
        ylabel=L"k_y"
    )
end

"""
    T_img(μ,M,Ham! ; rangeT = 50:50:500 , k = -1:1/(1<<4):1)
"""

function T_img(μ,M,Ham! ; rangeT = 50:50:500 , k = -1:1/(1<<4):1)
    plt = zeros(length(rangeT))
    dμ = step(k)^2
    @inbounds for mu in μ
        @. plt = 0.0
        @inbounds Threads.@threads for T in eachindex(rangeT)
            H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
            @inbounds for x in eachindex(k)
                @inbounds for y in eachindex(k)
                    plt[T] += nernst(k[x],k[y],rangeT[T],mu,Ham!,H1,H2,H3)
                end
            end
        end
        plot!(rangeT,plt.*dμ,label= L"\mu = %$(mu)",xlabel = "logT",ylabel="α",marker=true,ms=4,msw=0)
    end
    plot!()
end

"""
    μ_img(T,M,Ham!;rangeμ = -5:0.5:5 ,k = -1:1/(1<<4):1)
"""
function μ_img(T,M,Ham!;rangeμ = -5:0.5:5 ,k = -1:1/(1<<4):1)
    plt = zeros(length(rangeμ))
    dμ = step(k)^2
    @inbounds Threads.@threads for μ in eachindex(rangeμ)
        H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
        @inbounds for x in eachindex(k)
            @inbounds for y in eachindex(k)
                plt[μ] += nernst(k[x],k[y],T,rangeμ[μ],Ham!,H1,H2,H3)
            end
        end
    end
    plot!(rangeμ,plt.*dμ,label= L"T = %$(T)",xlabel = "μ",ylabel="α",marker=true,ms=4,msw=0)
end

"""
    Tμ_img(M,Ham!; rangeT = 100:100:1000 , rangeμ = -5:0.5:5 , k = -1:1/(1<<4):1)
"""

function Tμ_img(M,Ham!; rangeT = 100:100:1000 , rangeμ = -5:0.5:5 , k = -1:1/(1<<4):1)
    plt = zeros(length(rangeT),length(rangeμ))
    dμ = step(k)^2
    @inbounds Threads.@threads for T in eachindex(rangeT)
        H1, H2, H3 = zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M) , zeros(ComplexF64,M,M)
        @inbounds for μ in eachindex(rangeμ)
            @inbounds for x in eachindex(k)
                @inbounds for y in eachindex(k)
                    plt[T,μ] += nernst(k[x],k[y],rangeT[T],rangeμ[μ],Ham!,H1,H2,H3)
                end
            end
        end
    end
    @. plt *= dμ
    heatmap(rangeT,rangeμ,plt',
        c=cgrad(:bwr),
        xlabel = "T",ylabel="μ"
    )
end


using Test
using Polyhedra
using JuMP
const MOIB = MOI.Bridges

@testset "Continuous Linear problems with VRepOptimizer" begin
    optimizer = VRepOptimizer{Float64}()
    @testset "SolverName" begin
        @test MOI.get(optimizer, MOI.SolverName()) == "VRep"
    end
    cache = MOIU.UniversalFallback(Polyhedra._MOIModel{Float64}())
    cached = MOIU.CachingOptimizer(cache, optimizer)
    bridged = MOIB.full_bridge_optimizer(cached, Float64)
    config = MOI.Test.Config(
        exclude=Any[
            MOI.ConstraintBasisStatus,
            MOI.VariableBasisStatus,
            MOI.ConstraintDual,
            MOI.DualObjectiveValue,
            MOI.ObjectiveBound,
        ],
    )
    MOI.Test.runtests(
        bridged,
        config,
        exclude = String[
            "test_attribute_RawStatusString",
            "test_attribute_SolveTimeSec",
            "test_attribute_SolverVersion",
            #   MathOptInterface.jl issue #1431
            "test_model_LowerBoundAlreadySet",
            "test_model_UpperBoundAlreadySet",
        ],
    )
end
@testset "simplex chebyshev center with $T" for T in [Float64, Rational{BigInt}]
    h = HalfSpace([-1, 0], 0) ∩ HalfSpace([0, -1], 0) ∩ HyperPlane([1, 1], 1)
    lib = DefaultLibrary{T}(VRepOptimizer{T})
    poly = polyhedron(h, lib)
    center, radius = chebyshevcenter(poly)
    @test center ≈ [1/2, 1/2]
    @test radius ≈ 1/2
    @test dim(poly) == 1
end


@testset "gp" begin

    # # Test various utility functions.
    # let

    #     rng, N, N′, D = MersenneTwister(123456), 4, 5, 6
    #     x, x′ = DataSet(randn(rng, N)), DataSet(randn(rng, N′))
    #     X, X′ = DataSet(randn(rng, D, N)), DataSet(randn(rng, D, N′))
    #     μ, k = ConstantMean(randn(rng)), EQ()
    #     kc = Stheno.LhsCross(x->sum(abs2, x), k)

    #     @test Stheno.permutedims(x) == x'
    # end

    # Check various ways to construct a GP do what you would expect.
    let
        m = 5.1
        @test mean(GP(m, EQ(), GPC())) == ConstantMean(m)
        @test mean(GP(zero(m), EQ(), GPC())) === ZeroMean{typeof(m)}()
        @test mean_vec(GP(m, FiniteKernel(EQ(), randn(10)), GPC())) == fill(m, 10)
        @test mean(GP(zero(m), FiniteKernel(EQ(), randn(10)), GPC())) ==
            zero(FiniteMean(ConstantMean(m), randn(10)))

        x = randn(10)
        @test map(mean(GP(sin, EQ(), GPC())), x) == sin.(x)
    end

    # Test the creation of indepenent GPs.
    let rng = MersenneTwister(123456)

        # Specification for two independent GPs.
        gpc = GPC()
        μ1, μ2 = ConstantMean(1.0), ZeroMean{Float64}()
        k1, k2 = EQ(), Linear(5)
        f1, f2 = GP(μ1, k1, gpc), GP(μ2, k2, gpc)

        @test mean(GP(EQ(), GPC())) == ZeroMean{Float64}()

        @test mean(f1) == μ1
        @test mean(f2) == μ2

        @test kernel(f1) == k1
        @test kernel(f2) == k2

        @test kernel(f1, f1) == k1
        @test kernel(f1, f2) == ZeroKernel{Float64}()
        @test kernel(f2, f1) == ZeroKernel{Float64}()
        @test kernel(f2, f2) == k2
    end

    # Test conversion and promotion of non-GPs to GPs.
    let
        gpc = GPC()
        x, f, g = 5.0, sin, GP(ConstantMean(1.0), EQ(), gpc)
        @test convert(GP, x, gpc) == GP(ConstantMean(x), ZeroKernel{Float64}(), gpc)
        @test convert(GP, f, gpc) == GP(CustomMean(f), ZeroKernel{Float64}(), gpc)
        @test promote(x, g) == (convert(GP, x, gpc), g)
        @test promote(f, g) == (convert(GP, f, gpc), g)
    end

    # Test deterministic properties of `rand`.
    let
        rng, N, D = MersenneTwister(123456), 10, 2
        X, x, μ, k = ColsAreObs(randn(rng, D, N)), randn(rng, N), ConstantMean(1), EQ()
        fX = GP(FiniteMean(μ, X), FiniteKernel(k, X), GPC())
        fx = GP(FiniteMean(μ, x), FiniteKernel(k, x), GPC())

        # Check that single-GP samples have the correct dimensions.
        @test length(rand(rng, fX)) == length(X)
        @test size(rand(rng, fX, 10)) == (length(X), 10)

        @test length(rand(rng, fx)) == length(x)
        @test size(rand(rng, fx, 10)) == (length(x), 10)
    end

    # Test statistical properties of `rand`.
    let
        rng, N, D, μ0, S = MersenneTwister(123456), 10, 2, 1, 100_000
        X = ColsAreObs(randn(rng, D, N))
        μ, k = FiniteMean(ConstantMean(μ0), X), FiniteKernel(EQ(), X)
        f = GP(μ, k, GPC())

        # Check mean + covariance estimates approximately converge for single-GP sampling.
        f̂ = rand(rng, f, S)
        @test maximum(abs.(mean(f̂; dims=2) - AV(μ))) < 1e-2

        Σ′ = (f̂ .- AV(μ)) * (f̂ .- AV(μ))' ./ S
        @test mean(abs.(Σ′ - Matrix(cov(f)))) < 1e-2
    end

    # Test logpdf + elbo do something vaguely sensible + that elbo converges to logpdf.
    using Distributions: MvNormal, PDMat
    let
        rng, N, D, σ, gpc = MersenneTwister(123456), 5, 2, 1e-1, GPC()
        X = ColsAreObs(rand(rng, D, N))
        μ = FiniteMean(ConstantMean(1.0), X)
        kf, ky = FiniteKernel(EQ(), X), FiniteKernel(EQ() + Noise(σ^2), X)
        f = GP(μ, kf, gpc)
        y = GP(μ, ky, gpc)
        ŷ = rand(rng, y)

        # Check that logpdf returns the correct type and roughly agrees with Distributions.
        Σ = Stheno.unbox(pairwise(ky, eachindex(ky)) + 1e-6I)
        @test logpdf(y, ŷ) isa Real
        @test logpdf(y, ŷ) ≈ logpdf(MvNormal(Vector(AV(μ)), Σ), ŷ)

        # Ensure that the elbo is close to the logpdf when appropriate.
        @test elbo(f, ŷ, f, σ) isa Real
        @test elbo(f, ŷ, f, σ) < logpdf(y, ŷ)
        @test abs(elbo(f, ŷ, f, σ) - logpdf(y, ŷ)) < 1e-3
    end
end


using Test

using StaticArrays

using Polyhedra

include("solvers.jl")

@testset "Redundancy removal" begin
    @testset "LP-based" begin
        @testset "HRepresentation with $(typeof(x))" for (x, y, z, w) in [
            ([1, 0], [0, 1], [1, 1], [0, 0]),
            ([1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0]),
            ((@SVector [1, 0]), (@SVector [0, 1]), (@SVector [1, 1]), (@SVector [0, 0]))]
            T = eltype(x)
            AT = typeof(x)
            d = Polyhedra.FullDim(x)
            @testset "HAffineSpace" begin
                for hr in [hrep(typeof(HyperPlane(x, one(T)))[]; d=d),
                           intersect(HyperPlane(w, zero(T)))]
                    @test dim(hr) == 2
                    for h in [detecthlinearity(hr, lp_solver),
                              removehredundancy(hr, lp_solver)]
                        @test dim(h) == 2
                        @test h isa HyperPlanesIntersection{T, AT}
                        @test !hashyperplanes(h)
                        @test !hashalfspaces(h)
                    end
                end
                hr = intersect(HyperPlane(x, one(T)))
                @test dim(hr) == 1
                for h in [detecthlinearity(hr, lp_solver),
                          removehredundancy(hr, lp_solver)]
                    @test dim(h) == 1
                    @test h isa HyperPlanesIntersection{T, AT}
                    @test nhyperplanes(h) == 1
                    @test !hashalfspaces(h)
                end
                hr = intersect(HyperPlane(w, one(T)))
                @test dim(hr) == -1
                for h in [detecthlinearity(hr, lp_solver),
                          removehredundancy(hr, lp_solver)]
                    @test dim(h) == -1
                    @test h isa HyperPlanesIntersection{T, AT}
                    @test nhyperplanes(h) == 3
                    @test !hashalfspaces(h)
                end
            end
            @testset "Intersection" begin
                for hr in [HalfSpace(z + x, 3) ∩ HalfSpace(-x, -one(T)) ∩ HalfSpace(-2y, -2),
                           HalfSpace(x, one(T)) ∩ HalfSpace(y, one(T)) ∩ HalfSpace(-z, -2)]
                    h = detecthlinearity(hr, lp_solver, verbose=2)
                    @test h isa Polyhedra.Intersection{T, AT}
                    @test nhyperplanes(h) == 2
                    @test !hashalfspaces(h)
                end
                for hr in [HalfSpace(z, zero(T)) ∩ HalfSpace(-z, -zero(T)),
                           HalfSpace(x, one(T)) ∩ HalfSpace(-x, -one(T))]
                    h = detecthlinearity(hr, lp_solver, verbose=2)
                    @test h isa Polyhedra.Intersection{T, AT}
                    @test nhyperplanes(h) == 1
                    @test !hashalfspaces(h)
                end
                for hr in [HalfSpace(-x, one(T)) ∩ HalfSpace(-y, one(T)) ∩ HalfSpace(x, one(T)) ∩ HalfSpace(y, one(T)) ∩ HalfSpace(-x, -9),
                           HalfSpace(x, zero(T)) ∩ HalfSpace(-x, -one(T))]
                    h = detecthlinearity(hr, lp_solver, verbose=2)
                    @test h isa Polyhedra.Intersection{T, AT}
                    @test nhyperplanes(h) == 3
                    @test !hashalfspaces(h)
                end
            end
        end
        @testset "VRepresentation with $(typeof(x))" for (x, y, z, w) in [
            ([1, 0], [0, 1], [1, 1], [0, 0]),
            ([1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0]),
            ((@SVector [1, 0]), (@SVector [0, 1]), (@SVector [1, 1]), (@SVector [0, 0]))]
            T = eltype(x)
            AT = typeof(x)
            d = Polyhedra.FullDim(x)
            @testset "VLinearSpace" begin
                for vr in [vrep(typeof(Line(x))[]; d=d),
                           convexhull(Line(w))]
                    for v in [detectvlinearity(vr, lp_solver),
                              removevredundancy(vr, lp_solver)]
                        @test v isa LinesHull{T, AT}
                        @test !haspoints(v)
                        @test !hasrays(v)
                        @test !haslines(v)
                    end
                end
                vr = convexhull(Line(x))
                for v in [detectvlinearity(vr, lp_solver),
                          removevredundancy(vr, lp_solver)]
                    @test v isa LinesHull{T, AT}
                    @test npoints(v) == 1
                    @test !hasrays(v)
                    @test nlines(v) == 1
                end
                for vr in [convexhull(Line(x), Line(y)),
                           convexhull(Line(x), Line(y), Line(z)),
                           convexhull(Line(x), Line(z), Line(y)),
                           convexhull(Line(x), Line(z), Line(z))]
                    for v in [detectvlinearity(vr, lp_solver),
                              removevredundancy(vr, lp_solver)]
                        @test v isa LinesHull{T, AT}
                        @test npoints(v) == 1
                        @test !hasrays(v)
                        @test nlines(v) == 2
                    end
                end
                vrepm1 = Polyhedra.emptyspace(vr)
                @test vrepm1 === @inferred removevredundancy(vrepm1, lp_solver)
            end
            @testset "Polyhedra.RaysHull" begin
                vr = vrep(typeof(Line(x))[], typeof(Ray(x))[]; d=d)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test !haspoints(rm)
                @test !hasrays(rm)
                @test !haslines(rm)
                vr = convexhull(Ray(x))
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test npoints(rm) == 1
                @test nrays(rm) == 1
                @test !haslines(rm)
                vr = convexhull(Ray(x), Ray(y))
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test npoints(rm) == 1
                @test nrays(rm) == 2
                @test !haslines(rm)
                for vr in [convexhull(Ray(x), Ray(y), Ray(z)),
                           convexhull(Ray(x), Ray(z), Ray(y)),
                           convexhull(Ray(z), Ray(x), Ray(y))]
                    rm = removevredundancy(vr, lp_solver)
                    @test rm isa Polyhedra.RaysHull{T, AT}
                    @test npoints(rm) == 1
                    @test nrays(rm) == 2
                    rs = collect(rays(rm))
                    @test rs[1] == Ray(x)
                    @test rs[2] == Ray(y)
                    @test !haslines(rm)
                end
                vr = convexhull(Ray(x), Ray(y), Ray(-z))
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test npoints(rm) == 1
                @test nrays(rm) == 3
                @test !haslines(rm)
                vr = convexhull(Ray(x), Ray(y), Line(z))
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test npoints(rm) == 1
                @test nrays(rm) == 2
                @test nlines(rm) == 1
                vr = convexhull(Ray(x), Ray(-y), Line(z))
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test npoints(rm) == 1
                @test nrays(rm) == 1
                @test nlines(rm) == 1
                vr = conichull(Ray(x), Ray(y), Ray(-z))
                rm = detectvlinearity(vr, lp_solver)
                @test rm isa Polyhedra.RaysHull{T, AT}
                @test npoints(rm) == 1
                @test nrays(rm) == 0
                @test nlines(rm) == 2
            end
            @testset "Polyhedra.PointsHull" begin
                vr = vrep(typeof(x)[]; d=d)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.PointsHull{T, AT}
                @test !haspoints(rm)
                @test !hasrays(rm)
                @test !haslines(rm)
                vr = convexhull(x)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.PointsHull{T, AT}
                @test npoints(rm) == 1
                @test !hasrays(rm)
                @test !haslines(rm)
                vr = convexhull(x, y)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.PointsHull{T, AT}
                @test npoints(rm) == 2
                @test !hasrays(rm)
                @test !haslines(rm)
                for vr in [convexhull(2x, 2y, z),
                           convexhull(2x, z, 2y),
                           convexhull(z, 2x, 2y)]
                    rm = removevredundancy(vr, lp_solver)
                    @test rm isa Polyhedra.PointsHull{T, AT}
                    @test npoints(rm) == 2
                    ps = collect(points(rm))
                    @test ps[1] == 2x
                    @test ps[2] == 2y
                    @test !hasrays(rm)
                    @test !haslines(rm)
                end
                vr = convexhull(x, y, z)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.PointsHull{T, AT}
                @test npoints(rm) == 3
                @test !hasrays(rm)
                @test !haslines(rm)
                vr = convexhull(x, y, -z)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.PointsHull{T, AT}
                @test npoints(rm) == 3
                @test !hasrays(rm)
                @test !haslines(rm)
                vr = convexhull(x, y) + Ray(z)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.Hull{T, AT}
                @test npoints(rm) == 2
                @test nrays(rm) == 1
                @test !haslines(rm)
                vr = convexhull(x, y) + Line(z)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.Hull{T, AT}
                @test npoints(rm) == 2
                @test !hasrays(rm)
                @test nlines(rm) == 1
                vr = convexhull(x, -y) + Ray(z)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.Hull{T, AT}
                @test npoints(rm) == 1
                @test first(points(rm)) == -y
                @test nrays(rm) == 1
                @test !haslines(rm)
                vr = convexhull(x, -y) + Line(z)
                rm = removevredundancy(vr, lp_solver)
                @test rm isa Polyhedra.Hull{T, AT}
                @test npoints(rm) == 1
                @test !hasrays(rm)
                @test nlines(rm) == 1
            end
        end
    end
    @testset "Representation-based" begin
        @testset "isredundant" begin
            @test isredundant(HyperPlane([0, 1], 0) ∩ HalfSpace([1, 1], 1), [0, 0], nl=0)
            @test !isredundant(HalfSpace([1, 1], 1) ∩ HyperPlane([0, 1], 0) ∩ HalfSpace([-1, -1], 1), [1, 0], nl=0)
            #@test !ishredundant(convexhull(SymPoint([1, 0])), HyperPlane([0, 1], 0), d=1)
            #@test isredundant(convexhull(SymPoint([1, 0])), HalfSpace([0, 1], 1), d=1)
            @test isredundant(HalfSpace([1, 1], 2) ∩ HalfSpace([-1, 0], 0) ∩ HalfSpace([0, -1], 0), [1, 1], nl=0)
            @test !isredundant(HalfSpace([1, 1], 2) ∩ HalfSpace([-1, 0], 0) ∩ HalfSpace([0, -1], 0), [1, 1], nl=0, strongly=true)
        end
#    @testset "Non-redundant SymPoint" begin
#        vr = convexhull(SymPoint([-1, 0]), SymPoint([0, 1]))
#        hr = HalfSpace([-1, -1], 1) ∩ HalfSpace([1, -1], 1) ∩ HalfSpace([1, 1], 1) ∩ HalfSpace([-1, 1], 1)
#        vr = @inferred Polyhedra.removevredundancy(vr, hr)
#        @test vr isa Polyhedra.PointsHull{2,Int,Vector{Int}}
#        @test collect(sympoints(vr)) == [SymPoint([-1, 0]), SymPoint([0, 1])]
#        @test !haspoints(vr)
#        @test !hasallrays(vr)
#    end
        @testset "Point" begin
            # x - y ≤  1
            # x + y ≥ -1
            hr = HalfSpace([-1, -1], 1) ∩ HalfSpace([1, -1], 1)
            # [-1, 0] is weakly redundant as it belongs to the facet x + y = -1
            @test isredundant(hr, [-1, 0], nl=0)
            @test !isredundant(hr, [-1, 0], strongly=true, nl=0)
            # [0, -1] is not redundant as it is an extreme point
            @test !isredundant(hr, [0, -1], nl=0)
            @test !isredundant(hr, [0, -1], strongly=true, nl=0)
            # [0, 0] is strongly redundant as it is in the relative interior
            @test isredundant(hr, [0, 0], nl=0)
            @test isredundant(hr, [0, 0], strongly=true, nl=0)
            vr = convexhull([-1, 0], [0, -1], [0, 0]) + conichull([1, 1], [-1, 1])
            @test collect(removevredundancy(points(vr), hr, nl=0)) == [[0, -1]]
            @test collect(removevredundancy(points(vr), hr, strongly=true, nl=0)) == [[-1, 0], [0, -1]]
            vrr = Polyhedra.removevredundancy(vr, hr)
            @test collect(points(vrr)) == [[0, -1]]
            @test !haslines(vrr)
            @test collect(rays(vrr)) == [Ray([1, 1]), Ray([-1, 1])]
            vrr = Polyhedra.removevredundancy(vr, hr, strongly=true)
            @test collect(points(vrr)) == [[-1, 0], [0, -1]]
            @test !haslines(vrr)
            @test collect(rays(vrr)) == [Ray([1, 1]), Ray([-1, 1])]
            p = polyhedron(vr, DefaultLibrary{Rational{BigInt}}(Polyhedra.OppositeMockOptimizer))
            Polyhedra.computehrep!(p)
            removevredundancy!(p, strongly=true)
            @test collect(points(p)) == [[-1, 0], [0, -1]]
            @test !haslines(p)
            @test collect(rays(p)) == [Ray([1, 1]), Ray([-1, 1])]
            removevredundancy!(p, planar=false)
            @test collect(points(p)) == [[0, -1]]
            @test !haslines(p)
            @test collect(rays(p)) == [Ray([1, 1]), Ray([-1, 1])]
        end
#    @testset "Split SymPoint" begin
#        vr = convexhull(SymPoint([-1, 0]), SymPoint([0, 1])) + conichull([1, 1], [-1, 1])
#        hr = HalfSpace([-1, -1], 1) ∩ HalfSpace([1, -1], 1)
#        vr = Polyhedra.removevredundancy(vr, hr)
#        @test !hassympoints(vr)
#        @test collect(points(vr)) == [[0, -1]]
#        @test !haslines(vr)
#        @test collect(rays(vr)) == [Ray([1, 1]), Ray([-1, 1])]
#
#        vr = convexhull(SymPoint([1, 0]), SymPoint([0, 1])) + conichull([-1, 1])
#        hr = HalfSpace([-1, -1], 1) ∩ HalfSpace([1, -1], 1) ∩ HalfSpace([1, 1], 1)
#        vr = Polyhedra.removevredundancy(vr, hr)
#        @test !hassympoints(vr)
#        @test collect(points(vr)) == [[1, 0], [0, -1]]
#        @test !haslines(vr)
#        @test collect(rays(vr)) == [Ray([-1, 1])]
#    end
    end
end
@testset "Duplicate removal" begin
    h = removeduplicates(hrep([1 1; -1 -1; 1 0; 0 -1], [1, -1, 1, 0]))
    @test nhyperplanes(h) == 1
    @test first(hyperplanes(h)) == HyperPlane([1, 1], 1)
    @test first(hyperplanes(h)) == HyperPlane([-1, -1], -1)
    @test nhalfspaces(h) == 1
    h = removeduplicates(hrep([1 1; -1 -1], [2, -1]))
    @test !hashyperplanes(h)
    @test nhalfspaces(h) == 2
#    v = removeduplicates(convexhull([1, 2], SymPoint([1, 2]), [-1, 1]))
#    @test typeof(v) == Polyhedra.PointsHull{2,Int,Vector{Int}}
#    @test collect(sympoints(v)) == [SymPoint([1, 2])]
#    @test collect(points(v)) == [[-1, 1]]
#    @test !hasallrays(v)
    for v in (#removeduplicates(convexhull([1, 2], [2, 1], SymPoint([1, 2]), [-1, 1]) + Line([0, 1])),
              #removeduplicates(Line([0, 1]) + convexhull([1, 2], [2, 1], SymPoint([1, 2]), [-1, 1])),
              removeduplicates(Line([0, 1]) + convexhull([-1, -2], [2, 1], [1, 2], [-1, 1], [-1, -2])),)
        @test typeof(v) == Polyhedra.Hull{Int, Vector{Int}, Int}
        #@test collect(sympoints(v)) == [SymPoint([1, 2])]
        @test collect(points(v)) == [[-1, -2], [2, 1], [1, 2]]
        @test !hasrays(v)
        @test collect(lines(v)) == [Line([0, 1])]
    end
end


# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md

using JuliaFEM, Test, LinearAlgebra

x1 = [2.0, 3.0, 4.0]
x2 = [6.0, 3.0, 2.0]
x3 = [2.0, 5.0, 1.0]
x4 = [4.0, 3.0, 6.0]
x5 = 0.5*(x1+x2)
x6 = 0.5*(x2+x3)
x7 = 0.5*(x3+x1)
x8 = 0.5*(x1+x4)
x9 = 0.5*(x2+x4)
x10 = 0.5*(x3+x4)
X = Dict(
    1 => x1, 2 => x2, 3 => x3, 4 => x4, 5 => x5,
    6 => x6, 7 => x7, 8 => x8, 9 => x9, 10 => x10)
u = Dict(i => zeros(3) for i in 1:10)

element = Element(Tet10, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
update!(element, "youngs modulus", 480.0)
update!(element, "poissons ratio", 1/3)
update!(element, "geometry", X)
update!(element, "displacement", u)
problem = Problem(Elasticity, "tet10", 3)
add_element!(problem, element)
time = 0.0
assemble!(problem, time)
eigs = real(eigvals(Matrix(problem.assembly.K)))
eigs_expected = [8809.45, 4936.01, 2880.56, 2491.66, 2004.85,
                 1632.49, 1264.32, 1212.42, 817.905,
                 745.755, 651.034, 517.441, 255.1, 210.955,
                 195.832, 104.008, 72.7562, 64.4376, 53.8515,
                 23.8417, 16.6354, 9.54682, 6.93361, 2.22099,
                 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
@test isapprox(eigs, eigs_expected; atol=1.0e-2)


# Base Hamiltonian
function bHamiltonian(bitnum::Int)
    diagv = Array(Complex128,2^bitnum);
    index = 1;
    for i=0:bitnum
        for j = 1:binomial(bitnum,i)
            diagv[index] = i
            index+=1
        end
    end

    return spdiagm(diagv)
end

# problem Hamiltonian for SAT problem
function pHamiltonian{M,N}(ins::Instance{M,N},n::Integer)
    return spdiagm(Complex128[1-ins(Bits(i)) for i=0:2^n-1])
end

export bHamiltonian,pHamiltonian


module Util

using AccurateArithmetic: dot_oro
using LinearAlgebra: norm, ⋅, ×, normalize
using StaticArrays: SVector

export
    sec2rad, rad2sec, normalize_angle, angle,
    dms2rad, rad2dms, sec2deg, deg2sec,
    plane_section, point_on_limb

"""
    sec2rad(sec)

Convert an angle in arcseconds to radians.

# Example

```jldoctest
julia> sec2rad(3600 * 30)
0.5235987755982988
```
"""
sec2rad(sec) = deg2rad(sec / 3600)

"""
    rad2sec(rad)
Convert an angle in radians to arcseconds.

# Example

```jldoctest
julia> rad2sec(0.5235987755982988)
107999.99999999999
```
"""
rad2sec(rad) = rad2deg(rad) * 3600

sec2deg(sec) = sec / 3600
deg2sec(deg) = deg * 3600

function normalize_angle(angle, center)
    angle - 2π * floor((angle + π - center) / 2π)
end

function dms2rad(deg, arcmin, arcsec)
    deg2rad(deg + arcmin/60 + arcsec/3600)
end

function rad2dms(rad)
    d = rad2deg(rad)
    deg = trunc(d)
    arcmin = trunc((d-deg)*60)
    arcsec = (d-deg-arcmin/60)*3600
    return deg, arcmin, arcsec
end

function angle(v1, v2)
    normprod = norm(v1) * norm(v2)
    if normprod == 0.0
        throw(DomainError())
    else
        v1v2 = v1 ⋅ v2
        threshold = normprod * 0.9999
        if v1v2 >= -threshold && v1v2 <= threshold
            return acos(v1v2 / normprod)
        else
            v3n = norm(v1 × v2)
            return v1v2 >= 0.0 ? (v3n / normprod) : π - asin(v3n / normprod)
        end
    end
end

function azimuth(v)
    atan(v[2], v[1])
end

function elevation(v)
    asin(v[3] / norm(v))
end

function vector_azel(az, el)
    saz, caz = sincos(az)
    sel, cel = sincos(el)
    SVector(caz * cel, saz * cel, sel)
end

function angular_velocity(ψ, δψ, θ, δθ, ϕ, δϕ)
    Ω₁ = δψ * sin(θ) * sin(ϕ) - δθ * cos(ϕ)
    Ω₂ = δψ * sin(θ) * cos(ϕ) + δθ * sin(ϕ)
    Ω₃ = δψ * cos(θ) + δϕ
    [Ω₁, Ω₂, Ω₃]
end

@inbounds function frame(x)
    length(x) < 3 && throw(ArgumentError("Input vector must have three elements."))
    iszero(x) && return [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]

    x̂ = normalize(x[1:3])
    ŷ = Array{Float64}(undef, 3)
    ẑ = Array{Float64}(undef, 3)

    x2 = x̂ .^ 2
    _, idx = findmin(x2)
    s1, s2, s3 = circshift(1:3, 1-idx)
    f = sqrt(x2[s2] + x2[s3])

    ŷ[s1] = 0.0
    ŷ[s2] = -x̂[s3] / f
    ŷ[s3] = x̂[s2] / f
    ẑ[s1] = f
    ẑ[s2] = -x̂[s1] * ŷ[s3]
    ẑ[s3] = x̂[s1] * ŷ[s2]

    return x̂, ŷ, ẑ
end

function orthogonal(v)
    threshold = 0.6 * norm(v)
    iszero(threshold) && throw(ArgumentError("Norm of `v` is zero."))

    x, y, z = v
    if abs(x) <= threshold
        inverse = 1 / sqrt(y^2 + z^2)
        return [0.0, inverse * z, -inverse * y]
    elseif abs(y) <= threshold
        inverse = 1 / sqrt(x^2 + z^2)
        return [-inverse * z, 0.0, inverse * x]
    end
    inverse = 1 / sqrt(x^2 + y^2)
    return [inverse * y, -inverse * x, 0.0]
end

function plane_section(ellipsoid, plane_point, plane_normal)
    u = orthogonal(plane_normal)
    v = normalize(plane_normal × u)
    ue = u ./ ellipsoid
    ve = v ./ellipsoid
    pe = plane_point ./ ellipsoid
    alpha = ue ⋅ ue
    beta = ve ⋅ ve
    gamma = dot_oro(ue, ve)
    delta = dot_oro(pe, ue)
    epsilon = dot_oro(pe, ve)
    zeta = dot_oro([pe; 1.0], [pe; -1.0])

    if abs(gamma) < floatmin(Float64)
        tan_theta = 0.0
    else
        bma = beta - alpha
        tan_theta = bma >= 0.0 ? -2gamma / (bma + sqrt(bma^2 + 4gamma^2)) : -2gamma / (bma - sqrt(bma^2 + 4gamma^2))
    end
    tan2 = tan_theta^2
    cos2 = 1 / (1 + tan2)
    sin2 = tan2 * cos2
    cos_sin = tan_theta * cos2
    co = sqrt(cos2)
    si = tan_theta * co

    denom = dot_oro([gamma, -alpha], [gamma, beta])
    tau_c = dot_oro([beta, -gamma], [delta, epsilon]) / denom
    nu_c = dot_oro([alpha, -gamma], [epsilon, delta]) / denom

    twogcs = 2 * gamma * cos_sin
    A = alpha * cos2 + beta * sin2 + twogcs
    B = alpha * sin2 + beta * cos2 - twogcs
    F = (alpha * tau_c + 2 * (gamma * nu_c + delta)) * tau_c + (beta * nu_c + 2 * epsilon) * nu_c + zeta
    l = sqrt(-F / A)
    m = sqrt(-F / B)

    isnan(l + m) && return nothing

    if l > m
        return plane_point .+ tau_c .* u .+ nu_c .* v, co .* u .+ si .* v, -si .* u .+ co .* v, l, m
    else
        return plane_point .+ tau_c .* u .+ nu_c .* v, si .* u .- co .* v, co .* u .+ si .* v, m, l
    end
end

distance(v1, v2) = norm(v1 .- v2)

function isinside(ellipsoid, point)
    scaled = ellipsoid .- point
    return scaled ⋅ scaled <= 1.0
end

function point_on_limb(ellipsoid, observer, outside)
    isinside(ellipsoid, observer) && throw(ArgumentError("`observer` is inside `ellipsoid`."))

    normal = observer × outside
    center, u, v, a, b = plane_section(ellipsoid, zeros(3), normal)
    a2 = a^2
    b2 = b^2
    delta = observer - center
    xo = delta ⋅ u
    yo = delta ⋅ v
    xo2 = xo^2
    yo2 = yo^2
    alpha = a2 * yo2 + b2 * xo2
    beta = a2 * b2 * xo
    gamma = a2 * a2 * (b2 - yo2)
    sq = sqrt(beta^2 - alpha * gamma)
    if beta > 0
        s = beta + sq
        xt1 = s / alpha
        xt2 = gamma / s
    else
        s = beta - sq
        xt1 = gamma / s
        xt2 = s / alpha
    end
    t1 = center .+ xt1 .* u .+ (b2 * (a2 - xt1 * xo) / (a2 * yo)) .* v
    t2 = center .+ xt2 .* u .+ (b2 * (a2 - xt2 * xo) / (a2 * yo)) .* v
    return distance(t1, outside) <= distance(t2, outside) ? t1 : t2
end

end


# This could be refactored in to a different project with all the modeling stuff

""" Compute expectation of the competition field in the window using quadrature and FFT.
"""
function poissoncorrection(d::Discretization, f)
    poissoncorrection(d.window, length(d.gridx), f)
end
function poissoncorrection(window, nx, f)
    (x0, x1), (y0, y1) = window.x, window.y
    dx = x1-x0
    dy = y1-y0
    ny = round(Int, nx/dx*dy)

    fk(x1, x2) = f(sqrt(x1^2+x2^2))
    f1 = Matrix{Float64}(undef, 2nx-1, 2ny-1)
    # for j in axes(f1, 2)
    #     for i in axes(f1, 1)
    #         f1[i, j] = fk((i-nx)*dx/nx, (j-ny)*dy/ny)
    #     end
    # end
    for j in 1:ny #axes(f1, 2)
        for i in 1:j #1:nx #axes(f1, 1)
            f1[i, j] = fk((i-nx)*dx/nx, (j-ny)*dy/ny)
            f1[j, i] = f1[i, j]
        end
    end
    for j in 1:ny #axes(f1, 2)
        for i in 1:nx #axes(f1, 1)
            f1[2nx-i, j] = f1[i, j]
            f1[2nx-i, 2ny-j] = f1[i, j]
            f1[i, 2ny-j] = f1[i, j]
        end
    end
    f0 = zero(f1)
    s2 = dx/nx*dy/ny
    for j in 1:ny
        for i in 1:nx
            f0[i, j] = s2
        end
    end
    # N = 2*gridn # The padding neceassary for periodicity
    # f1 = [fk(s*min(x, N-x), s*min(y, N-y)) for x in 0:1:N-1, y in 0:1:N-1]
    # f0 = [ifelse(x <= gridn && y <= gridn, 1.0*s^2, 0.0) for x in 1:N, y in 1:N]

    f2 = rfft(f1).*rfft(f0)
    fi = irfft(f2, size(f0, 1))
    I, E = quadgk(r -> r*f(r), 0.0, Inf)
    if !isfinite(I)
        error("Failed to integrate competition kernel.")
    end

    2*pi*I .- fi[nx:end, ny:end]
end


using PyPlot
using LinearAlgebra
using Statistics
using Random
using NLopt
using UKrig

##
# data grid and interpolation grid
n₀ = 100 # grid evals
n₁ = 500  # obs points
x₁ = rand(n₁)
y₁ = rand(n₁)

sidex₀ = range(-0.1, 1.1, length=n₀)
sidey₀ = range(-0.1, 1.1, length=n₀)
x₀ = sidex₀ .+ 0 .* sidey₀'
y₀ = 0 .* sidex₀ .+ sidey₀'

##
# set the simulation truth parameters
σεₒ = 0.1
σzₒ = 1.0
ρₒ  = 0.2
νₒ  = 1.25
mₒ  = 2
βₒ  = 2 * (rand(mₒ) .- 1/2)

##
# simulate data
dmat = UKrig.distmat((x₁,y₁), (x₁,y₁))
Σ₁ = (σzₒ^2) .* Mnu.(dmat ./ ρₒ, νₒ)
L₁ = cholesky(Σ₁).L
Z₁ = L₁ * randn(n₁)
Y₁ = Z₁ # .+ fpx.(1:mₒ,x₁')' * βₒ
data = Y₁ + σεₒ .* randn(n₁)

## ===============================
#

musr = 2
ν    = 1.25

loglike_inv_nu1, loglike_chol_nu1, wgrad_loglike_inv_nu1 = loglike_Gnu(
	data, x₁, y₁;
	musr=musr,
	ν=ν, # ≈ differentiability
)

#=
loglike_inv_nu1(0.1, 2.0)
loglike_chol_nu1(0.1, 2.0)
wgrad_loglike_inv_nu1(0.1, 2.0)
ϵ = 1e-7
(loglike_inv_nu1(0.1+ϵ, 2.0) - loglike_inv_nu1(0.1-ϵ, 2.0))/(2ϵ)
(loglike_inv_nu1(0.1, 2.0+ϵ) - loglike_inv_nu1(0.1, 2.0-ϵ))/(2ϵ)
=#

#LN_algm = [:LN_BOBYQA, :LN_COBYLA, :LN_PRAXIS, :LN_NELDERMEAD, :LN_SBPLX]
#LD_algm = [:LD_MMA, :LD_SLSQP, :LD_LBFGS, :LD_TNEWTON]
opt1 = Opt(:LN_BOBYQA, 2) # second arg is the number of variables to optimize
opt2 = Opt(:LN_BOBYQA, 2) # second arg is the number of variables to optimize
opt3 = Opt(:LD_LBFGS, 2) # second arg is the number of variables to optimize
opt1.max_objective = (x,grad) -> loglike_inv_nu1(x[1],x[2])
opt2.max_objective = (x,grad) -> loglike_chol_nu1(x[1],x[2])
opt3.max_objective = function (x,grad)
	ll, grad1, grad2 = wgrad_loglike_inv_nu1(x[1],x[2])
	if length(grad)>0
		grad[1]=grad1
		grad[2]=grad2
	end
	return ll
end
opt1.lower_bounds = [0.0, 0.0]
opt2.lower_bounds = [0.0, 0.0]
opt3.lower_bounds = [0.0, 0.0]
opt1.maxtime = 10 # in seconds
opt2.maxtime = 10
opt3.maxtime = 10
# opt1.maxeval = 50
# opt2.maxeval = 50
# opt3.maxeval = 50
# opt1.ftol_abs = 0.1 * sqrt(length(data)) / sqrt(2)
# opt2.ftol_abs = 0.1 * sqrt(length(data)) / sqrt(2)
# opt3.ftol_abs = 0.1 * sqrt(length(data)) / sqrt(2)
xinit1 = [0.1, 2.0]
xinit2 = [0.1, 2.0]
xinit3 = [0.1, 2.0]
optf1, optx1, ret1 = optimize!(opt1, xinit1)
optf2, optx2, ret2 = optimize!(opt2, xinit2)
optf3, optx3, ret3 = optimize!(opt3, xinit3)
@show sqrt(length(data))/√2

σe = optx1[1]
σg = optx1[2]

rng_σe = σe |> x->range(x - .05 * x, x + .05 * x, length=50)
rng_σg = σg |> x->range(x - .15 * x, x + .15 * x, length=50)
llmat = loglike_chol_nu1.(rng_σe,rng_σg')
pcolor(rng_σe, rng_σg, llmat)

krig1 = krig_Gnu(
	data, x₁, y₁;
	musr=musr,
	ν=ν,
	σe=σe,
	σg=σg,
)

figure()
scatter(x₁, y₁ ,c=data)

figure()
pcolor(x₀, y₀, krig1.(x₀, y₀))


# -*- coding: utf-8 -*-
using LightGraphs
using SemanticModels
using SemanticModels.ModelTools.CategoryTheory
using SemanticModels.ModelTools.PetriModels
using SemanticModels.ModelTools.GraphModels
using ModelingToolkit
using Petri

# # Example

n = 3
a = smallgraph(:house)
b = StarGraph(n)

f = FinSetMorph([5,3,4])
@assert FinSetMorph([5,4,3,2,1])(model(GraphModel, a)).model |>
    adjacency_matrix |>
    collect == [0 1 1 0 0;
                1 0 1 1 0;
                1 1 0 0 1;
                0 1 0 0 1;
                0 0 1 1 0]

# +
# Create a decorated morphism for a
g = FinSetMorph(1:5, [5,4])
# Add graph a as decoration of morphism f
dec_g = Decorated(g, model(GraphModel, a))
# Create a decorated morphism for b
f = FinSetMorph(1:3, [1,2])
# Add graph b as decoration of morphism g
dec_f = Decorated(f, model(GraphModel, b))
# Create the span of decorated morphisms
s = Span(dec_f,dec_g)
# Solve for the decorated cospan that solves the pushout defined by the span
H = CategoryTheory.pushout(s)

dec = decorations(H, GraphModel)[1]

@show collect(edges(dec.model))

# +
@variables S, I, R, I′

# Create S -> I -> R model
sir = Petri.Model([S, I, R], [(S+I, 2I), (I,R)])
sir_model = model(PetriModel, sir)

# Create Susceptible to 2 diseases model
sii = Petri.Model([S, I, I′], [(S+I,  2I ), (S+I′, 2I′)])
sii_model = model(PetriModel, sii)

# Decorate a finite set with SIR
f = FinSetMorph(1:3, [1, 2])
dec_f = Decorated(f, sir_model)

# Decorate a finite set with SII
g = FinSetMorph(1:3, [1, 2])
dec_g = Decorated(g, sii_model)

# Create a span of decorated morphisms
s = Span(dec_f, dec_g)

# Solve the pushout that combines the two Petri decorations
H = CategoryTheory.pushout(s)

map(petri->println(petri.model.S, "\n", petri.model.Δ), decorations(H, PetriModel))
# -


############################################################################################
#                              WALL CONSTRAINT          								   #
############################################################################################
"""
	WallConstraint{P,T}

Constraint of the form

	   NO constraint violation
-----------------------------------------
              p1 * ///
                 |////
  NO constraint  |////        constraint
  violation      |------> v   violation
                 |////
                 |////
              p2 * ///
-----------------------------------------
	   NO constraint violation

`` (x - p1)^T v \\leq 0 ``
where ``x = [x[x], x[y]]``, ``p1 = [x1,y1]``, ``p2 = [x2,y2]`` ``v = [xv,yv]``.
The ordering of p1 and p2 does not matter.

# Constructor:
```julia
WallConstraint(n, x1::SVector{P}, y1::SVector{P}, x2::SVector{P}, y2::SVector{P}, xv::SVector{P}, yv::SVector{P}, x=1, y=2)
```
"""
struct WallConstraint{P,T} <: TrajectoryOptimization.StateConstraint
	n::Int
	x1::SVector{P,T}
	y1::SVector{P,T}
	x2::SVector{P,T}
	y2::SVector{P,T}
	xv::SVector{P,T}
	yv::SVector{P,T}
	x::Int  # index of x-state
	y::Int  # index of y-state
	function WallConstraint{P,T}(n::Int, x1::AbstractVector, y1::AbstractVector,
		x2::AbstractVector, y2::AbstractVector, xv::AbstractVector, yv::AbstractVector,
		x=1, y=2) where {P,T}
    	@assert length(x1) == length(y1) == length(x2) == length(y2) == length(xv) == length(yv) == P "Lengths of x1, y1, x2, y2, xv, yv must be equal. Got lengths ($(length(x1)), $(length(y1)), $(length(x2)), $(length(y2)), $(length(xv)), $(length(yv)))"
        new{P,T}(n, x1, y1, x2, y2, xv, yv, x, y)
    end
end
function WallConstraint(n::Int, x1::AbstractVector, y1::AbstractVector,
	x2::AbstractVector, y2::AbstractVector, xv::AbstractVector, yv::AbstractVector,
		x=1, y=2)
    T = promote_type(eltype(x1), eltype(y1), eltype(x2), eltype(y2), eltype(xv), eltype(yv))
    P = length(x1)
    WallConstraint{P,T}(n, x1, y1, x2, y2, xv, yv, x, y)
end
TrajectoryOptimization.state_dim(con::WallConstraint) = con.n

function TrajectoryOptimization.evaluate(con::WallConstraint, X::StaticVector)
	x1 = con.x1
	y1 = con.y1
	x2 = con.x2
	y2 = con.y2
	xv = con.xv
	yv = con.yv
	x = X[con.x]
	y = X[con.y]

	left  = (x .- x1) .* (x2 .- x1) + (y .- y1) .* (y2 .- y1) .> 0
	right = (x .- x2) .* (x1 .- x2) + (y .- y2) .* (y1 .- y2) .> 0
	out = (x .- x1) .* xv + (y .- y1) .* yv
	return out .* left .* right
end

function TrajectoryOptimization.jacobian!(∇c, con::WallConstraint{P}, X::SVector) where P
	x1 = con.x1
	y1 = con.y1
	x2 = con.x2
	y2 = con.y2
	xv = con.xv
	yv = con.yv
	x = X[con.x]
	y = X[con.y]

	left  = (x .- x1) .* (x2 .- x1) + (y .- y1) .* (y2 .- y1) .> 0
	right = (x .- x2) .* (x1 .- x2) + (y .- y2) .* (y1 .- y2) .> 0
	for i = 1:P
		∇c[i,con.x] = left[i] * right[i] * xv[i]
		∇c[i,con.y] = left[i] * right[i] * yv[i]
	end
	return false
end

@inline Base.length(::WallConstraint{P}) where P = P
@inline TrajectoryOptimization.sense(::WallConstraint) = Inequality()

function TrajectoryOptimization.change_dimension(con::WallConstraint, n::Int, m::Int, ix=1:n, iu=1:m)
	WallConstraint(n, con.x1, con.y1, con.x2, con.y2, con.xv, con.yv, ix[con.x], ix[con.y])
end

############################################################################################
#                              WALL3D CONSTRAINT          								   #
############################################################################################
"""
	Wall3DConstraint{P,T}

Constraint of the form

	   NO constraint violation
-----------------------------------------
  *  -------- p1 * ///
                 |////
  NO constraint  |////        constraint
  violation      |------> v   violation
                 |////
                 |////
p3 * -------- p2 * ///

                 -
	         -|  NO cons. viol.
      p3 *c.v.|
     -    |   |               -
 - |  cons.|  |           -
   |  viol. | |       -
   |         ||   -
p1 * -------- * p2
   |  c.v. -
   |  -
  -|     NO cons. viol.
-----------------------------------------
	   NO constraint violation
The points p1, p2, p3 form a parallelepiped in 3D space.

`` (x - p1)^T v \\leq 0 ``
where ``x = [x[x], x[y], x[z]]``, ``p1 = [x1,y1,z1]``, ``p2 = [x2,y2,z2]``, ``p3 = [x3,y3,z3]``, ``v = [xv,yv,zv]``.

# Constructor:
```julia
Wall3DConstraint(n, x1::SVector{P}, y1::SVector{P}, z1::SVector{P}, x2::SVector{P}, y2::SVector{P}, z2::SVector{P},  x3::SVector{P}, y3::SVector{P}, z3::SVector{P}, xv::SVector{P}, yv::SVector{P}, x=1, y=2, z=3)
```
"""
struct Wall3DConstraint{P,T} <: TrajectoryOptimization.StateConstraint
	n::Int
	x1::SVector{P,T}
	y1::SVector{P,T}
	z1::SVector{P,T}
	x2::SVector{P,T}
	y2::SVector{P,T}
	z2::SVector{P,T}
	x3::SVector{P,T}
	y3::SVector{P,T}
	z3::SVector{P,T}
	xv::SVector{P,T}
	yv::SVector{P,T}
	zv::SVector{P,T}
	x::Int  # index of x-state
	y::Int  # index of y-state
	z::Int  # index of z-state
	function Wall3DConstraint{P,T}(n::Int,
		x1::AbstractVector, y1::AbstractVector, z1::AbstractVector,
		x2::AbstractVector, y2::AbstractVector, z2::AbstractVector,
		x3::AbstractVector, y3::AbstractVector, z3::AbstractVector,
		xv::AbstractVector, yv::AbstractVector, zv::AbstractVector,
		x=1, y=2, z=3) where {P,T}
    	@assert length(x1) == length(y1) == length(z1) == length(x2) == length(y2) == length(z2) == length(x3) == length(y3) == length(z3) == length(xv) == length(yv) == length(zv) == P "Lengths of x1, y1, x2, y2, xv, yv must be equal. Got lengths ($(length(x1)), $(length(y1)), $(length(x2)), $(length(y2)), $(length(xv)), $(length(yv)))"
        new{P,T}(n, x1, y1, z1, x2, y2, z2, x3, y3, z3, xv, yv, zv, x, y, z)
    end
end
function Wall3DConstraint(n::Int,
		x1::AbstractVector, y1::AbstractVector, z1::AbstractVector,
		x2::AbstractVector, y2::AbstractVector, z2::AbstractVector,
		x3::AbstractVector, y3::AbstractVector, z3::AbstractVector,
		xv::AbstractVector, yv::AbstractVector, zv::AbstractVector,
		x=1, y=2, z=3)
    T = promote_type(
		eltype(x1), eltype(y1), eltype(z1),
		eltype(x2), eltype(y2), eltype(z2),
		eltype(x3), eltype(y3), eltype(z3),
		eltype(xv), eltype(yv), eltype(zv))
    P = length(x1)
    Wall3DConstraint{P,T}(n, x1, y1, z1, x2, y2, z2, x3, y3, z3, xv, yv, zv, x, y, z)
end
TrajectoryOptimization.state_dim(con::Wall3DConstraint) = con.n

function TrajectoryOptimization.evaluate(con::Wall3DConstraint, X::StaticVector)
	x1 = con.x1
	y1 = con.y1
	z1 = con.z1
	x2 = con.x2
	y2 = con.y2
	z2 = con.z2
	x3 = con.x3
	y3 = con.y3
	z3 = con.z3
	xv = con.xv
	yv = con.yv
	zv = con.zv
	x = X[con.x]
	y = X[con.y]
	z = X[con.z]

	left    = (x .- x1) .* (x2 .- x1) + (y .- y1) .* (y2 .- y1) + (z .- z1) .* (z2 .- z1) .> 0
	right   = (x .- x2) .* (x1 .- x2) + (y .- y2) .* (y1 .- y2) + (z .- z2) .* (z1 .- z2) .> 0
	bottom  = (x .- x3) .* (x2 .- x3) + (y .- y3) .* (y2 .- y3) + (z .- z3) .* (z2 .- z3) .> 0
	top     = (x .- x2) .* (x3 .- x2) + (y .- y2) .* (y3 .- y2) + (z .- z2) .* (z3 .- z2) .> 0
	out = (x .- x1) .* xv + (y .- y1) .* yv + (z .- z1) .* zv
	return out .* left .* right .* bottom .* top
end

function TrajectoryOptimization.jacobian!(∇c, con::Wall3DConstraint{P}, X::SVector) where P
	x1 = con.x1
	y1 = con.y1
	z1 = con.z1
	x2 = con.x2
	y2 = con.y2
	z2 = con.z2
	x3 = con.x3
	y3 = con.y3
	z3 = con.z3
	xv = con.xv
	yv = con.yv
	zv = con.zv
	x = X[con.x]
	y = X[con.y]
	z = X[con.z]

	left    = (x .- x1) .* (x2 .- x1) + (y .- y1) .* (y2 .- y1) + (z .- z1) .* (z2 .- z1) .> 0
	right   = (x .- x2) .* (x1 .- x2) + (y .- y2) .* (y1 .- y2) + (z .- z2) .* (z1 .- z2) .> 0
	bottom  = (x .- x3) .* (x2 .- x3) + (y .- y3) .* (y2 .- y3) + (z .- z3) .* (z2 .- z3) .> 0
	top     = (x .- x2) .* (x3 .- x2) + (y .- y2) .* (y3 .- y2) + (z .- z2) .* (z3 .- z2) .> 0
	for i = 1:P
		∇c[i,con.x] = left[i] * right[i] * bottom[i] * top[i] * xv[i]
		∇c[i,con.y] = left[i] * right[i] * bottom[i] * top[i] * yv[i]
		∇c[i,con.z] = left[i] * right[i] * bottom[i] * top[i] * zv[i]
	end
	return false
end

@inline Base.length(::Wall3DConstraint{P}) where P = P
@inline TrajectoryOptimization.sense(::Wall3DConstraint) = Inequality()

function TrajectoryOptimization.change_dimension(con::Wall3DConstraint, n::Int, m::Int, ix=1:n, iu=1:m)
	Wall3DConstraint(n,
		con.x1, con.y1, con.z1,
		con.x2, con.y2, con.z2,
		con.x3, con.y3, con.z3,
		con.x4, con.y4, con.z4,
		con.xv, con.yv, con.zv,
		ix[con.x], ix[con.y], ix[con.z])
end


export securitydice, normaldice, riskydice

"""
    securitydice(list)

Computes the transition probability matrix for the security dice
with squares types defined in `list`.
"""
function securitydice(list::Vector{Int64})::Array{Float64,2}
    proba = zeros(15, 15)
    for i = 1:14
        proba[i,i] = 0.5
        if i == 3
            proba[i,i+1] = 0.25
            proba[i,i+8] = 0.25
        elseif i == 10
            proba[i,i+5] = 0.5
        else
            proba[i,i+1] = 0.5
        end
    end
    proba[15,15] = 1
    return proba
end

"""
    normaldice(list, circular)

Computes the transition probability matrix for the normal dice
with squares types defined in `list` and circularity of
board defined by `circular`.
"""
function normaldice(list::Vector{Int64}, circular::Bool)::Array{Float64,2}
    proba = zeros(15, 15)
    # Compute first the transition proba matrix as if there were no traps
    p = 1/3
    for i = 1:14
        proba[i,i] = p
        if i == 3
            proba[i,i+1] = p/2
            proba[i,i+2] = p/2
            proba[i,i+8] = p/2
            proba[i,i+9] = p/2
        elseif i == 9
            proba[i,i+1] = p
            proba[i,i+6] = p
        elseif i == 10
            if circular
                proba[i,i+5] = p
                proba[i,(i+6) % 15] = p
            else
                proba[i,i+5] = 2*p
            end
        elseif i == 14
            if circular
                proba[i,i+1] = p
                proba[i,(i+2) % 15] = p
            else
                proba[i,i+1] = 2*p
            end
        else
            proba[i,i+1] = p
            proba[i,i+2] = p
        end
    end
    proba[15,15] = 1

    copy_proba = copy(proba)
    # Remodify each line to take traps into account
    for i = 1:14
        for j = 1:15
            if list[j] == 1 # Trap of type 1 : go back to square 1
                if j != 1
                    proba[i,1] = proba[i,1] + proba[i,j]/2
                    proba[i,j] = proba[i,j]/2
                end
            elseif list[j] == 2 # Trap of type 2 : go back three squares
                proba[i,squaresub(j, 3)] = proba[i,squaresub(j, 3)] + proba[i,j]/2
                proba[i,j] = proba[i,j]/2
            end
        end
    end
    return proba
end

"""
    riskydice(list, circular)

Computes the transition probability matrix for the risky dice
with squares types defined in `list` and circularity of
board defined by `circular`.
"""
function riskydice(list::Vector{Int64}, circular::Bool)::Array{Float64,2}
    proba = zeros(15, 15)
    # Compute first the transition proba matrix as if there were no traps
    for i = 1:14
        proba[i,i] = 0.25
        if i == 3
            proba[i,i+1] = 0.125
            proba[i,i+2] = 0.125
            proba[i,i+3] = 0.125
            proba[i,i+8] = 0.125
            proba[i,i+9] = 0.125
            proba[i,i+10] = 0.125
        elseif i == 8
            proba[i,i+1] = 0.25
            proba[i,i+2] = 0.25
            proba[i,i+7] = 0.25
        elseif i == 9
            proba[i,i+1] = 0.25
            if circular
                proba[i,i+6] = 0.25
                proba[i,i-8] = 0.25
            else
                proba[i,i+6] = 0.5
            end
        elseif i == 10
            if circular
                proba[i,i+5] = 0.25
                proba[i,i-9] = 0.25
                proba[i,i-8] = 0.25
            else
                proba[i,i+5] = 0.75
            end
        elseif i == 13
            proba[i,i+1] = 0.25
            if circular
                proba[i,i+2] = 0.25
                proba[i,i-12] = 0.25
            else
                proba[i,i+2] = 0.5
            end
        elseif i == 14
            if circular
                proba[i,i+1] = 0.25
                proba[i,i-13] = 0.25
                proba[i,i-12] = 0.25
            else
                proba[i,i+1] = 0.75
            end
        else
            proba[i,i+1] = 0.25
            proba[i,i+2] = 0.25
            proba[i,i+3] = 0.25
        end
    end
    proba[15,15] = 1

    copy_proba = copy(proba)
    # Remodify each line to take traps into account
    for i = 1:14
        for j = 1:15
            if list[j] == 1 # Trap of type 1 : go back to square 1
                if j != 1
                    proba[i,1] = proba[i,1] + proba[i,j]
                    proba[i,j] = 0
                end
            elseif list[j] == 2 # Trap of type 2 : go back three squares
                proba[i, squaresub(j, 3)] = proba[i, squaresub(j, 3)] + proba[i,j]
                proba[i,j] = 0
            end
        end
    end
    return proba
end


export Graph, erdos_renyi, planted_clique, get_dcs_test_instance

typealias TGraph{T} SparseMatrixCSC{T,Int64}
typealias Graph TGraph{Float64}
typealias GraphVec Array{Graph, 1}

function erdos_renyi(n::Integer, p::Real; is_directed::Bool=false)
  A = spones(sprand(n, n, p))
  if is_directed==false
    A = triu(A, 1)
    A = A + A'
  else
    for i=1:n
      A[i, i]=0.0
    end
  end
  return A
end

function erdos_renyi_fast(n::Integer, p::Real; is_directed::Bool=false)
  A = spzeros(Bool, n, n)
  if is_directed
    for i=1:n
      @simd for j=(i+1):n
        @inbounds A[i, j] = rand() < p
        @inbounds A[j, i] = rand() < p
      end
    end
  else
    for i=1:n
      B = sprandbool(1, n - i, p)
      # bi = find(B)
      @simd for j= 1 : (n-i)
        @inbounds A[i, j+i] = B[j]
        @inbounds A[j+i, i] = B[j]
      end

    end
  end
  return A
end

function planted_clique(n::Integer, p::Real, k::Integer; is_directed=false)
  A = erdos_renyi(n, p; is_directed = is_directed)
  S = randperm(n)[1:k;]
  for i=1:k
    for j=1:k
      if i != j
        A[S[i], S[j]] = 1
      end
    end
  end
  return A
end

function get_dcs_test_instance(n::Integer = 15, p::Real = 0.1, k::Integer = 0)
  if k==0 && VERSION.minor == 3
    k = max(5, iceil(sqrt(n)))
  elseif k==0
    k = max(5, ceil(Integer, sqrt(n)))
  end

  G = Graph[]
  push!(G,erdos_renyi(n, p))
  push!(G,planted_clique(n, p, k))

  return G
end


using Manopt, Manifolds, Makie, Colors, LinearAlgebra, ColorSchemes
#
# Settings
n_lon = 201
n_lat = 101
lat = range(-π / 2, π / 2; length=n_lat)
lon = range(-π, π; length=n_lon)
#
# Colors
sphere_color = colorant"rgb(223 186 105)"
plane_color = colorant"rgb(255 254 223)"
edge_color = colorant"rgb(85 85 85)"
dot_color = edge_color
vec_color = dot_color
geod_color = dot_color

A = [8.0 1.0 6.0; 3.0 5.0 7.0; 4.0 9.0 2.0]
A = A + A'
A = A ./ opnorm(A)
F(p) = p' * A * p
spherical_to_euclidean(θ, φ, r=1) = [r * sin(θ) * cos(φ), r * sin(θ) * sin(φ), r * cos(θ)]
M = Manifolds.Sphere(2)

mesh_x = [spherical_to_euclidean(θ, φ)[1] for θ in lat, φ in lon]
mesh_y = [spherical_to_euclidean(θ, φ)[2] for θ in lat, φ in lon]
mesh_z = [spherical_to_euclidean(θ, φ)[3] for θ in lat, φ in lon]

mesh_pts = [spherical_to_euclidean(θ, φ) for θ in lat, φ in lon]
data = [F(spherical_to_euclidean(θ, φ)) for θ in lat, φ in lon]

manopt_pre_scheme = ColorScheme(
    [sphere_color, plane_color], "Manopt", "The Sphere color Scheme for the Manopt Logo"
)
manopt_scheme = ColorScheme(
    [get(manopt_pre_scheme, i) for i in 0.0:0.01:1.0],
    "Manopt",
    "The Sphere color Scheme for the Manopt Logo",
)

# mesh(Makie.Sphere(Point3f0(0), 1f0), color=data, colormap = manopt_pre_scheme, levels=5, interpolate=false)
contour3d(lat, lon, data; levels=11, color=:black, linewidth=3)


using PtFEM

n = 5           # No of nodes
els = n - 1     # No of elements

data = Dict(
  # Frame(nels, nn, ndim, nst, nip, finite_element(nod, nodof))
  :struc_el => Frame(els, n, 3, 1, 1, Line(2, 3)),
  :properties => [
    4.0e6 1.0e6 0.3e6 0.3e6],
  :x_coords => Float64[i for i in 0:n-1],
  :y_coords => zeros(n),
  :z_coords => zeros(n),
  :gamma => zeros(n),     # No rotation of element about local axis (dip)
  :g_num => hcat(1:n-1,2:n)',
  :support => [
    (1, [0 0 0 0 0 0])
  ],
  :loaded_nodes => [
    (n, [0.0 0.0 100.0 0.0 0.0 0.0])
  ]
)

data |> display
println()

@time fem, dis_df, fm_df = p44(data)
println()

dis_df |> display
println()

println("Actions:")
fm_df |> display
println()


using LeastSquaresOptim, Statistics, Test

# StRD tests (in progress)
# http://www.itl.nist.gov/div898/strd/nls/nls_main.shtml

function misra1a()
    name = "misra1a"
    data = [
    10.07E0      77.6E0;
    14.73E0     114.9E0;
    17.94E0     141.1E0;
    23.93E0     190.8E0;
    29.61E0     239.9E0;
    35.18E0     289.0E0;
    40.02E0     332.8E0;
    44.82E0     378.4E0;
    50.76E0     434.8E0;
    55.05E0     477.3E0;
    61.01E0     536.8E0;
    66.40E0     593.1E0;
    75.47E0     689.1E0;
    81.78E0     760.0E0;
    ]
    parameters =[
    5.0000000000E+02        2.5000000000E+02        2.3894212918E+02;
    1.0000000000E-04        5.0000000000E-04        5.5015643181E-04;
    ]
    f(x, beta) = beta[1]*(1-exp(-beta[2]*x))
    solution = [2.3894212918E+02, 5.5015643181E-04]

    name, data, parameters, f, solution
end

function Chwirut2()
    name = "Chwirut2"
    data = [
    92.9000E0     0.500E0;
    57.1000E0     1.000E0;
    31.0500E0     1.750E0;
    11.5875E0     3.750E0;
    8.0250E0     5.750E0;
    63.6000E0     0.875E0;
    21.4000E0     2.250E0;
    14.2500E0     3.250E0;
    8.4750E0     5.250E0;
    63.8000E0     0.750E0;
    26.8000E0     1.750E0;
    16.4625E0     2.750E0;
    7.1250E0     4.750E0;
    67.3000E0     0.625E0;
    41.0000E0     1.250E0;
    21.1500E0     2.250E0;
    8.1750E0     4.250E0;
    81.5000E0      .500E0;
    13.1200E0     3.000E0;
    59.9000E0      .750E0;
    14.6200E0     3.000E0;
    32.9000E0     1.500E0;
    5.4400E0     6.000E0;
    12.5600E0     3.000E0;
    5.4400E0     6.000E0;
    32.0000E0     1.500E0;
    13.9500E0     3.000E0;
    75.8000E0      .500E0;
    20.0000E0     2.000E0;
    10.4200E0     4.000E0;
    59.5000E0      .750E0;
    21.6700E0     2.000E0;
    8.5500E0     5.000E0;
    62.0000E0      .750E0;
    20.2000E0     2.250E0;
    7.7600E0     3.750E0;
    3.7500E0     5.750E0;
    11.8100E0     3.000E0;
    54.7000E0      .750E0;
    23.7000E0     2.500E0;
    11.5500E0     4.000E0;
    61.3000E0      .750E0;
    17.7000E0     2.500E0;
    8.7400E0     4.000E0;
    59.2000E0      .750E0;
    16.3000E0     2.500E0;
    8.6200E0     4.000E0;
    81.0000E0      .500E0;
    4.8700E0     6.000E0;
    14.6200E0     3.000E0;
    81.7000E0      .500E0;
    17.1700E0     2.750E0;
    81.3000E0      .500E0;
    28.9000E0     1.750E0;
    ]

    parameters = [
    0.1 0.15;
    0.01        0.008;
    0.02        0.010;
    ]
    f(x, beta) = exp(-beta[1]*x)/(beta[2]+beta[3]*x)
    solution =[ 1.6657666537E-01, 5.1653291286E-03, 1.2150007096E-02]

    name, data, parameters, f, solution
end

function Chwitrut1()
    name = "Chwirut1"
    data =[
    92.9000E0     0.5000E0;
    78.7000E0     0.6250E0;
    64.2000E0     0.7500E0;
    64.9000E0     0.8750E0;
    57.1000E0     1.0000E0;
    43.3000E0     1.2500E0;
    31.1000E0     1.7500E0;
    23.6000E0     2.2500E0;
    31.0500E0     1.7500E0;
    23.7750E0     2.2500E0;
    17.7375E0     2.7500E0;
    13.8000E0     3.2500E0;
    11.5875E0     3.7500E0;
    9.4125E0     4.2500E0;
    7.7250E0     4.7500E0;
    7.3500E0     5.2500E0;
    8.0250E0     5.7500E0;
    90.6000E0     0.5000E0;
    76.9000E0     0.6250E0;
    71.6000E0     0.7500E0;
    63.6000E0     0.8750E0;
    54.0000E0     1.0000E0;
    39.2000E0     1.2500E0;
    29.3000E0     1.7500E0;
    21.4000E0     2.2500E0;
    29.1750E0     1.7500E0;
    22.1250E0     2.2500E0;
    17.5125E0     2.7500E0;
    14.2500E0     3.2500E0;
    9.4500E0     3.7500E0;
    9.1500E0     4.2500E0;
    7.9125E0     4.7500E0;
    8.4750E0     5.2500E0;
    6.1125E0     5.7500E0;
    80.0000E0     0.5000E0;
    79.0000E0     0.6250E0;
    63.8000E0     0.7500E0;
    57.2000E0     0.8750E0;
    53.2000E0     1.0000E0;
    42.5000E0     1.2500E0;
    26.8000E0     1.7500E0;
    20.4000E0     2.2500E0;
    26.8500E0     1.7500E0;
    21.0000E0     2.2500E0;
    16.4625E0     2.7500E0;
    12.5250E0     3.2500E0;
    10.5375E0     3.7500E0;
    8.5875E0     4.2500E0;
    7.1250E0     4.7500E0;
    6.1125E0     5.2500E0;
    5.9625E0     5.7500E0;
    74.1000E0     0.5000E0;
    67.3000E0     0.6250E0;
    60.8000E0     0.7500E0;
    55.5000E0     0.8750E0;
    50.3000E0     1.0000E0;
    41.0000E0     1.2500E0;
    29.4000E0     1.7500E0;
    20.4000E0     2.2500E0;
    29.3625E0     1.7500E0;
    21.1500E0     2.2500E0;
    16.7625E0     2.7500E0;
    13.2000E0     3.2500E0;
    10.8750E0     3.7500E0;
    8.1750E0     4.2500E0;
    7.3500E0     4.7500E0;
    5.9625E0     5.2500E0;
    5.6250E0     5.7500E0;
    81.5000E0      .5000E0;
    62.4000E0      .7500E0;
    32.5000E0     1.5000E0;
    12.4100E0     3.0000E0;
    13.1200E0     3.0000E0;
    15.5600E0     3.0000E0;
    5.6300E0     6.0000E0;
    78.0000E0      .5000E0;
    59.9000E0      .7500E0;
    33.2000E0     1.5000E0;
    13.8400E0     3.0000E0;
    12.7500E0     3.0000E0;
    14.6200E0     3.0000E0;
    3.9400E0     6.0000E0;
    76.8000E0      .5000E0;
    61.0000E0      .7500E0;
    32.9000E0     1.5000E0;
    13.8700E0     3.0000E0;
    11.8100E0     3.0000E0;
    13.3100E0     3.0000E0;
    5.4400E0     6.0000E0;
    78.0000E0      .5000E0;
    63.5000E0      .7500E0;
    33.8000E0     1.5000E0;
    12.5600E0     3.0000E0;
    5.6300E0     6.0000E0;
    12.7500E0     3.0000E0;
    13.1200E0     3.0000E0;
    5.4400E0     6.0000E0;
    76.8000E0      .5000E0;
    60.0000E0      .7500E0;
    47.8000E0     1.0000E0;
    32.0000E0     1.5000E0;
    22.2000E0     2.0000E0;
    22.5700E0     2.0000E0;
    18.8200E0     2.5000E0;
    13.9500E0     3.0000E0;
    11.2500E0     4.0000E0;
    9.0000E0     5.0000E0;
    6.6700E0     6.0000E0;
    75.8000E0      .5000E0;
    62.0000E0      .7500E0;
    48.8000E0     1.0000E0;
    35.2000E0     1.5000E0;
    20.0000E0     2.0000E0;
    20.3200E0     2.0000E0;
    19.3100E0     2.5000E0;
    12.7500E0     3.0000E0;
    10.4200E0     4.0000E0;
    7.3100E0     5.0000E0;
    7.4200E0     6.0000E0;
    70.5000E0      .5000E0;
    59.5000E0      .7500E0;
    48.5000E0     1.0000E0;
    35.8000E0     1.5000E0;
    21.0000E0     2.0000E0;
    21.6700E0     2.0000E0;
    21.0000E0     2.5000E0;
    15.6400E0     3.0000E0;
    8.1700E0     4.0000E0;
    8.5500E0     5.0000E0;
    10.1200E0     6.0000E0;
    78.0000E0      .5000E0;
    66.0000E0      .6250E0;
    62.0000E0      .7500E0;
    58.0000E0      .8750E0;
    47.7000E0     1.0000E0;
    37.8000E0     1.2500E0;
    20.2000E0     2.2500E0;
    21.0700E0     2.2500E0;
    13.8700E0     2.7500E0;
    9.6700E0     3.2500E0;
    7.7600E0     3.7500E0;
    5.4400E0     4.2500E0;
    4.8700E0     4.7500E0;
    4.0100E0     5.2500E0;
    3.7500E0     5.7500E0;
    24.1900E0     3.0000E0;
    25.7600E0     3.0000E0;
    18.0700E0     3.0000E0;
    11.8100E0     3.0000E0;
    12.0700E0     3.0000E0;
    16.1200E0     3.0000E0;
    70.8000E0      .5000E0;
    54.7000E0      .7500E0;
    48.0000E0     1.0000E0;
    39.8000E0     1.5000E0;
    29.8000E0     2.0000E0;
    23.7000E0     2.5000E0;
    29.6200E0     2.0000E0;
    23.8100E0     2.5000E0;
    17.7000E0     3.0000E0;
    11.5500E0     4.0000E0;
    12.0700E0     5.0000E0;
    8.7400E0     6.0000E0;
    80.7000E0      .5000E0;
    61.3000E0      .7500E0;
    47.5000E0     1.0000E0;
    29.0000E0     1.5000E0;
    24.0000E0     2.0000E0;
    17.7000E0     2.5000E0;
    24.5600E0     2.0000E0;
    18.6700E0     2.5000E0;
    16.2400E0     3.0000E0;
    8.7400E0     4.0000E0;
    7.8700E0     5.0000E0;
    8.5100E0     6.0000E0;
    66.7000E0      .5000E0;
    59.2000E0      .7500E0;
    40.8000E0     1.0000E0;
    30.7000E0     1.5000E0;
    25.7000E0     2.0000E0;
    16.3000E0     2.5000E0;
    25.9900E0     2.0000E0;
    16.9500E0     2.5000E0;
    13.3500E0     3.0000E0;
    8.6200E0     4.0000E0;
    7.2000E0     5.0000E0;
    6.6400E0     6.0000E0;
    13.6900E0     3.0000E0;
    81.0000E0      .5000E0;
    64.5000E0      .7500E0;
    35.5000E0     1.5000E0;
    13.3100E0     3.0000E0;
    4.8700E0     6.0000E0;
    12.9400E0     3.0000E0;
    5.0600E0     6.0000E0;
    15.1900E0     3.0000E0;
    14.6200E0     3.0000E0;
    15.6400E0     3.0000E0;
    25.5000E0     1.7500E0;
    25.9500E0     1.7500E0;
    81.7000E0      .5000E0;
    61.6000E0      .7500E0;
    29.8000E0     1.7500E0;
    29.8100E0     1.7500E0;
    17.1700E0     2.7500E0;
    10.3900E0     3.7500E0;
    28.4000E0     1.7500E0;
    28.6900E0     1.7500E0;
    81.3000E0      .5000E0;
    60.9000E0      .7500E0;
    16.6500E0     2.7500E0;
    10.0500E0     3.7500E0;
    28.9000E0     1.7500E0;
    28.9500E0     1.7500E0]

    parameters = [
    0.1         0.15;
    0.01        0.008;
    0.02        0.010;
    ]
    f(x, beta) = exp(-beta[1]*x)/(beta[2]+beta[3]*x)
    solution = [1.9027818370E-01, 6.1314004477E-03, 1.0530908399E-02]

    name, data, parameters, f, solution
end

function Lanczos3()
    name = "Lanczos3"
    data = [
    2.5134E+00  0.00000E+00;
    2.0443E+00  5.00000E-02;
    1.6684E+00  1.00000E-01;
    1.3664E+00  1.50000E-01;
    1.1232E+00  2.00000E-01;
    0.9269E+00  2.50000E-01;
    0.7679E+00  3.00000E-01;
    0.6389E+00  3.50000E-01;
    0.5338E+00  4.00000E-01;
    0.4479E+00  4.50000E-01;
    0.3776E+00  5.00000E-01;
    0.3197E+00  5.50000E-01;
    0.2720E+00  6.00000E-01;
    0.2325E+00  6.50000E-01;
    0.1997E+00  7.00000E-01;
    0.1723E+00  7.50000E-01;
    0.1493E+00  8.00000E-01;
    0.1301E+00  8.50000E-01;
    0.1138E+00  9.00000E-01;
    0.1000E+00  9.50000E-01;
    0.0883E+00  1.00000E+00;
    0.0783E+00  1.05000E+00;
    0.0698E+00  1.10000E+00;
    0.0624E+00  1.15000E+00]

    parameters = [
    1.2         0.5;
    0.3         0.7;
    5.6         3.6;
    5.5         4.2;
    6.5         4  ;
    7.6         6.3;
    ]
    f(x, beta) = beta[1] * exp(-beta[2] * x) + beta[3] * exp(-beta[4]*x) + beta[5] * exp(-beta[6]*x)
    solution = [
 8.6816414977E-02,
 9.5498101505E-01,
 8.4400777463E-01,
 2.9515951832E+00,
 1.5825685901E+00,
 4.9863565084E+00
 ]

    name, data, parameters, f, solution
end

function Gauss1()
    name = "Gauss1"
    data = [
    97.62227    1.000000;
    97.80724    2.000000;
    96.62247    3.000000;
    92.59022    4.000000;
    91.23869    5.000000;
    95.32704    6.000000;
    90.35040    7.000000;
    89.46235    8.000000;
    91.72520    9.000000;
    89.86916   10.000000;
    86.88076    11.00000;
    85.94360    12.00000;
    87.60686    13.00000;
    86.25839    14.00000;
    80.74976    15.00000;
    83.03551    16.00000;
    88.25837    17.00000;
    82.01316    18.00000;
    82.74098    19.00000;
    83.30034    20.00000;
    81.27850    21.00000;
    81.85506    22.00000;
    80.75195    23.00000;
    80.09573    24.00000;
    81.07633    25.00000;
    78.81542    26.00000;
    78.38596    27.00000;
    79.93386    28.00000;
    79.48474    29.00000;
    79.95942    30.00000;
    76.10691    31.00000;
    78.39830    32.00000;
    81.43060    33.00000;
    82.48867    34.00000;
    81.65462    35.00000;
    80.84323    36.00000;
    88.68663    37.00000;
    84.74438    38.00000;
    86.83934    39.00000;
    85.97739    40.00000;
    91.28509    41.00000;
    97.22411    42.00000;
    93.51733    43.00000;
    94.10159    44.00000;
    101.91760    45.00000;
    98.43134    46.00000;
    110.4214    47.00000;
    107.6628    48.00000;
    111.7288    49.00000;
    116.5115    50.00000;
    120.7609    51.00000;
    123.9553    52.00000;
    124.2437    53.00000;
    130.7996    54.00000;
    133.2960    55.00000;
    130.7788    56.00000;
    132.0565    57.00000;
    138.6584    58.00000;
    142.9252    59.00000;
    142.7215    60.00000;
    144.1249    61.00000;
    147.4377    62.00000;
    148.2647    63.00000;
    152.0519    64.00000;
    147.3863    65.00000;
    149.2074    66.00000;
    148.9537    67.00000;
    144.5876    68.00000;
    148.1226    69.00000;
    148.0144    70.00000;
    143.8893    71.00000;
    140.9088    72.00000;
    143.4434    73.00000;
    139.3938    74.00000;
    135.9878    75.00000;
    136.3927    76.00000;
    126.7262    77.00000;
    124.4487    78.00000;
    122.8647    79.00000;
    113.8557    80.00000;
    113.7037    81.00000;
    106.8407    82.00000;
    107.0034    83.00000;
    102.46290    84.00000;
    96.09296    85.00000;
    94.57555    86.00000;
    86.98824    87.00000;
    84.90154    88.00000;
    81.18023    89.00000;
    76.40117    90.00000;
    67.09200    91.00000;
    72.67155    92.00000;
    68.10848    93.00000;
    67.99088    94.00000;
    63.34094    95.00000;
    60.55253    96.00000;
    56.18687    97.00000;
    53.64482    98.00000;
    53.70307    99.00000;
    48.07893   100.00000;
    42.21258   101.00000;
    45.65181   102.00000;
    41.69728   103.00000;
    41.24946   104.00000;
    39.21349   105.00000;
    37.71696    106.0000;
    36.68395    107.0000;
    37.30393    108.0000;
    37.43277    109.0000;
    37.45012    110.0000;
    32.64648    111.0000;
    31.84347    112.0000;
    31.39951    113.0000;
    26.68912    114.0000;
    32.25323    115.0000;
    27.61008    116.0000;
    33.58649    117.0000;
    28.10714    118.0000;
    30.26428    119.0000;
    28.01648    120.0000;
    29.11021    121.0000;
    23.02099    122.0000;
    25.65091    123.0000;
    28.50295    124.0000;
    25.23701    125.0000;
    26.13828    126.0000;
    33.53260    127.0000;
    29.25195    128.0000;
    27.09847    129.0000;
    26.52999    130.0000;
    25.52401    131.0000;
    26.69218    132.0000;
    24.55269    133.0000;
    27.71763    134.0000;
    25.20297    135.0000;
    25.61483    136.0000;
    25.06893    137.0000;
    27.63930    138.0000;
    24.94851    139.0000;
    25.86806    140.0000;
    22.48183    141.0000;
    26.90045    142.0000;
    25.39919    143.0000;
    17.90614    144.0000;
    23.76039    145.0000;
    25.89689    146.0000;
    27.64231    147.0000;
    22.86101    148.0000;
    26.47003    149.0000;
    23.72888    150.0000;
    27.54334    151.0000;
    30.52683    152.0000;
    28.07261    153.0000;
    34.92815    154.0000;
    28.29194    155.0000;
    34.19161    156.0000;
    35.41207    157.0000;
    37.09336    158.0000;
    40.98330    159.0000;
    39.53923    160.0000;
    47.80123    161.0000;
    47.46305    162.0000;
    51.04166    163.0000;
    54.58065    164.0000;
    57.53001    165.0000;
    61.42089    166.0000;
    62.79032    167.0000;
    68.51455    168.0000;
    70.23053    169.0000;
    74.42776    170.0000;
    76.59911    171.0000;
    81.62053    172.0000;
    83.42208    173.0000;
    79.17451    174.0000;
    88.56985    175.0000;
    85.66525    176.0000;
    86.55502    177.0000;
    90.65907    178.0000;
    84.27290    179.0000;
    85.72220    180.0000;
    83.10702    181.0000;
    82.16884    182.0000;
    80.42568    183.0000;
    78.15692    184.0000;
    79.79691    185.0000;
    77.84378    186.0000;
    74.50327    187.0000;
    71.57289    188.0000;
    65.88031    189.0000;
    65.01385    190.0000;
    60.19582    191.0000;
    59.66726    192.0000;
    52.95478    193.0000;
    53.87792    194.0000;
    44.91274    195.0000;
    41.09909    196.0000;
    41.68018    197.0000;
    34.53379    198.0000;
    34.86419    199.0000;
    33.14787    200.0000;
    29.58864    201.0000;
    27.29462    202.0000;
    21.91439    203.0000;
    19.08159    204.0000;
    24.90290    205.0000;
    19.82341    206.0000;
    16.75551    207.0000;
    18.24558    208.0000;
    17.23549    209.0000;
    16.34934    210.0000;
    13.71285    211.0000;
    14.75676    212.0000;
    13.97169    213.0000;
    12.42867    214.0000;
    14.35519    215.0000;
    7.703309    216.0000;
    10.234410    217.0000;
    11.78315    218.0000;
    13.87768    219.0000;
    4.535700    220.0000;
    10.059280    221.0000;
    8.424824    222.0000;
    10.533120    223.0000;
    9.602255    224.0000;
    7.877514    225.0000;
    6.258121    226.0000;
    8.899865    227.0000;
    7.877754    228.0000;
    12.51191    229.0000;
    10.66205    230.0000;
    6.035400    231.0000;
    6.790655    232.0000;
    8.783535    233.0000;
    4.600288    234.0000;
    8.400915    235.0000;
    7.216561    236.0000;
    10.017410    237.0000;
    7.331278    238.0000;
    6.527863    239.0000;
    2.842001    240.0000;
    10.325070    241.0000;
    4.790995    242.0000;
    8.377101    243.0000;
    6.264445    244.0000;
    2.706213    245.0000;
    8.362329    246.0000;
    8.983658    247.0000;
    3.362571    248.0000;
    1.182746    249.0000;
    4.875359    250.0000;
    ]

    parameters = [
    97.0        94.0;
    0.009       0.0105;
    100.0        99.0;
    65.0        63.0;
    20.0        25.0;
    70.0        71.0 ;
    178.0       180.0;
    16.5        20.0;
    ]

    solution = [
    9.8778210871E+01,
    1.0497276517E-02,
    1.0048990633E+02,
    6.7481111276E+01,
    2.3129773360E+01,
    7.1994503004E+01,
    1.7899805021E+02,
    1.8389389025E+01
    ]
    f(x, beta) = beta[1] * exp(-beta[2]x) + beta[3]*exp(-(x - beta[4])^2 / beta[5]^2) + beta[6]*exp(-(x - beta[7])^2 / beta[8]^2)

    return name, data, parameters, f, solution
end

function Gauss2()
    name = "Gauss2"
    data = [
    97.58776    1.000000;
    97.76344    2.000000;
    96.56705    3.000000;
    92.52037    4.000000;
    91.15097    5.000000;
    95.21728    6.000000;
    90.21355    7.000000;
    89.29235    8.000000;
    91.51479    9.000000;
    89.60966   10.000000;
    86.56187    11.00000;
    85.55316    12.00000;
    87.13054    13.00000;
    85.67940    14.00000;
    80.04851    15.00000;
    82.18925    16.00000;
    87.24081    17.00000;
    80.79407    18.00000;
    81.28570    19.00000;
    81.56940    20.00000;
    79.22715    21.00000;
    79.43275    22.00000;
    77.90195    23.00000;
    76.75468    24.00000;
    77.17377    25.00000;
    74.27348    26.00000;
    73.11900    27.00000;
    73.84826    28.00000;
    72.47870    29.00000;
    71.92292    30.00000;
    66.92176    31.00000;
    67.93835    32.00000;
    69.56207    33.00000;
    69.07066    34.00000;
    66.53983    35.00000;
    63.87883    36.00000;
    69.71537    37.00000;
    63.60588    38.00000;
    63.37154    39.00000;
    60.01835    40.00000;
    62.67481    41.00000;
    65.80666    42.00000;
    59.14304    43.00000;
    56.62951    44.00000;
    61.21785    45.00000;
    54.38790    46.00000;
    62.93443    47.00000;
    56.65144    48.00000;
    57.13362    49.00000;
    58.29689    50.00000;
    58.91744    51.00000;
    58.50172    52.00000;
    55.22885    53.00000;
    58.30375    54.00000;
    57.43237    55.00000;
    51.69407    56.00000;
    49.93132    57.00000;
    53.70760    58.00000;
    55.39712    59.00000;
    52.89709    60.00000;
    52.31649    61.00000;
    53.98720    62.00000;
    53.54158    63.00000;
    56.45046    64.00000;
    51.32276    65.00000;
    53.11676    66.00000;
    53.28631    67.00000;
    49.80555    68.00000;
    54.69564    69.00000;
    56.41627    70.00000;
    54.59362    71.00000;
    54.38520    72.00000;
    60.15354    73.00000;
    59.78773    74.00000;
    60.49995    75.00000;
    65.43885    76.00000;
    60.70001    77.00000;
    63.71865    78.00000;
    67.77139    79.00000;
    64.70934    80.00000;
    70.78193    81.00000;
    70.38651    82.00000;
    77.22359    83.00000;
    79.52665    84.00000;
    80.13077    85.00000;
    85.67823    86.00000;
    85.20647    87.00000;
    90.24548    88.00000;
    93.61953    89.00000;
    95.86509    90.00000;
    93.46992    91.00000;
    105.8137    92.00000;
    107.8269    93.00000;
    114.0607    94.00000;
    115.5019    95.00000;
    118.5110    96.00000;
    119.6177    97.00000;
    122.1940    98.00000;
    126.9903    99.00000;
    125.7005   100.00000;
    123.7447   101.00000;
    130.6543   102.00000;
    129.7168   103.00000;
    131.8240   104.00000;
    131.8759   105.00000;
    131.9994    106.0000;
    132.1221    107.0000;
    133.4414    108.0000;
    133.8252    109.0000;
    133.6695    110.0000;
    128.2851    111.0000;
    126.5182    112.0000;
    124.7550    113.0000;
    118.4016    114.0000;
    122.0334    115.0000;
    115.2059    116.0000;
    118.7856    117.0000;
    110.7387    118.0000;
    110.2003    119.0000;
    105.17290    120.0000;
    103.44720    121.0000;
    94.54280    122.0000;
    94.40526    123.0000;
    94.57964    124.0000;
    88.76605    125.0000;
    87.28747    126.0000;
    92.50443    127.0000;
    86.27997    128.0000;
    82.44307    129.0000;
    80.47367    130.0000;
    78.36608    131.0000;
    78.74307    132.0000;
    76.12786    133.0000;
    79.13108    134.0000;
    76.76062    135.0000;
    77.60769    136.0000;
    77.76633    137.0000;
    81.28220    138.0000;
    79.74307    139.0000;
    81.97964    140.0000;
    80.02952    141.0000;
    85.95232    142.0000;
    85.96838    143.0000;
    79.94789    144.0000;
    87.17023    145.0000;
    90.50992    146.0000;
    93.23373    147.0000;
    89.14803    148.0000;
    93.11492    149.0000;
    90.34337    150.0000;
    93.69421    151.0000;
    95.74256    152.0000;
    91.85105    153.0000;
    96.74503    154.0000;
    87.60996    155.0000;
    90.47012    156.0000;
    88.11690    157.0000;
    85.70673    158.0000;
    85.01361    159.0000;
    78.53040    160.0000;
    81.34148    161.0000;
    75.19295    162.0000;
    72.66115    163.0000;
    69.85504    164.0000;
    66.29476    165.0000;
    63.58502    166.0000;
    58.33847    167.0000;
    57.50766    168.0000;
    52.80498    169.0000;
    50.79319    170.0000;
    47.03490    171.0000;
    46.47090    172.0000;
    43.09016    173.0000;
    34.11531    174.0000;
    39.28235    175.0000;
    32.68386    176.0000;
    30.44056    177.0000;
    31.98932    178.0000;
    23.63330    179.0000;
    23.69643    180.0000;
    20.26812    181.0000;
    19.07074    182.0000;
    17.59544    183.0000;
    16.08785    184.0000;
    18.94267    185.0000;
    18.61354    186.0000;
    17.25800    187.0000;
    16.62285    188.0000;
    13.48367    189.0000;
    15.37647    190.0000;
    13.47208    191.0000;
    15.96188    192.0000;
    12.32547    193.0000;
    16.33880    194.0000;
    10.438330    195.0000;
    9.628715    196.0000;
    13.12268    197.0000;
    8.772417    198.0000;
    11.76143    199.0000;
    12.55020    200.0000;
    11.33108    201.0000;
    11.20493    202.0000;
    7.816916    203.0000;
    6.800675    204.0000;
    14.26581    205.0000;
    10.66285    206.0000;
    8.911574    207.0000;
    11.56733    208.0000;
    11.58207    209.0000;
    11.59071    210.0000;
    9.730134    211.0000;
    11.44237    212.0000;
    11.22912    213.0000;
    10.172130    214.0000;
    12.50905    215.0000;
    6.201493    216.0000;
    9.019605    217.0000;
    10.80607    218.0000;
    13.09625    219.0000;
    3.914271    220.0000;
    9.567886    221.0000;
    8.038448    222.0000;
    10.231040    223.0000;
    9.367410    224.0000;
    7.695971    225.0000;
    6.118575    226.0000;
    8.793207    227.0000;
    7.796692    228.0000;
    12.45065    229.0000;
    10.61601    230.0000;
    6.001003    231.0000;
    6.765098    232.0000;
    8.764653    233.0000;
    4.586418    234.0000;
    8.390783    235.0000;
    7.209202    236.0000;
    10.012090    237.0000;
    7.327461    238.0000;
    6.525136    239.0000;
    2.840065    240.0000;
    10.323710    241.0000;
    4.790035    242.0000;
    8.376431    243.0000;
    6.263980    244.0000;
    2.705892    245.0000;
    8.362109    246.0000;
    8.983507    247.0000;
    3.362469    248.0000;
    1.182678    249.0000;
    4.875312    250.0000;
    ]

    parameters = [
    96.0        98.0;
    0.009       0.0105;
    103.0       103.0;
    106.0       105.0;
    18.0        20.0;
    72.0        73.0;
    151.0       150.0;
    18.0        20.0;
    ]
    f(x, beta) = beta[1] * exp(-beta[2]*x) + beta[3] * exp(-(x - beta[4])^2 / beta[5]^2) + beta[6]*exp(-(x - beta[7])^2 / beta[8]^2)

     solution = [
     9.9018328406E+01 ,
     1.0994945399E-02 ,
     1.0188022528E+02 ,
     1.0703095519E+02 ,
     2.3578584029E+01 ,
     7.2045589471E+01 ,
     1.5327010194E+02 ,
     1.9525972636E+01
     ]
    return name, data, parameters, f, solution
end

function DanWood()
    name = "DanWood"
    data = [
    2.138E0        1.309E0;
    3.421E0        1.471E0;
    3.597E0        1.490E0;
    4.340E0        1.565E0;
    4.882E0        1.611E0;
    5.660E0        1.680E0;
    ]
    parameters =[
    1           0.7;
    5           4;
    ]
    f(x, beta) = beta[1] * x^beta[2]
    solution =  [7.6886226176E-01,  3.8604055871E+00]

    return name, data, parameters, f, solution
end

function Misra1b()
    name = "Misra1b"
    data =[
    10.07E0      77.6E0;
    14.73E0     114.9E0;
    17.94E0     141.1E0;
    23.93E0     190.8E0;
    29.61E0     239.9E0;
    35.18E0     289.0E0;
    40.02E0     332.8E0;
    44.82E0     378.4E0;
    50.76E0     434.8E0;
    55.05E0     477.3E0;
    61.01E0     536.8E0;
    66.40E0     593.1E0;
    75.47E0     689.1E0;
    81.78E0     760.0E0;
    ]

    parameters = [
    500         300   ;
    0.0001      0.0002;
    ]

    f(x, beta) =  beta[1]* (1 - (1 / (1 + beta[2] * x / 2)^2))
    solution = [3.3799746163E+02, 3.9039091287E-04]

    return name, data, parameters, f, solution
end

function MGH09()
    name = "MGH09"
    data = [
    1.957000E-01    4.000000E+00;
    1.947000E-01    2.000000E+00;
    1.735000E-01    1.000000E+00;
    1.600000E-01    5.000000E-01;
    8.440000E-02    2.500000E-01;
    6.270000E-02    1.670000E-01;
    4.560000E-02    1.250000E-01;
    3.420000E-02    1.000000E-01;
    3.230000E-02    8.330000E-02;
    2.350000E-02    7.140000E-02;
    2.460000E-02    6.250000E-02;
    ]

    parameters = [
    25          0.25 ;
    39          0.39 ;
    41.5        0.415;
    39          0.39 ;
    ]
    f(x, beta) =  beta[1]*(x^2 + x*beta[2]) / (x^2 + x * beta[3] + beta[4])
    solution = [
    1.9280693458E-01,
    1.9128232873E-01,
    1.2305650693E-01,
    1.3606233068E-01
    ]
    return name, data, parameters, f, solution
end

function Thurber()
    name = "Thurber"
    data = [
    80.574E0      -3.067E0;
    84.248E0      -2.981E0;
    87.264E0      -2.921E0;
    87.195E0      -2.912E0;
    89.076E0      -2.840E0;
    89.608E0      -2.797E0;
    89.868E0      -2.702E0;
    90.101E0      -2.699E0;
    92.405E0      -2.633E0;
    95.854E0      -2.481E0;
    100.696E0      -2.363E0;
    101.060E0      -2.322E0;
    401.672E0      -1.501E0;
    390.724E0      -1.460E0;
    567.534E0      -1.274E0;
    635.316E0      -1.212E0;
    733.054E0      -1.100E0;
    759.087E0      -1.046E0;
    894.206E0      -0.915E0;
    990.785E0      -0.714E0;
    1090.109E0      -0.566E0;
    1080.914E0      -0.545E0;
    1122.643E0      -0.400E0;
    1178.351E0      -0.309E0;
    1260.531E0      -0.109E0;
    1273.514E0      -0.103E0;
    1288.339E0       0.010E0;
    1327.543E0       0.119E0;
    1353.863E0       0.377E0;
    1414.509E0       0.790E0;
    1425.208E0       0.963E0;
    1421.384E0       1.006E0;
    1442.962E0       1.115E0;
    1464.350E0       1.572E0;
    1468.705E0       1.841E0;
    1447.894E0       2.047E0;
    1457.628E0       2.200E0;
    ]

    parameters = [
    1000        1300     ;
    1000        1500     ;
    400         500      ;
    40          75       ;
    0.7         1        ;
    0.3         0.4      ;
    0.03        0.05     ;
    ]
    f(x, beta) =  (beta[1] + beta[2]*x + beta[3]*x^2 + beta[4]*x^3) / (1 + beta[5]*x + beta[6]*x^2 + beta[7]*x^3)
    solution = [
    1.2881396800E+03 ,
     1.4910792535E+03,
     5.8323836877E+02,
     7.5416644291E+01,
     9.6629502864E-01,
     3.9797285797E-01,
     4.9727297349E-02
     ]
    return name, data, parameters, f, solution
end

function BoxBOD()
    name = "BoxBOD"
    data = [
    109             1;
    149             2;
    149             3;
    191             5;
    213             7;
    224            10;
    ]

    parameters = [
    1           100;
    1          0.75;
    ]
    f(x, beta) =  beta[1]* (1 - exp(-beta[2]*x))
    solution = [2.1380940889E+02,  5.4723748542E-01]

    return name, data, parameters, f, solution
end

function Rat42()
    name = "Rat42"
    data = [
    8.930E0        9.000E0;
    10.800E0       14.000E0;
    18.590E0       21.000E0;
    22.330E0       28.000E0;
    39.350E0       42.000E0;
    56.110E0       57.000E0;
    61.730E0       63.000E0;
    64.620E0       70.000E0;
    67.080E0       79.000E0;
    ]

    parameters = [
    100         75;
    1          2.5;
    0.1        0.07;
    ]

    f(x, beta) = beta[1] / (1 + exp(beta[2] - beta[3]*x))
    solution = [ 7.2462237576E+01 , 2.6180768402E+00, 6.7359200066E-02]

    return name, data, parameters, f, solution
end

function MGH10()
    name = "MGH10"
    data = [
    3.478000E+04    5.000000E+01;
    2.861000E+04    5.500000E+01;
    2.365000E+04    6.000000E+01;
    1.963000E+04    6.500000E+01;
    1.637000E+04    7.000000E+01;
    1.372000E+04    7.500000E+01;
    1.154000E+04    8.000000E+01;
    9.744000E+03    8.500000E+01;
    8.261000E+03    9.000000E+01;
    7.030000E+03    9.500000E+01;
    6.005000E+03    1.000000E+02;
    5.147000E+03    1.050000E+02;
    4.427000E+03    1.100000E+02;
    3.820000E+03    1.150000E+02;
    3.307000E+03    1.200000E+02;
    2.872000E+03    1.250000E+02;
    ]

    parameters = [
    2         0.02 ;
    400000      4000 ;
    25000       250    ;
    ]

    f(x, beta) =  beta[1] * exp(beta[2] / (x + beta[3]))
    solution = [5.6096364710E-03, 6.1813463463E+03, 3.4522363462E+02    ]
    return name, data, parameters, f, solution
end

function Eckerle4()
    name = "Eckerle4"
    data = [
    0.0001575E0    400.000000E0;
    0.0001699E0    405.000000E0;
    0.0002350E0    410.000000E0;
    0.0003102E0    415.000000E0;
    0.0004917E0    420.000000E0;
    0.0008710E0    425.000000E0;
    0.0017418E0    430.000000E0;
    0.0046400E0    435.000000E0;
    0.0065895E0    436.500000E0;
    0.0097302E0    438.000000E0;
    0.0149002E0    439.500000E0;
    0.0237310E0    441.000000E0;
    0.0401683E0    442.500000E0;
    0.0712559E0    444.000000E0;
    0.1264458E0    445.500000E0;
    0.2073413E0    447.000000E0;
    0.2902366E0    448.500000E0;
    0.3445623E0    450.000000E0;
    0.3698049E0    451.500000E0;
    0.3668534E0    453.000000E0;
    0.3106727E0    454.500000E0;
    0.2078154E0    456.000000E0;
    0.1164354E0    457.500000E0;
    0.0616764E0    459.000000E0;
    0.0337200E0    460.500000E0;
    0.0194023E0    462.000000E0;
    0.0117831E0    463.500000E0;
    0.0074357E0    465.000000E0;
    0.0022732E0    470.000000E0;
    0.0008800E0    475.000000E0;
    0.0004579E0    480.000000E0;
    0.0002345E0    485.000000E0;
    0.0001586E0    490.000000E0;
    0.0001143E0    495.000000E0;
    0.0000710E0    500.000000E0;
    ]

    parameters = [
    1           1.5 ;
    10           5   ;
    500         450   ;
    ]
    f(x, beta) = beta[1] / beta[2] * exp(-(x - beta[3])^2 / (2*beta[2]^2))
    solution = [1.5543827178E+00    ,   4.0888321754E+00    ,4.5154121844E+02]

    return name, data, parameters, f, solution
end

function Rat43()
    name = "Rat43"
    data =[
    16.08E0     1.0E0;
    33.83E0     2.0E0;
    65.80E0     3.0E0;
    97.20E0     4.0E0;
    191.55E0     5.0E0;
    326.20E0     6.0E0;
    386.87E0     7.0E0;
    520.53E0     8.0E0;
    590.03E0     9.0E0;
    651.92E0    10.0E0;
    724.93E0    11.0E0;
    699.56E0    12.0E0;
    689.96E0    13.0E0;
    637.56E0    14.0E0;
    717.41E0    15.0E0;
    ]

    parameters = [
    100         700;
    10           5;
    1           0.75;
    1           1.3 ;
    ]
    f(x, beta) = beta[1] / (1 + exp(beta[2] - beta[3] * x))^(1/beta[4])
    solution = [
    6.9964151270E+02,
    5.2771253025E+00,
    7.5962938329E-01,
    1.2792483859E+00
  ]

    return name, data, parameters, f, solution
end

function Bennett5()

    name = "Bennet5"
    data = [
    -34.834702E0      7.447168E0;
    -34.393200E0      8.102586E0;
    -34.152901E0      8.452547E0;
    -33.979099E0      8.711278E0;
    -33.845901E0      8.916774E0;
    -33.732899E0      9.087155E0;
    -33.640301E0      9.232590E0;
    -33.559200E0      9.359535E0;
    -33.486801E0      9.472166E0;
    -33.423100E0      9.573384E0;
    -33.365101E0      9.665293E0;
    -33.313000E0      9.749461E0;
    -33.260899E0      9.827092E0;
    -33.217400E0      9.899128E0;
    -33.176899E0      9.966321E0;
    -33.139198E0     10.029280E0;
    -33.101601E0     10.088510E0;
    -33.066799E0     10.144430E0;
    -33.035000E0     10.197380E0;
    -33.003101E0     10.247670E0;
    -32.971298E0     10.295560E0;
    -32.942299E0     10.341250E0;
    -32.916302E0     10.384950E0;
    -32.890202E0     10.426820E0;
    -32.864101E0     10.467000E0;
    -32.841000E0     10.505640E0;
    -32.817799E0     10.542830E0;
    -32.797501E0     10.578690E0;
    -32.774300E0     10.613310E0;
    -32.757000E0     10.646780E0;
    -32.733799E0     10.679150E0;
    -32.716400E0     10.710520E0;
    -32.699100E0     10.740920E0;
    -32.678799E0     10.770440E0;
    -32.661400E0     10.799100E0;
    -32.644001E0     10.826970E0;
    -32.626701E0     10.854080E0;
    -32.612202E0     10.880470E0;
    -32.597698E0     10.906190E0;
    -32.583199E0     10.931260E0;
    -32.568699E0     10.955720E0;
    -32.554298E0     10.979590E0;
    -32.539799E0     11.002910E0;
    -32.525299E0     11.025700E0;
    -32.510799E0     11.047980E0;
    -32.499199E0     11.069770E0;
    -32.487598E0     11.091100E0;
    -32.473202E0     11.111980E0;
    -32.461601E0     11.132440E0;
    -32.435501E0     11.152480E0;
    -32.435501E0     11.172130E0;
    -32.426800E0     11.191410E0;
    -32.412300E0     11.210310E0;
    -32.400799E0     11.228870E0;
    -32.392101E0     11.247090E0;
    -32.380501E0     11.264980E0;
    -32.366001E0     11.282560E0;
    -32.357300E0     11.299840E0;
    -32.348598E0     11.316820E0;
    -32.339901E0     11.333520E0;
    -32.328400E0     11.349940E0;
    -32.319698E0     11.366100E0;
    -32.311001E0     11.382000E0;
    -32.299400E0     11.397660E0;
    -32.290699E0     11.413070E0;
    -32.282001E0     11.428240E0;
    -32.273300E0     11.443200E0;
    -32.264599E0     11.457930E0;
    -32.256001E0     11.472440E0;
    -32.247299E0     11.486750E0;
    -32.238602E0     11.500860E0;
    -32.229900E0     11.514770E0;
    -32.224098E0     11.528490E0;
    -32.215401E0     11.542020E0;
    -32.203800E0     11.555380E0;
    -32.198002E0     11.568550E0;
    -32.189400E0     11.581560E0;
    -32.183601E0     11.594420E0;
    -32.174900E0     11.607121E0;
    -32.169102E0     11.619640E0;
    -32.163300E0     11.632000E0;
    -32.154598E0     11.644210E0;
    -32.145901E0     11.656280E0;
    -32.140099E0     11.668200E0;
    -32.131401E0     11.679980E0;
    -32.125599E0     11.691620E0;
    -32.119801E0     11.703130E0;
    -32.111198E0     11.714510E0;
    -32.105400E0     11.725760E0;
    -32.096699E0     11.736880E0;
    -32.090900E0     11.747890E0;
    -32.088001E0     11.758780E0;
    -32.079300E0     11.769550E0;
    -32.073502E0     11.780200E0;
    -32.067699E0     11.790730E0;
    -32.061901E0     11.801160E0;
    -32.056099E0     11.811480E0;
    -32.050301E0     11.821700E0;
    -32.044498E0     11.831810E0;
    -32.038799E0     11.841820E0;
    -32.033001E0     11.851730E0;
    -32.027199E0     11.861550E0;
    -32.024300E0     11.871270E0;
    -32.018501E0     11.880890E0;
    -32.012699E0     11.890420E0;
    -32.004002E0     11.899870E0;
    -32.001099E0     11.909220E0;
    -31.995300E0     11.918490E0;
    -31.989500E0     11.927680E0;
    -31.983700E0     11.936780E0;
    -31.977900E0     11.945790E0;
    -31.972099E0     11.954730E0;
    -31.969299E0     11.963590E0;
    -31.963501E0     11.972370E0;
    -31.957701E0     11.981070E0;
    -31.951900E0     11.989700E0;
    -31.946100E0     11.998260E0;
    -31.940300E0     12.006740E0;
    -31.937401E0     12.015150E0;
    -31.931601E0     12.023490E0;
    -31.925800E0     12.031760E0;
    -31.922899E0     12.039970E0;
    -31.917101E0     12.048100E0;
    -31.911301E0     12.056170E0;
    -31.908400E0     12.064180E0;
    -31.902599E0     12.072120E0;
    -31.896900E0     12.080010E0;
    -31.893999E0     12.087820E0;
    -31.888201E0     12.095580E0;
    -31.885300E0     12.103280E0;
    -31.882401E0     12.110920E0;
    -31.876600E0     12.118500E0;
    -31.873699E0     12.126030E0;
    -31.867901E0     12.133500E0;
    -31.862101E0     12.140910E0;
    -31.859200E0     12.148270E0;
    -31.856300E0     12.155570E0;
    -31.850500E0     12.162830E0;
    -31.844700E0     12.170030E0;
    -31.841801E0     12.177170E0;
    -31.838900E0     12.184270E0;
    -31.833099E0     12.191320E0;
    -31.830200E0     12.198320E0;
    -31.827299E0     12.205270E0;
    -31.821600E0     12.212170E0;
    -31.818701E0     12.219030E0;
    -31.812901E0     12.225840E0;
    -31.809999E0     12.232600E0;
    -31.807100E0     12.239320E0;
    -31.801300E0     12.245990E0;
    -31.798401E0     12.252620E0;
    -31.795500E0     12.259200E0;
    -31.789700E0     12.265750E0;
    -31.786800E0     12.272240E0;
    ]

    parameters = [
    -2000       -1500;
    50          45;
    0.8         0.85;
    ]
    f(x, beta) = beta[1] *(beta[2] + x)^(-1/beta[3])
    solution = [    -2.5235058043E+03,4.6736564644E+01,9.3218483193E-01]
    return name, data, parameters, f, solution
end

function ff!(fcur, x, f, data)
    for i in 1:size(data, 1)
        fcur[i] = data[i, 1] - f(data[i, 2], x)
    end
end

tests =  [misra1a, Chwirut2, Chwitrut1, Lanczos3, Gauss1, Gauss2, DanWood, Misra1b, MGH09, Thurber, BoxBOD, Rat42, MGH10, Eckerle4, Rat43, Bennett5]

for (optimizer, optimizer_abbr) in ((LeastSquaresOptim.Dogleg, :dl), (LeastSquaresOptim.LevenbergMarquardt, :lm))
    n = 0
    N = 0
    for test in tests
        global name, data, parameters, f, solution = test()
      #  @show solution
        for j in 1:size(parameters, 2)
            global nls = LeastSquaresProblem(x = parameters[:, j], f! = (fcur, x) -> ff!(fcur, x, f, data), output_length = size(data, 1))
            global r = optimize!(nls, optimizer(LeastSquaresOptim.QR()), x_tol = 1e-50, f_tol = 1e-36, g_tol = 1e-50)
            n += norm(r.minimizer - solution) <= 1e-3
            N += 1
            @test !isnan(mean(r.minimizer) )
        end
    end
    println("strd $optimizer_abbr $n / $N")
end