text:
module HTTPDownloads

# We vendor a copy of Downloads.jl. This is necessary because:
#
# * We need extensions to the version of Downloads which is distributed with
#   julia-1.6
# * As a stdlib it's impossible to install newer versions without upgrading
#   Julia itself.
include("Downloads.jl")

import .Downloads: Downloads, Curl, Downloader

using Sockets: TCPSocket
import NetworkOptions

using URIs
using HTTP

abstract type LibCurlLayer{Next <: HTTP.Layer} <: HTTP.Layer{Next} end

const DOWNLOAD_LOCK = ReentrantLock()
const DOWNLOADER = Ref{Union{Nothing,Downloader}}(nothing)

function http_easy_hook(easy, info)
    # Disable redirects - HTTP.jl will handle this.
    Curl.setopt(easy, Curl.CURLOPT_FOLLOWLOCATION, false)

    # TODO: Figure out how to allow setting of
    # https://curl.se/libcurl/c/CURLMOPT_MAXCONNECTS.html
end

function get_downloader()
    lock(DOWNLOAD_LOCK) do
        yield() # let other downloads finish
        downloader = DOWNLOADER[]
        if downloader === nothing
            # TODO: Install our own easy_handle here.
            downloader = DOWNLOADER[] = Downloader()
            downloader.easy_hook = http_easy_hook
        end
        downloader
    end
end

function HTTP.request(::Type{LibCurlLayer{Next}}, url::URI, req, body;
            response_stream=nothing,
            iofunction=nothing,

            # # TimeoutLayer
            readtimeout=0,

            # # ConnectionLayer
            socket_type::Type=TCPSocket,

            verbose::Int=0,

            # List of all HTTP.jl keywords for the connection pool and stream
            # layers which we could/should consider emulating:

            # ConnectionPool / getconnection / newconnection
            #
            # * connection_limit::Int=default_connection_limit,
            #   We use the multi interface, so probably need to figure out how
            #   to use the multi CURLMOPT_MAXCONNECTS.
            #     https://curl.se/libcurl/c/CURLMOPT_MAXCONNECTS.html
            #     https://curl.se/libcurl/c/CURLOPT_MAXCONNECTS.html
            #
            # * pipeline_limit::Int=1
            #     # likely will be deprecated in HTTP.jl - ignore
            # * idle_timeout::Int=0,
            #     # Similar to the Downloader's grace parameter

            # ConnectionPool / sslconnection
            require_ssl_verification::Bool=NetworkOptions.verify_host(url.host, "SSL"),
            sslconfig=nothing,

            keepalive::Bool=false,
            connect_timeout::Int=0,

            proxy::Union{AbstractString,Nothing}=nothing,
            # * reuse_limit =
            #     No equiv? Ignored for now

            # # StreamLayer
            # * reached_redirect_limit=false,
            #     FIXME ???
            # * iofunction=nothing,
            #     TODO: Figure out callbacks?
            # * verbose::Int=0,

            kw...) where Next

    if iofunction !== nothing    || socket_type !== TCPSocket || sslconfig !== nothing
        # Fallback to pure-Julia implementation in HTTP.ConnectionPool for
        # options we can't handle with libcurl.
        #
        # FIXME: This is required until we can figure out how to expose this
        # functionality via libcurl
        if sslconfig === nothing
            sslconfig = HTTP.ConnectionPool.nosslconfig
        end
        return HTTP.request(Next, url, req, body;
                            response_stream=response_stream,
                            iofunction=iofunction,
                            readtimeout=readtimeout,
                            socket_type=socket_type,
                            sslconfig=sslconfig,
                            kw...)
    end

    input = req.body === HTTP.body_is_a_stream ? body :
            length(req.body) > 0 ? IOBuffer(req.body) : nothing

    output_buf = nothing
    output = response_stream
    if response_stream === nothing
        # HTTP.jl assumes you always want the body by default.
        output = output_buf = IOBuffer()
    end

    function per_request_easy_hook(easy)
        if !require_ssl_verification
            Curl.setopt(easy, Curl.CURLOPT_SSL_VERIFYPEER, false)
        end
        if keepalive
            Curl.setopt(easy, Curl.CURLOPT_TCP_KEEPALIVE, true)
        end
        if proxy !== nothing
            Curl.setopt(easy, Curl.CURLOPT_PROXY, proxy)
        end
        if connect_timeout > 0
            Curl.setopt(easy, Curl.CURLOPT_CONNECTTIMEOUT, connect_timeout)
            # See also https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT_MS.html
            # if we want to allow subsecond timeouts.
        end
    end

    response = Downloads.request(string(url);
        downloader = get_downloader(),
        method = req.method,
        headers = req.headers,
        input = input,
        output = output,
        timeout = readtimeout <= 0 ? Inf : readtimeout,
        throw = false,
        verbose = verbose > 0,
        easy_hook = per_request_easy_hook)

    if response isa Downloads.RequestError
        # We couldn't even get a response. There's a huge list of possible
        # return codes in libcurl's curl.h.
        #
        # Users of HTTP.jl largely seem to match these based on whether they're
        # "retryable", and expect to see HTTP.jl's error types. This informal
        # API is hard to match! For now:
        #
        #   * If an error "seems retryable", wrap in HTTP.IOError
        #   * For other cases, just throw the RequestError
        #   * Ugh.
        #
        #
        # As an example, AWS.jl has the following logic:
        #
        #  # Base.IOError is needed because HTTP.jl can often have errors that aren't
        #  # caught and wrapped in an HTTP.IOError
        #  # https://github.com/JuliaWeb/HTTP.jl/issues/382
        #  @delay_retry if isa(e, Sockets.DNSError) ||
        #                  isa(e, HTTP.ParseError) ||
        #                  isa(e, HTTP.IOError) ||
        #                  isa(e, Base.IOError) ||
        #                  (isa(e, HTTP.StatusError) && _http_status(e) >= 500)
        #
        if response.code == Curl.CURLE_OPERATION_TIMEDOUT
            # HTTP throws
            #   * HTTP.TimeoutRequest.ReadTimeoutError for readtimeout
            #   * HTTP.ConnectionPool.ConnectTimeout   for connect_timeout
            # CURL doesn't distinguish between these however, so it's not easy
            # to know which one we've hit here.
            t = readtimeout > 0 ? readtimeout : connect_timeout
            throw(HTTP.TimeoutRequest.ReadTimeoutError(t))
            # TODO: Would it be better just to throw(response) ??
        elseif response.code in (Curl.CURLE_PEER_FAILED_VERIFICATION,
                                 Curl.CURLE_SSL_CERTPROBLEM,
                                 Curl.CURLE_SSL_CIPHER,
                                 Curl.CURLE_SSL_CACERT_BADFILE,
                                 Curl.CURLE_SSL_CRL_BADFILE,
                                 Curl.CURLE_SSL_ISSUER_ERROR)
            # SSL certificate problems - not retryable, so just throw the
            # original error.
            throw(response)
        else
            # Wrap other errors in HTTP.IOError. Downstream codes will likely
            # assume all such errors are retry-able
            #
            throw(HTTP.IOError(response, response.message))
        end
    end

    # Convert output back into HTTP.Response
    body = output_buf === nothing ?
           HTTP.MessageRequest.body_was_streamed :
           take!(output_buf)

    res = HTTP.Response(response.status, HTTP.mkheaders(response.headers);
                        body=body, request=req)
end

function has_http_layer(stack, layer)
    while true
        if stack === Union{}
            return false
        elseif stack <: layer
            return true
        end
        stack = stack.parameters[1]
    end
end

function set_downloads_backend(use_downloads::Bool=true)
    stack = HTTP.stack()
    if use_downloads && !has_http_layer(stack, LibCurlLayer)
        HTTP.insert_default!(ConnectionPoolLayer, LibCurlLayer)
    elseif !use_downloads && has_http_layer(stack, LibCurlLayer)
        HTTP.remove_default!(ConnectionPoolLayer, LibCurlLayer)
    end
    nothing
end

end


text:
@testset "Spoerer2017" begin
    batchsize = 5
    channels = 3
    classes = 12
    maps = 45
    input1 = rand(28, 28, 1, batchsize)
    input2 = rand(32, 32, 3, batchsize)

    # test B model
    B = spoerer_model_b(Float32)
    @test size(B(input1)) == (10, batchsize)
    B = spoerer_model_b(Float64, channels=channels, inputsize=(32,32), classes=classes)
    @test size(B(input2)) == (classes, batchsize)

    # test model BK
    BK = spoerer_model_bk(Float32)
    @test size(BK(input1)) == (10, batchsize)
    BK = spoerer_model_bk(Float64, channels=channels, inputsize=(32,32), classes=classes)
    @test size(BK(input2)) == (classes, batchsize)

    # test model BF
    BF = spoerer_model_bf(Float32)
    @test size(BF(input1)) == (10, batchsize)
    BF = spoerer_model_bf(Float64, channels=channels, inputsize=(32,32), classes=classes)
    @test size(BF(input2)) == (classes, batchsize)

    # test model BL
    BL = spoerer_model_bl(Float32)
    h = Dict(
        "l1" => zeros(Float32, 28, 28, 32, batchsize),
        "l2" => zeros(Float32, 14, 14, 32, batchsize)
    )
    model = Flux.Recur(BL, h)
    @test size(model(input1)) == (10, batchsize)
    BL = spoerer_model_bl(
        Float64, channels=channels, inputsize=(32,32), classes=classes, features=maps
    )
    h = Dict(
        "l1" => zeros(32, 32, maps, batchsize),
        "l2" => zeros(16, 16, maps, batchsize)
    )
    model = Flux.Recur(BL, h)
    @test size(model(input2)) == (classes, batchsize)

    # test model BT
    BT = spoerer_model_bt(Float32)
    h = Dict(
        "l2" => zeros(Float32, 14, 14, 32, batchsize)
    )
    model = Flux.Recur(BT, h)
    @test size(model(input1)) == (10, batchsize)
    BT = spoerer_model_bt(
        Float64, channels=channels, inputsize=(32,32), classes=classes, features=maps
    )
    h = Dict(
        "l2" => zeros(16, 16, maps, batchsize)
    )
    model = Flux.Recur(BT, h)
    @test size(model(input2)) == (classes, batchsize)

    # test model BLT
    BLT = spoerer_model_blt(Float32)
    h = Dict(
        "l1" => zeros(Float32, 28, 28, 32, batchsize),
        "l2" => zeros(Float32, 14, 14, 32, batchsize)
    )
    model = Flux.Recur(BLT, h)
    @test size(model(input1)) == (10, batchsize)
    BLT = spoerer_model_blt(
        Float64, channels=channels, inputsize=(32,32), classes=classes, features=maps
    )
    h = Dict(
        "l1" => zeros(32, 32, maps, batchsize),
        "l2" => zeros(16, 16, maps, batchsize)
    )
    model = Flux.Recur(BLT, h)
    @test size(model(input2)) == (classes, batchsize)

end # @testset "Spoerer2017"

ext: jl


text:
module straight_duct_examples
using FinEtools
using FinEtoolsAcoustics
using UnicodePlots

function straight_duct_H8_example()
    t0  =  time()

    rho = 1.21*phun("kg/m^3");# mass density
    c  = 343.0*phun("m/s");# sound speed
    bulk =  c^2*rho;
    omega =  54.5901*phun("rev/s")
    vn0 =  -1.0*phun("m/s")
    Lx = 10.0*phun("m");# length of the box, millimeters
    Ly = 1.0*phun("m"); # length of the box, millimeters
    n = 20;#number of elements along the length

    println("""

    Straight duct with anechoic termination.
    Example from Boundary element acoustics: Fundamentals and computer codes, TW Wu, page 44.
    Both real and imaginary components of the pressure should have amplitude of
    rho*c = $(rho*c).

    Hexahedral mesh.
    """)

    fens,fes  =  H8block(Lx,Ly,Ly,n,2,2); # Mesh
    bfes  =  meshboundary(fes)
    L0 = selectelem(fens,bfes,facing = true, direction = [-1.0 0.0 0.0])
    L10 = selectelem(fens,bfes,facing = true, direction = [+1.0 0.0 0.0])
    nLx = selectnode(fens,box = [0.0 Lx  0.0 0.0 0.0 0.0], inflate = Lx/1.0e5)

    geom  =  NodalField(fens.xyz)
    P  =  NodalField(zeros(ComplexF64,size(fens.xyz,1),1))

    numberdofs!(P)

    material = MatAcoustFluid(bulk,rho)
    femm  =  FEMMAcoust(IntegDomain(fes, GaussRule(3, 2)), material)

    S  =  acousticstiffness(femm, geom, P);
    C  =  acousticmass(femm, geom, P);

    E10femm  =  FEMMAcoustSurf(IntegDomain(subset(bfes,L10),GaussRule(2, 2)), material)
    D  =  acousticABC(E10femm, geom, P);

    E0femm  =  FEMMBase(IntegDomain(subset(bfes,L0), GaussRule(2,  2)))
    fi  =  ForceIntensity(-1.0im*omega*rho*vn0);
    F  =  distribloads(E0femm, geom, P, fi, 2);

    p = (-omega^2*S +omega*1.0im*D + C)\F
    scattersysvec!(P, p[:])

    println("Pressure amplitude bounds: ")
    println("  real $(minimum(real(P.values)))/$(maximum(real(P.values)))")
    println("  imag $(minimum(imag(P.values)))/$(maximum(imag(P.values)))")

    println("Total time elapsed  =  ",time() - t0,"s")

    File  =   "straight_duct.vtk"
    scalars = real(P.values);
    vtkexportmesh(File, connasarray(fes), geom.values, FinEtools.MeshExportModule.H8;
    scalars = [("Pressure", scalars)])
    @async run(`"paraview.exe" $File`)

    # @pyimport matplotlib.pyplot as plt
    # plt.style[:use]("seaborn-whitegrid")
    # fig = plt.figure()
    # ax = plt.axes()
    # ix = sortperm(geom.values[nLx,1])
    # ax[:plot](geom.values[nLx,1][ix], real(P.values)[nLx][ix], color = :blue, label = "real")
    # ax[:plot](geom.values[nLx,1][ix], imag(P.values)[nLx][ix], color = :red, label  =  "imag")
    # ax[:set_xlabel]("x coordinate")
    # ax[:set_ylabel]("Pressure")
    # plt.show()
    # set(axis="normal", plotstyle="linespoints", linewidth=3, pointsize = 2, color = "black", xlabel = "x coordinate", ylabel = "Pressure", grid="on", title = "")
    ix = sortperm(geom.values[nLx,1])
    # f = figure()
    # plot(geom.values[nLx,1][ix], real(P.values)[nLx][ix], legend = "real", marker = "ecircle")
    # plot!(geom.values[nLx,1][ix], imag(P.values)[nLx][ix], legend = "imag", marker = "edmd")
    # figure(f)
    plt = lineplot(geom.values[nLx,1][ix], real(P.values)[nLx][ix], canvas = DotCanvas, title = "Steady-state pressure", name = "real", xlabel = "x", ylabel = "P")
    plt = lineplot!(plt, geom.values[nLx,1][ix], imag(P.values)[nLx][ix], name = "imag")
    display(plt)

    true

end # straight_duct_H8_example

function straight_duct_T10_example()
    t0  =  time()

    rho = 1.21*phun("kg/m^3");# mass density
    c  = 343.0*phun("m/s");# sound speed
    bulk =  c^2*rho;
    omega =  54.5901*phun("rev/s")
    vn0 =  -1.0*phun("m/s")
    Lx = 10.0*phun("m");# length of the box, millimeters
    Ly = 1.0*phun("m"); # length of the box, millimeters
    n = 20;#number of elements along the length

    println("""

    Straight duct with anechoic termination.
    Example from Boundary element acoustics: Fundamentals and computer codes, TW Wu, page 44.
    Both real and imaginary components of the pressure should have amplitude of
    rho*c = $(rho*c).

    Quadratic tetrahedral mesh.
    """)

    fens,fes  =  T10block(Lx,Ly,Ly,n,2,2); # Mesh
    bfes  =  meshboundary(fes)
    L0 = selectelem(fens,bfes,facing = true, direction = [-1.0 0.0 0.0])
    L10 = selectelem(fens,bfes,facing = true, direction = [+1.0 0.0 0.0])
    nLx = selectnode(fens,box = [0.0 Lx  0.0 0.0 0.0 0.0], inflate = Lx/1.0e5)

    geom  =  NodalField(fens.xyz)
    P  =  NodalField(zeros(ComplexF64,size(fens.xyz,1),1))

    numberdofs!(P)

    material = MatAcoustFluid(bulk,rho)
    femm  =  FEMMAcoust(IntegDomain(fes, TetRule(5)), material)

    S  =  acousticstiffness(femm, geom, P);
    C  =  acousticmass(femm, geom, P);

    E10femm  =  FEMMAcoustSurf(IntegDomain(subset(bfes,L10),TriRule(6)), material)
    D  =  acousticABC(E10femm, geom, P);

    E0femm  =  FEMMBase(IntegDomain(subset(bfes,L0), TriRule(6)))
    fi  =  ForceIntensity(-1.0im*omega*rho*vn0);
    F  =  distribloads(E0femm, geom, P, fi, 2);

    p = (-omega^2*S +omega*1.0im*D + C)\F
    scattersysvec!(P, p[:])

    println("Pressure amplitude bounds: ")
    println("  real $(minimum(real(P.values)))/$(maximum(real(P.values)))")
    println("  imag $(minimum(imag(P.values)))/$(maximum(imag(P.values)))")

    println("Total time elapsed  =  ",time() - t0,"s")

    File  =   "straight_duct_T10.vtk"
    scalars = real(P.values);
    vtkexportmesh(File, connasarray(fes), geom.values, FinEtools.MeshExportModule.T10;
    scalars = [("Pressure", scalars)])
    @async run(`"paraview.exe" $File`)

    # @pyimport matplotlib.pyplot as plt
    # plt.style[:use]("seaborn-whitegrid")
    # fig = plt.figure()
    # ax = plt.axes()
    # ix = sortperm(geom.values[nLx,1])
    # ax[:plot](geom.values[nLx,1][ix], real(P.values)[nLx][ix], color = :blue, label = "real")
    # ax[:plot](geom.values[nLx,1][ix], imag(P.values)[nLx][ix], color = :red, label  =  "imag")
    # ax[:set_xlabel]("x coordinate")
    # ax[:set_ylabel]("Pressure")
    # plt.show()
    # set(axis="normal", plotstyle="linespoints", linewidth=3, pointsize = 2, color = "black", xlabel = "x coordinate", ylabel = "Pressure", grid="on", title = "")
    ix = sortperm(geom.values[nLx,1])
    # f = figure()
    # plot(geom.values[nLx,1][ix], real(P.values)[nLx][ix], legend = "real", marker = "ecircle")
    # plot!(geom.values[nLx,1][ix], imag(P.values)[nLx][ix], legend = "imag", marker = "edmd")
    # figure(f)
    plt = lineplot(geom.values[nLx,1][ix], real(P.values)[nLx][ix], canvas = DotCanvas, title = "Steady-state pressure", name = "real", xlabel = "x", ylabel = "P")
    plt = lineplot!(plt, geom.values[nLx,1][ix], imag(P.values)[nLx][ix], name = "imag")
    display(plt)

    true

end # straight_duct_T10_example

function straight_duct_T4_example()
    t0  =  time()

    rho = 1.21*phun("kg/m^3");# mass density
    c  = 343.0*phun("m/s");# sound speed
    bulk =  c^2*rho;
    omega =  54.5901*phun("rev/s")
    vn0 =  -1.0*phun("m/s")
    Lx = 10.0*phun("m");# length of the box, millimeters
    Ly = 1.0*phun("m"); # length of the box, millimeters
    n = 20;#number of elements along the length

    println("""

    Straight duct with anechoic termination.
    Example from Boundary element acoustics: Fundamentals and computer codes, TW Wu, page 44.
    Both real and imaginary components of the pressure should have amplitude of
    rho*c = $(rho*c).

    Tetrahedral mesh.
    """)

    fens,fes  =  T4block(Lx,Ly,Ly,n,2,2); # Mesh
    bfes  =  meshboundary(fes)
    L0 = selectelem(fens,bfes,facing = true, direction = [-1.0 0.0 0.0])
    L10 = selectelem(fens,bfes,facing = true, direction = [+1.0 0.0 0.0])
    nLx = selectnode(fens,box = [0.0 Lx  0.0 0.0 0.0 0.0], inflate = Lx/1.0e5)

    geom  =  NodalField(fens.xyz)
    P  =  NodalField(zeros(ComplexF64,size(fens.xyz,1),1))

    numberdofs!(P)

    material = MatAcoustFluid(bulk,rho)
    femm  =  FEMMAcoust(IntegDomain(fes, SimplexRule(3, 1)), material)

    S  =  acousticstiffness(femm, geom, P);
    C  =  acousticmass(femm, geom, P);

    E10femm  =  FEMMAcoustSurf(IntegDomain(subset(bfes,L10),TriRule(3)), material)
    D  =  acousticABC(E10femm, geom, P);

    E0femm  =  FEMMBase(IntegDomain(subset(bfes,L0), TriRule(3)))
    fi  =  ForceIntensity(-1.0im*omega*rho*vn0);
    F  =  distribloads(E0femm, geom, P, fi, 2);

    p = (-omega^2*S +omega*1.0im*D + C)\F
    scattersysvec!(P, p[:])

    println("Pressure amplitude bounds: ")
    println("  real $(minimum(real(P.values)))/$(maximum(real(P.values)))")
    println("  imag $(minimum(imag(P.values)))/$(maximum(imag(P.values)))")

    println("Total time elapsed  =  ",time() - t0,"s")

    File  =   "straight_duct_T4.vtk"
    scalars = real(P.values);
    vtkexportmesh(File, connasarray(fes), geom.values, FinEtools.MeshExportModule.T4;
    scalars = [("Pressure", scalars)])
    @async run(`"paraview.exe" $File`)

    # @pyimport matplotlib.pyplot as plt
    # plt.style[:use]("seaborn-whitegrid")
    # fig = plt.figure()
    # ax = plt.axes()
    # ix = sortperm(geom.values[nLx,1])
    # ax[:plot](geom.values[nLx,1][ix], real(P.values)[nLx][ix], color = :blue, label = "real")
    # ax[:plot](geom.values[nLx,1][ix], imag(P.values)[nLx][ix], color = :red, label  =  "imag")
    # ax[:set_xlabel]("x coordinate")
    # ax[:set_ylabel]("Pressure")
    # plt.show()
    # set(axis="normal", plotstyle="linespoints", linewidth=3, pointsize = 2, color = "black", xlabel = "x coordinate", ylabel = "Pressure", grid="on", title = "")
    ix = sortperm(geom.values[nLx,1])
    # f = figure()
    # plot(geom.values[nLx,1][ix], real(P.values)[nLx][ix], legend = "real", marker = "ecircle")
    # plot!(geom.values[nLx,1][ix], imag(P.values)[nLx][ix], legend = "imag", marker = "edmd")
    # figure(f)
    plt = lineplot(geom.values[nLx,1][ix], real(P.values)[nLx][ix], canvas = DotCanvas, title = "Steady-state pressure", name = "real", xlabel = "x", ylabel = "P")
    plt = lineplot!(plt, geom.values[nLx,1][ix], imag(P.values)[nLx][ix], name = "imag")
    display(plt)

    true

end # straight_duct_T4_example

function allrun()
    println("#####################################################")
    println("# straight_duct_H8_example ")
    straight_duct_H8_example()
    println("#####################################################")
    println("# straight_duct_T10_example ")
    straight_duct_T10_example()
    println("#####################################################")
    println("# straight_duct_T4_example ")
    straight_duct_T4_example()
end # function allrun

end # module straight_duct_examples


text:
using ObjectDetector
using Test, PrettyTables
using FileIO, ImageCore

dThresh = 0.5 #Detect Threshold (minimum acceptable confidence)
oThresh = 0.5 #Overlap Threshold (maximum acceptable IoU)
@info "Testing all models with detectThresh = $dThresh, overlapThresh = $oThresh"

testimages = ["dog-cycle-car","dog-cycle-car_nonsquare"]
pretrained_list = [
                    YOLO.v2_tiny_416_COCO,
                    # YOLO.v2_608_COCO,
                    YOLO.v3_tiny_416_COCO,
                    YOLO.v3_320_COCO,
                    YOLO.v3_416_COCO,
                    YOLO.v3_608_COCO,
                    # YOLO.v3_spp_608_COCO
                    ]

header = ["Model" "loaded?" "load time (s)" "ran?" "run time (s)" "objects detected"]
table = Array{Any}(undef, length(pretrained_list) * length(testimages), 6)
for (j, imagename) in pairs(testimages)
    global table
    @info """Testing image "$imagename" """
    IMG = load(joinpath(@__DIR__,"images","$imagename.png"))
    resultsdir = joinpath(@__DIR__,"results",imagename)
    !isdir(resultsdir) && mkdir(resultsdir)
    for (k, pretrained) in pairs(pretrained_list)
        i = ((j-1)*length(pretrained_list)) + k
        modelname = string(pretrained)
        table[i,:] .= [modelname, false, "-", "-", "-", "-"]
        @testset "Pretrained Model: $modelname" begin
            global table

            t_load = @elapsed begin
                yolomod = pretrained(silent=true)
            end
            table[i, 2] = true
            table[i, 3] = round(t_load, digits=3)
            @info "$modelname: Loaded in $(round(t_load, digits=2)) seconds."

            batch = emptybatch(yolomod)
            batch[:,:,:,1], padding = prepareImage(IMG, yolomod)

            res = yolomod(batch, detectThresh=dThresh, overlapThresh=oThresh) #run once
            @test size(res,2) > 0

            val, t_run, bytes, gctime, m = @timed yolomod(batch, detectThresh=dThresh, overlapThresh=oThresh);
            table[i, 4] = true
            table[i, 5] = round(t_run, digits=4)
            table[i, 6] = size(res, 2)
            @info "$modelname: Ran in $(round(t_run, digits=2)) seconds. (bytes $bytes, gctime $gctime)"

            imgBoxes = drawBoxes(IMG, yolomod, padding, res)
            resfile = joinpath(resultsdir,"$(modelname).jpg")
            save(resfile, imgBoxes)
            @info "$modelname: View result: $resfile"

        end
        GC.gc()
    end
end
pretty_table(table, header)
@info "Times approximate. For more accurate benchmarking run ObjectDetector.benchmark()"

@testset "Custom cfg's" begin
    @testset "Valid non-square dimensions (512x384)" begin
        IMG = load(joinpath(@__DIR__,"images","dog-cycle-car.png"))
        yolomod = YOLO.v3_COCO(silent=true, cfgchanges=[(:net, 1, :width, 512), (:net, 1, :height, 384)])
        batch = emptybatch(yolomod)
        batch[:,:,:,1], padding = prepareImage(IMG, yolomod)
        res = yolomod(batch, detectThresh=dThresh, overlapThresh=oThresh) #run once
        @test size(res,2) > 0
    end
    @testset "Invalid non-square dimensions" begin
        IMG = load(joinpath(@__DIR__,"images","dog-cycle-car.png"))
        # invalid height
        @test_throws AssertionError YOLO.v3_COCO(silent=false, w=512, h=383)
        # invalid width
        @test_throws AssertionError YOLO.v3_COCO(silent=false, w=511, h=384)
    end
end

@testset "Drawing boxes" begin
    # Testing that boxes are drawn as expected

    ### square model
    yolomod = YOLO.v3_tiny_COCO(w=416, h=416, silent=true)
    #Nonsquare low aspect ratio image
    img = ones(Gray, 200, 100)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    #Nonsquare high aspect ratio image
    img = ones(Gray, 100, 200)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    #Square image, square model
    img = ones(Gray, 100, 100)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    ### nonsquare low aspect ratio model
    yolomod = YOLO.v3_tiny_COCO(w=512, h=416, silent=true)
    #Square image
    img = ones(Gray, 100, 100)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    #nonsquare low aspect ratio image
    img = ones(Gray, 200, 100)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    ### nonsquare high aspect ratio model
    yolomod = YOLO.v3_tiny_COCO(w=416, h=512, silent=true)
    #Square image
    img = ones(Gray, 100, 100)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    #nonsquare low aspect ratio image
    img = ones(Gray, 100, 200)
    batch = emptybatch(yolomod)
    batch[:,:,:,1], padding = prepareImage(img, yolomod)
    res = collect([ padding[1] padding[2] 1.0-padding[3] 1.0-padding[4] 0.0 0.0;]') #note the transpose!
    imgboxes = drawBoxes(img, yolomod, padding, res)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

    ## Non-transposed
    imgboxes = drawBoxes(collect(img'), yolomod, padding, res, transpose=false)
    @test all(imgboxes[1,:] .== Gray(0))
    @test all(imgboxes[:,1] .== Gray(0))
    @test all(imgboxes[end,:] .== Gray(0))
    @test all(imgboxes[:,end] .== Gray(0))

end


text:
function update_v_t()
    if constant_in_time
        midterm.v = y[t] - Z*a
    else
        midterm.v = y[t] - Z*a
    end
end


text:
"""
    ADI.ADIDesign

An abstract type used for holding the output of [`ADI.fit`](@ref). The purpose of these types is to hold the *minimum* information required to reconstruct the PSF estimate (e.g. weights and principal components from PCA). ADI.jl includes two designs- [`ADI.ClassicDesign`](@ref) and [`ADI.LinearDesign`](@ref).

## Interface

When implementing a new algorithm, if your outputs do not fit into either of those designs, you will have to create your own `ADIDesign` with the following methods-

    ADI.design(::Design)

accessor for the pertinent data to approximate the PSF

---

    reconstruct(::Design)

return the approximate PSF estimate as a matrix
"""
abstract type ADIDesign end

# eg MultiAnnulusView
reconstruct(designs::AbstractVector{<:ADIDesign}) = map(reconstruct, designs)

"""
    ADI.LinearDesign(basis, coeffs)

A "linear" design implies the use of some linear basis for reconstructing data along with a set of coefficients or weights. The reconstruction will be the matrix product of the weights and the basis, `w * Z`.

[`ADI.design`](@ref) will return `(basis, ceoffs)`, and you can also extract them via iteration, like `Z, w = design`.
"""
struct LinearDesign{BT,WT} <: ADIDesign
    basis::BT
    coeffs::WT
end

design(des::LinearDesign) = des.basis, des.coeffs
reconstruct(des::LinearDesign) = des.coeffs * des.basis
Base.iterate(des::LinearDesign, state=1) = iterate(design(des), state)

"""
    ADI.design(::ADIDesign)

Return the pertinent data required to form the PSF approximation. For example, weights and components for PCA/NMF or the median frame for classic ADI.
"""
function design end


text:
######################################################################
# TODO: Take methods from docs/jmd/extremumEstimation.jmd and put here
######################################################################

######################################################################

# EL inference

"""
    gel_pλ(model::GMMModel, h::Function=log)

Return a function that given parameters θ solves

`` max_p sum h(p) s.t. sum(p) = 1, sum(p*gi(θ)) = 0 ``

The returned function gives (p(θ),λ(θ))

The returned function is not thread-safe.
"""
function gel_pλ(model::GMMModel, h::Function=log)
  n = number_observations(model)
  d = number_parameters(model)
  k = number_moments(model)
  gi = get_gi(model)
  g = gi(zeros(d))
  p = Convex.Variable(n)
  el = sum(h.(p))
  problem = Convex.maximize(el, sum(p)==1, p>=0, g'*p==0)
  function(θ)
    g = gi(θ)
    problem.constraints[3] =  g'*p==0
    Convex.solve!(problem, ECOSSolver(verbose=false), #warmstart=false,
                  verbose=false)
    (p=p.value ,λ=problem.constraints[3].dual)
  end
end

"""
    gel_tests(θ, model::GMMModel, h::Function=log)

Computes GEL test statistics for H₀ : θ = θ₀. Returns a tuple
containing statistics and p-values.
"""
function gel_tests(θ, model::GMMModel, h::Function=log)
  d = number_parameters(model)
  k = number_moments(model)
  p, λ = gel_pλ(model, h)(θ)
  # Compared to Andrews & Guggenberger, this λ = -n*λ(AG)
  n = length(p)
  λ /= -n
  lr = -2*sum(h.(p) .- h(1/n))

  gi = get_gi(model)
  iΔ = inv(cov(gi(θ)))
  D = ForwardDiff.jacobian(x->gi(x)'*p, θ)
  S = (n*λ'*D*inv(D'*iΔ*D)*D'*λ)[1]
  return(GELR=lr, S=S, plr=cdf(Chisq(k),lr), ps=cdf(Chisq(d),S))
end


text:
using Multivectors, Combinatorics

for comb in combinations([3, 3x̂, 3x̂ŷ, 3ı̂])
    length(comb) < 2 && continue
    println(comb)
    println("+:\t", +(comb...))
    println("-:\t", -(comb...))
end

max_stars_repo_name: Samuele-Colombo/Multivectors

max_issues_repo_name: Samuele-Colombo/Multivectors

max_forks_repo_name: Samuele-Colombo/Multivectors


text:
@testset "Algorithm" begin
    #=
        min     0.4 x1 + 0.5 x2
        s.t.    0.3 x1 + 0.1 x2 = 2.7
                0.6 x1 + 0.4 x2 = 6.0
                3.0 <= x1 <= 10.0
                1.0 <= x2 <= 5.0
    =#
    A  = [0.3 0.1; 0.6 0.4]
    A  = sparse(A) # solver current only supports sparse matrices
    b  = [2.7, 6.0]
    c  = [0.4, 0.5]
    lo = [3.0, 1.0]
    hi = [10.0, Inf]
    lp = Problem{Float64}()
    load_problem!(lp, A, b, c, lo, hi)
    sln = solve(lp)
    @test norm(sln.x) ≈ norm([8.0, 3.0])
end


text:
# using Plots
using CairoMakie
using Trixi, OrdinaryDiffEq
using LinearAlgebra: diagm, norm, kron
using FFTW
using JLD2
using LaTeXStrings

# generate data and save to a JLD2 file
for (polydeg, cells_per_dimension) in ((3, 64), (7, 32))

    T_final = 25.0 # replace with very small final time if we're just loading JLD2 files
    tsave = LinRange(0, T_final, 2) # no need to save time history

    # Same as the default Trixi example, but slope = 25 and v2 is a non-symmetric perturbation
    function initial_condition_khi_nonsymmetric_perturbation(x, t, equations::CompressibleEulerEquations2D)
        # change discontinuity to tanh
        # typical resolution 128^2, 256^2
        # domain size is [-1,+1]^2
        slope = 15
        # discontinuous function with value 2 for -.5 <= x <= .5 and 0 elsewhere
        B = tanh(slope * x[2] + 7.5) - tanh(slope * x[2] - 7.5)
        rho = 0.5 + 0.75 * B
        v1 = 0.5 * (B - 1)
        # scale by (1 + .01 * sin(pi*x) * sin(pi*y)) to break symmetry
        v2 = 0.1 * sin(2 * pi * x[1]) * (1.0 + 1e-2 * sin(pi * x[1]) * sin(pi * x[2]))
        p = 1.0
        return prim2cons(SVector(rho, v1, v2, p), equations)
    end

    filename = "data/sol_Gauss_p$(polydeg)_$(cells_per_dimension)_cells.jld2"
    if isfile(filename)==false
        trixi_include("elixir_euler_khi_dgmulti.jl", approximation_type = GaussSBP(),
                    initial_condition = initial_condition_khi_nonsymmetric_perturbation,
                    tspan = (0, T_final), polydeg = polydeg, tsave = tsave,
                    cells_per_dimension = (cells_per_dimension))
        sol_Gauss = deepcopy(sol)
        @save filename sol_Gauss
    else
        # run for a very short time to load libraries
        trixi_include("elixir_euler_khi_dgmulti.jl", approximation_type = GaussSBP(),
                    initial_condition = initial_condition_khi_nonsymmetric_perturbation,
                    tspan = (0, 1e-2), polydeg = polydeg, tsave = tsave,
                    cells_per_dimension = (cells_per_dimension))
    end

    filename = "data/sol_DGSEM_SC_PP_p$(polydeg)_$(cells_per_dimension)_cells.jld2"
    if isfile(filename)==false
        trixi_include("elixir_euler_khi_dgsem_SC_PP.jl",
                    tspan = (0, T_final), polydeg = polydeg, tsave = tsave,
                    cells_per_dimension = (cells_per_dimension),
                    alpha_max=0.005,
                    initial_condition=initial_condition_khi_nonsymmetric_perturbation)
        sol_DGSEM_SC_PP = deepcopy(sol)
        @save filename sol_DGSEM_SC_PP
    else
        # run for a very short time to load libraries
        trixi_include("elixir_euler_khi_dgsem_SC_PP.jl",
        tspan = (0, 1e-2), polydeg = polydeg, tsave = tsave,
        cells_per_dimension = (cells_per_dimension),
        alpha_max=0.005,
        initial_condition=initial_condition_khi_nonsymmetric_perturbation)
    end
end

# ==================== plot solution heatmaps ====================

include("utilities.jl")
include("read_fluxo_utilities.jl")

for (polydeg, cells_per_dimension) in ((3, 64), (7, 32))

    @load "data/sol_Gauss_p$(polydeg)_$(cells_per_dimension)_cells.jld2" sol_Gauss
    @load "data/sol_DGSEM_SC_PP_p$(polydeg)_$(cells_per_dimension)_cells.jld2" sol_DGSEM_SC_PP

    # read in flux solution
    filename = "data/sol_ascii_N$(polydeg)_$(cells_per_dimension)elems.dat"
    polydeg, total_cells, x, y, rho, u, v, p = read_fluxo_sol(filename)

    # hack workaround - copy FLUXO's solution into Trixi's solution container
    # and use existing Trixi visualization routines.
    sol_DGSEM_subcell = deepcopy(sol_DGSEM_SC_PP)
    sol_u = Trixi.wrap_array(sol_DGSEM_subcell.u[end], sol_DGSEM_subcell.prob.p)
    cache = sol_DGSEM_subcell.prob.p.cache
    cache.elements.node_coordinates[1, :, :, :] .= copy(reshape(x, polydeg + 1, polydeg + 1, total_cells))
    cache.elements.node_coordinates[2, :, :, :] .= copy(reshape(y, polydeg + 1, polydeg + 1, total_cells))
    sol_u[1, :, :, :] .= reshape(rho, polydeg + 1, polydeg + 1, total_cells)
    sol_u[2, :, :, :] .= reshape(u, polydeg + 1, polydeg + 1, total_cells)
    sol_u[3, :, :, :] .= reshape(v, polydeg + 1, polydeg + 1, total_cells)
    sol_u[4, :, :, :] .= reshape(p, polydeg + 1, polydeg + 1, total_cells)

    with_theme(Theme(markersize = 5, fontsize=18)) do
        fig = Figure()
        rows, cols = 3, 2
        axes = [Makie.Axis(fig[i, j]) for j in 1:rows, i in 1:cols]
        row_list, col_list = [i for j in 1:rows, i in 1:cols], [j for j in 1:rows, i in 1:cols]

        limits = extrema(getindex.(sol_Gauss.u[end], 1))
        plot!(fig[1, 1], Trixi.PlotData2DTriangulated(sol_DGSEM_SC_PP)["rho"], plot_mesh=false, colorrange=limits)
        plot!(fig[1, 2], Trixi.PlotData2DTriangulated(sol_DGSEM_subcell)["rho"], plot_mesh=false, colorrange=limits)
        plot!(fig[1, 3], Trixi.PlotData2D(sol_Gauss)["rho"], plot_mesh=false, colorrange=limits)
        Label(fig[1, 1, Bottom()], LaTeXString("DGSEM-SC-PP density"), padding=(0, 0, 5, 5))
        Label(fig[1, 2, Bottom()], LaTeXString("DGSEM-subcell density"))
        Label(fig[1, 3, Bottom()], LaTeXString("Gauss density"))
        Colorbar(fig[1, 4], limits=limits, colormap=Trixi.default_Makie_colormap())
        rowsize!(fig.layout, 1, Aspect(2, 1))

        limits = extrema(getindex.(sol_Gauss.u[end], 4))
        plot!(fig[2, 1], Trixi.PlotData2DTriangulated(sol_DGSEM_SC_PP)["p"], plot_mesh=false, colorrange=limits)
        plot!(fig[2, 2], Trixi.PlotData2DTriangulated(sol_DGSEM_subcell)["p"], plot_mesh=false, colorrange=limits)
        plot!(fig[2, 3], Trixi.PlotData2D(sol_Gauss)["p"], plot_mesh=false, colorrange=limits)
        Label(fig[2, 1, Bottom()], LaTeXString("DGSEM-SC-PP pressure"), padding=(0, 0, 5, 5))
        Label(fig[2, 2, Bottom()], LaTeXString("DGSEM-subcell pressure"))
        Label(fig[2, 3, Bottom()], LaTeXString("Gauss pressure"))
        Colorbar(fig[2, 4], limits=limits, colormap=Trixi.default_Makie_colormap())
        rowsize!(fig.layout, 2, Aspect(2, 1))

        for ax in axes
            ax.aspect=DataAspect(); Makie.tightlimits!(ax); hidedecorations!(ax); hidespines!(ax)
        end
        fig

        save("khi_polydeg_$(polydeg)_$(cells_per_dimension)_cells.png", fig.scene, px_per_unit = 3)
    end
end

# # ==================== plot spectra ====================

# set_theme!()
# with_theme(Theme(markersize = 4, fontsize=22)) do
#     fig = Figure(resolution=(1500, 600))
#     rows, cols = 1, 2
#     axes = [Makie.Axis(fig[i, j], xscale=log10, yscale=log10) for i in 1:rows, j in 1:cols]

#     @load "data/sol_Gauss_p3_64_cells.jld2" sol_Gauss
#     @load "data/sol_DGSEM_SC_PP_p3_64_cells.jld2" sol_DGSEM_SC_PP
#     filename = "data/sol_ascii_N3_64elems.dat"
#     polydeg, total_cells, x, y, rho, u, v, p = read_fluxo_sol(filename)

#     Ek_Gauss, k1D = compute_2D_energy_spectrum(convert_to_Cartesian_arrays(sol_Gauss.u[end], sol_Gauss.prob.p)[1:4]...)
#     Ek_DGSEM_SC_PP, k1D = compute_2D_energy_spectrum(convert_to_Cartesian_arrays(sol_DGSEM_SC_PP.u[end], sol_DGSEM_SC_PP.prob.p)[1:4]...)
#     Ek_subcell, k1D = compute_2D_energy_spectrum(convert_to_Cartesian_arrays(polydeg, total_cells, x, y, rho, u, v, p)[1:4]...)
#     p = sortperm(k1D)

#     Makie.scatter!(axes[1, 1], k1D, Ek_Gauss, label="Gauss")
#     Makie.scatter!(axes[1, 1], k1D, Ek_subcell, label="DGSEM-Subcell")
#     Makie.scatter!(axes[1, 1], k1D, Ek_DGSEM_SC_PP, label="DGSEM-SC-PP")
#     Makie.lines!(axes[1, 1], k1D[p], 1.5e8*k1D[p] .^ (-7/3), label=L"k^{-7/3}", linestyle=:dash) #, title="Energy spectra, T = $(sol_Gauss.t[time_index])")
#     axislegend(axes[1, 1], position=:lb)
#     Makie.ylims!(axes[1, 1], (1e-5, 1e9))
#     Label(fig[1, 1, Bottom()], L"$N = 3$ mesh of $64^2$ elements", padding=(0, 0, 0, 50), textsize=32)

#     @load "data/sol_Gauss_p7_32_cells.jld2" sol_Gauss
#     @load "data/sol_DGSEM_SC_PP_p7_32_cells.jld2" sol_DGSEM_SC_PP
#     filename = "data/sol_ascii_N7_32elems.dat"
#     polydeg, total_cells, x, y, rho, u, v, p = read_fluxo_sol(filename)

#     Ek_Gauss, k1D = compute_2D_energy_spectrum(convert_to_Cartesian_arrays(sol_Gauss.u[end], sol_Gauss.prob.p)[1:4]...)
#     Ek_DGSEM_SC_PP, k1D = compute_2D_energy_spectrum(convert_to_Cartesian_arrays(sol_DGSEM_SC_PP.u[end], sol_DGSEM_SC_PP.prob.p)[1:4]...)
#     Ek_subcell, k1D = compute_2D_energy_spectrum(convert_to_Cartesian_arrays(polydeg, total_cells, x, y, rho, u, v, p)[1:4]...)
#     p = sortperm(k1D)

#     Makie.scatter!(axes[1, 2], k1D, Ek_Gauss, label="Gauss")
#     Makie.scatter!(axes[1, 2], k1D, Ek_subcell, label="DGSEM-Subcell")
#     Makie.scatter!(axes[1, 2], k1D, Ek_DGSEM_SC_PP, label="DGSEM-SC-PP")
#     Makie.lines!(axes[1, 2], k1D[p], 1.5e8*k1D[p] .^ (-7/3), label=L"k^{-7/3}", linestyle=:dash) #, title="Energy spectra, T = $(sol_Gauss.t[time_index])")
#     axislegend(axes[1, 2], position=:lb)
#     Makie.ylims!(axes[1, 2], (1e-5, 1e9))
#     # rowsize!(fig.layout, 1, Aspect(2, 1))
#     Label(fig[1, 2, Bottom()], L"$N = 7$ mesh of $32^2$ elements", padding=(0, 0, 0, 50), textsize=32)

#     save("spectra.png", fig.scene)
# end


text:
# Autogenerated wrapper script for Pixman_jll for x86_64-linux-gnu
export libpixman

JLLWrappers.@generate_wrapper_header("Pixman")
JLLWrappers.@declare_library_product(libpixman, "libpixman-1.so.0")
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_library_product(
        libpixman,
        "lib/libpixman-1.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
struct EmpiricalMean{T<:Real,V<:AbstractVector{<:Real},O} <: PriorMean{T}
    C::V
    opt::O
end

"""
    EmpiricalMean(c::AbstractVector{<:Real}=1.0;opt=ADAM(0.01))

## Arguments
- `c::AbstractVector` : Empirical mean vector

Construct a empirical mean with values `c`
Optionally give an optimiser `opt` (`ADAM(0.01)` by default)
"""
function EmpiricalMean(c::V; opt=ADAM(0.01)) where {V<:AbstractVector{<:Real}}
    return EmpiricalMean{eltype(c),V,typeof(opt)}(copy(c), opt)
end

function Base.show(io::IO, ::MIME"text/plain", μ₀::EmpiricalMean)
    return print(io, "Empirical Mean Prior (length(c) = ", length(μ₀.C), ")")
end

function (μ₀::EmpiricalMean{T})(x::AbstractVector) where {T<:Real}
    length(x) == length(μ₀.C) || error("Wrong dimension between x and μ₀")
    return μ₀.C
end

function init_priormean_state(hyperopt_state, μ₀::EmpiricalMean)
    μ₀_state = (; C=Optimisers.init(μ₀.opt, μ₀.C))
    return merge(hyperopt_state, (; μ₀_state))
end

function update!(μ₀::EmpiricalMean{T}, hyperopt_state, grad) where {T<:Real}
    μ₀_state = hyperopt_state.μ₀_state
    C, ΔC = Optimisers.apply(μ₀.opt, μ₀_state.C, μ₀.C, grad.C)
    μ₀.C .+= ΔC
    return merge(hyperopt_state, (; μ₀_state=(; C)))
end


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

struct VirtualMachineScaleSetsApi <: SwaggerApi
    client::Swagger.Client
end

"""

Create or update a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: parameters::VirtualMachineScaleSet (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSet
"""
function virtualMachineScaleSetsCreateOrUpdate(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PUT", VirtualMachineScaleSet, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Deallocates specific virtual machines in a VM scale set. Shuts down the virtual machines and releases the compute resources. You are not billed for the compute resources that this virtual machine scale set deallocates.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceIDs
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsDeallocate(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; vmInstanceIDs=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/deallocate", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Deletes a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsDelete(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "DELETE", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Deletes virtual machines in a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceRequiredIDs (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsDeleteInstances(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, vmInstanceIDs, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/delete", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Display information about a virtual machine scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSet
"""
function virtualMachineScaleSetsGet(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", VirtualMachineScaleSet, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Gets the status of a VM scale set instance.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSetInstanceView
"""
function virtualMachineScaleSetsGetInstanceView(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", VirtualMachineScaleSetInstanceView, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/instanceView", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Gets a list of all VM scale sets under a resource group.
Param: resourceGroupName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSetListResult
"""
function virtualMachineScaleSetsList(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", VirtualMachineScaleSetListResult, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Gets a list of all VM Scale Sets in the subscription, regardless of the associated resource group. Use nextLink property in the response to get the next page of VM Scale Sets. Do this till nextLink is not null to fetch all the VM Scale Sets.
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSetListWithLinkResult
"""
function virtualMachineScaleSetsListAll(_api::VirtualMachineScaleSetsApi, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", VirtualMachineScaleSetListWithLinkResult, "/subscriptions/{subscriptionId}/providers/Microsoft.Compute/virtualMachineScaleSets", ["azure_auth"])
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Gets a list of SKUs available for your VM scale set, including the minimum and maximum VM instances allowed for each SKU.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSetListSkusResult
"""
function virtualMachineScaleSetsListSkus(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", VirtualMachineScaleSetListSkusResult, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/skus", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Power off (stop) one or more virtual machines in a VM scale set. Note that resources are still attached and you are getting charged for the resources. Instead, use deallocate to release resources and avoid charges.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceIDs
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsPowerOff(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; vmInstanceIDs=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/poweroff", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Reimages (upgrade the operating system) one or more virtual machines in a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceIDs
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsReimage(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; vmInstanceIDs=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/reimage", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Reimages all the disks ( including data disks ) in the virtual machines in a VM scale set. This operation is only supported for managed disks.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceIDs
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsReimageAll(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; vmInstanceIDs=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/reimageall", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Restarts one or more virtual machines in a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceIDs
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsRestart(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; vmInstanceIDs=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/restart", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Starts one or more virtual machines in a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceIDs
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsStart(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, api_version::String, subscriptionId::String; vmInstanceIDs=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/start", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Update a VM scale set.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: parameters::VirtualMachineScaleSetUpdate (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: VirtualMachineScaleSet
"""
function virtualMachineScaleSetsUpdate(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PATCH", VirtualMachineScaleSet, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

Upgrades one or more virtual machines to the latest SKU set in the VM scale set model.
Param: resourceGroupName::String (required)
Param: vmScaleSetName::String (required)
Param: vmInstanceIDs::VirtualMachineScaleSetVMInstanceRequiredIDs (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: OperationStatusResponse
"""
function virtualMachineScaleSetsUpdateInstances(_api::VirtualMachineScaleSetsApi, resourceGroupName::String, vmScaleSetName::String, vmInstanceIDs, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", OperationStatusResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/manualupgrade", ["azure_auth"], vmInstanceIDs)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "vmScaleSetName", vmScaleSetName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    Swagger.exec(_ctx)
end

export virtualMachineScaleSetsCreateOrUpdate, virtualMachineScaleSetsDeallocate, virtualMachineScaleSetsDelete, virtualMachineScaleSetsDeleteInstances, virtualMachineScaleSetsGet, virtualMachineScaleSetsGetInstanceView, virtualMachineScaleSetsList, virtualMachineScaleSetsListAll, virtualMachineScaleSetsListSkus, virtualMachineScaleSetsPowerOff, virtualMachineScaleSetsReimage, virtualMachineScaleSetsReimageAll, virtualMachineScaleSetsRestart, virtualMachineScaleSetsStart, virtualMachineScaleSetsUpdate, virtualMachineScaleSetsUpdateInstances


text:
# utext.jl - Wrapper for ICU (International Components for Unicode) library

# Some content of the documentation strings was derived from the ICU header files utext.h
# (Those portions copyright (C) 1996-2015, International Business Machines Corporation and others)

"""
"""
module utext end

macro libtext(s)    ; _libicu(s, iculib,     "utext_")    ; end

mutable struct UText
    p::Ptr{Cvoid}
    s

    function UText(str::ByteStr)
        err = Ref{UErrorCode}(0)
        v = convert(UTF8Str, str)
        p = ccall(@libtext(openUTF8), Ptr{Cvoid},
                  (Ptr{Cvoid}, Ptr{UInt8}, Int64, Ptr{UErrorCode}),
                  C_NULL, v, sizeof(v), err)
        @assert SUCCESS(err[])
        # Retain pointer to data so that it won't be GCed
        self = new(p, v)
        finalizer(self, close)
        self
    end

    function UText(str::UTF16Str)
        err = Ref{UErrorCode}(0)
        p = ccall(@libtext(openUChars), Ptr{Cvoid},
                  (Ptr{Cvoid}, Ptr{UChar}, Int64, Ptr{UErrorCode}),
                  C_NULL, pointer(str), ncodeunits(str), err)
        @assert SUCCESS(err[])
        # Retain pointer to data so that it won't be GCed
        self = new(p, str)
        finalizer(self, close)
        self
    end
end

close(t::UText) =
    t.p == C_NULL ||
        (ccall(@libtext(close), Cvoid, (Ptr{Cvoid},), t.p) ; t.p = C_NULL ; t.s = Cvoid())


text:
    """
    `SpinAngular.completlyReducedCfpByIndices(aTerm::QspaceTerm, bTerm::QspaceTerm)`
        ... returns the (completely) reduced coefficient of fractional parentage (j alpha Q J||| a^(qj)||| j alpha' Q' J')
            for j = 1/2, 3/2, 5/2 and 7/2, and where alpha == Nr refers to some additional quantum number for j=9/2 subshell
            states. In all other cases Nr = Nrp = 0 is expected by this routine. A coeff::Float64 is returned
    """
    function completlyReducedCfpByIndices(aTerm::QspaceTerm, bTerm::QspaceTerm)
        #
        # Distinguish due to different j-values
        if       aTerm.j != bTerm.j               error("stop a")
        elseif   aTerm.j == AngularJ64(1//2)      wa = reducedCfp1half[aTerm.index, bTerm.index]
        elseif   aTerm.j == AngularJ64(3//2)      wa = reducedCfp3half[aTerm.index, bTerm.index]
        elseif   aTerm.j == AngularJ64(5//2)      wa = reducedCfp5half[aTerm.index, bTerm.index]
        elseif   aTerm.j == AngularJ64(7//2)      wa = reducedCfp7half[aTerm.index, bTerm.index]
        elseif   aTerm.j == AngularJ64(9//2)      wa = reducedCfp9half[aTerm.index, bTerm.index]
        else     error("stop b")
        end
        @show wa

        wb = wa[1] * sqrt(wa[2]/wa[3])
        return( wb )
    end

    """
    `SpinAngular.completlyReducedCfp(j::AngularJ64, Nr::Int64, Q::AngularJ64, J::AngularJ64, Nrp::Int64, Qp::AngularJ64, Jp::AngularJ64)`
        ... returns the (completely) reduced coefficient of fractional parentage (j alpha Q J||| a^(qj)||| j alpha' Q' J')
            for j = 1/2, 3/2, 5/2 and 7/2, and where alpha == Nr refers to some additional quantum number for j=9/2 subshell
            states. In all other cases Nr = Nrp = 0 is expected by this routine. A coeff::Float64 is returned
    """
    function completlyReducedCfp(j::AngularJ64, Nr::Int64, Q::AngularJ64, J::AngularJ64, Nrp::Int64, Qp::AngularJ64, Jp::AngularJ64)
        #
        # Define a tuple for comparison in the subsequent lists; apply symmetries to reduced the list of coefficients
        println("!!! Here, apply symmetries to reduced the list of coefficients !!!")
        wc = (Nr, twice(Q), twice(J), Nrp, twice(Qp), twice(Jp))
        #
        # Distinguish due to different j-values
        if       j == AngularJ64(1//2)
            if      wc == (0, 0, 0,  0, 0, 0)        wa = ( 0,     0,     0)             # !! Please, check
            end
        elseif   j == AngularJ64(3//2)
            if      wc == (0, 2, 0,  0, 1, 3)        wa = (-1,    12,     1)
            elseif  wc == (0, 0, 4,  0, 1, 3)        wa = ( 1,    20,     1)
            end
        elseif   j == AngularJ64(5//2)
            if      wc == (0, 2, 5,  0, 3, 0)        wa = ( 0,     0,     0)
            elseif  wc == (0, 2, 5,  0, 1, 4)        wa = ( 0,     0,     0)
            elseif  wc == (0, 2, 5,  0, 1, 8)        wa = ( 0,     0,     0)
            elseif  wc == (0, 0, 3,  0, 1, 4)        wa = ( 0,     0,     0)
            elseif  wc == (0, 0, 3,  0, 1, 8)        wa = ( 0,     0,     0)
            elseif  wc == (0, 0, 9,  0, 1, 4)        wa = ( 0,     0,     0)
            elseif  wc == (0, 0, 9,  0, 1, 8)        wa = ( 0,     0,     0)
            end
        ##  elseif   j == AngularJ64(5//2)  For j = 5/2, 7/2 and 9/2, we should generate the corresponding code from running
        ##  elseif   j == AngularJ64(7//2)  the routine of ANCO and printing the results in a proper format, I guess
        ##  elseif   j == AngularJ64(9//2)
        else     error("stop a")
        end

        wb = wa[1] * sqrt(wa[2]/wa[3])
        return( wb )
    end

    """
    `SpinAngular.completelyReducedWkk(kq::Int64, kj::Int64, j::AngularJ64,
                                      Nr::Int64, Q::AngularJ64, J::AngularJ64, Nrp::Int64, Qp::AngularJ64, Jp::AngularJ64)`
        ... returns the (completely) reduced coefficient of fractional parentage (j alpha Q J||| a^(qj)||| j alpha' Q' J')
            for j = 1/2, 3/2, 5/2 and 7/2, and where alpha == Nr refers to some additional quantum number for j=9/2 subshell
            states. In all other cases Nr = Nrp = 0 is expected by this routine. A coeff::Float64 is returned
    """
    function completelyReducedWkk(kq::Int64, kj::Int64, j::AngularJ64,
                                  Nr::Int64, Q::AngularJ64, J::AngularJ64, Nrp::Int64, Qp::AngularJ64, Jp::AngularJ64)
        #
        # Define a tuple for comparison in the subsequent lists; apply symmetries to reduced the list of coefficients
        println("!!! Here, apply symmetries to reduced the list of coefficients !!!")
        wc = (Nr, twice(Q), twice(J), Nrp, twice(Qp), twice(Jp))
        #
        # Distinguish due to different j-values
        if       j == AngularJ64(1//2)   &&     (kq,kj) == (0,0)
            if      wc == (0, 0, 0,  0, 0, 0)           wa = ( 0,     0,     0)             # !! Please, check
            end
        elseif   j == AngularJ64(3//2)   &&     (kq,kj) == (0,0)
            if      wc == (0, 2, 0,  0, 1, 3)           wa = (-1,    12,     1)
            elseif  wc == (0, 0, 4,  0, 1, 3)           wa = ( 1,    20,     1)
            end
        elseif   j == AngularJ64(3//2)   &&     (kq,kj) == (1,0)
            if      wc == (0, 2, 0,  0, 1, 3)           wa = (-1,    12,     1)
            elseif  wc == (0, 0, 4,  0, 1, 3)           wa = ( 1,    20,     1)
            end
        ##  elseif   j == AngularJ64(5//2)  For j = 3/2, 5/2, 7/2 and 9/2, again, we should generate the corresponding code from running
        ##  elseif   j == AngularJ64(7//2)  the routine of ANCO and printing the results in a proper format, I guess
        ##  elseif   j == AngularJ64(9//2)
        else     error("stop a")
        end

        wb = wa[1] * sqrt(wa[2]/wa[3])
        return( wb )
    end

    """
    `SpinAngular.qspaceTerms(j::AngularJ64)`
        ... returns a list of Q-space terms for the given j; a termList::Array{SpinAngular.QspaceTerm,1} is returned.
    """
    function  qspaceTerms(j::AngularJ64)
        # First define some short-cuts to simplify the subsequent input
        h0  = AngularJ64(0//2);   h1  = AngularJ64(1//2);   h2  = AngularJ64(2//2);   h3  = AngularJ64(3//2);   h4  = AngularJ64(4//2)
        h5  = AngularJ64(5//2);   h6  = AngularJ64(6//2);   h7  = AngularJ64(7//2);   h8  = AngularJ64(8//2);   h9  = AngularJ64(9//2)
        h10 = AngularJ64(10//2);  h11 = AngularJ64(11//2);  h12 = AngularJ64(12//2);  h13 = AngularJ64(13//2);  h14 = AngularJ64(14//2)
        h15 = AngularJ64(15//2);  h16 = AngularJ64(16//2);  h17 = AngularJ64(17//2);  h18 = AngularJ64(18//2);  h19 = AngularJ64(19//2)
        h20 = AngularJ64(20//2);  h21 = AngularJ64(21//2);  h22 = AngularJ64(22//2);  h23 = AngularJ64(23//2);  h24 = AngularJ64(24//2)
        h25 = AngularJ64(25//2);  h26 = AngularJ64(26//2);  h27 = AngularJ64(27//2)
        #
       # Distinguish due to different j-values
        if       j == AngularJ64(1//2)
            wa = [ QspaceTerm(h1, h0, h1, 0,  1), QspaceTerm(h1, h1, h0, 0,  2) ]
            #
        elseif   j == AngularJ64(3//2)
            wa = [ QspaceTerm(h3, h1, h3, 0,  1), QspaceTerm(h3, h2, h0, 0,  2), QspaceTerm(h3, h0, h4, 0,  3) ]
            #
        elseif   j == AngularJ64(5//2)
            wa = [ QspaceTerm(h5, h2, h5, 0,  1), QspaceTerm(h5, h0, h3, 0,  2), QspaceTerm(h5, h0, h9, 0,  3),
                   QspaceTerm(h5, h3, h0, 0,  4), QspaceTerm(h5, h1, h4, 0,  5), QspaceTerm(h5, h1, h8, 0,  6) ]
            #
        elseif   j == AngularJ64(7//2)
            wa = [ QspaceTerm(h7, h1, h3, 0,  1), QspaceTerm(h7, h1, h5, 0,  2), QspaceTerm(h7, h3, h7, 0,  3),
                   QspaceTerm(h7, h1, h9, 0,  4), QspaceTerm(h7, h1,h11, 0,  5), QspaceTerm(h7, h1,h15, 0,  6),
                   QspaceTerm(h7, h4, h0, 0,  7), QspaceTerm(h7, h2, h4, 0,  8), QspaceTerm(h7, h0, h8, 0,  9),
                   QspaceTerm(h7, h0, h4, 0, 10), QspaceTerm(h7, h2, h8, 0, 11), QspaceTerm(h7, h0,h10, 0, 12),
                   QspaceTerm(h7, h2,h12, 0, 13), QspaceTerm(h7, h0,h16, 0, 14) ]
            #
        elseif   j == AngularJ64(9//2)
            wa = [ QspaceTerm(h9, h0, h1, 0,  1), QspaceTerm(h9, h2, h3, 0,  2), QspaceTerm(h9, h2, h5, 0,  3),
                   QspaceTerm(h9, h0, h5, 0,  4), QspaceTerm(h9, h2, h7, 0,  5), QspaceTerm(h9, h0, h7, 0,  6),
                   QspaceTerm(h9, h4, h9, 0,  7), QspaceTerm(h9, h2, h9, 0,  8), QspaceTerm(h9, h0, h9, 0,  9),
                   QspaceTerm(h9, h2,h11, 0, 10), QspaceTerm(h9, h0,h11, 0, 11), QspaceTerm(h9, h2,h13, 0, 12),
                   QspaceTerm(h9, h0,h13, 0, 13), QspaceTerm(h9, h2,h15, 0, 14), QspaceTerm(h9, h0,h15, 0, 15),
                   QspaceTerm(h9, h2,h17, 0, 16), QspaceTerm(h9, h0,h17, 0, 17), QspaceTerm(h9, h0,h19, 0, 18),
                   QspaceTerm(h9, h2,h21, 0, 19), QspaceTerm(h9, h0,h25, 0, 20), QspaceTerm(h9, h5, h0, 0, 21),
                   QspaceTerm(h9, h1, h0, 0, 22), QspaceTerm(h9, h3, h4, 0, 23), QspaceTerm(h9, h1, h4, 0, 24),
                   QspaceTerm(h9, h1, h6, 0, 25), QspaceTerm(h9, h3, h8, 0, 26), QspaceTerm(h9, h1, h8, 1, 27),
                   QspaceTerm(h9, h1, h8, 2, 28), QspaceTerm(h9, h1,h10, 0, 29), QspaceTerm(h9, h3,h12, 0, 30),
                   QspaceTerm(h9, h1,h12, 1, 31), QspaceTerm(h9, h1,h12, 2, 32), QspaceTerm(h9, h1,h14, 0, 33),
                   QspaceTerm(h9, h3,h16, 0, 34), QspaceTerm(h9, h1,h16, 0, 35), QspaceTerm(h9, h1,h18, 0, 36),
                   QspaceTerm(h9, h1,h20, 0, 37), QspaceTerm(h9, h1,h24, 0, 38) ]
        else     error("stop a")
        end

        return( wa )
    end

    """
    `SpinAngular.subshellTerms(j::AngularJ64, N::Int64)`
        ... returns a list of subshell terms for the given j^N; a termList::Array{SpinAngular.SubshellTerm,1} is returned.
    """
    function  subshellTerms(j::AngularJ64, N::Int64)
        # First define some short-cuts to simplify the subsequent input
        h0  = AngularJ64(0//2);   h1  = AngularJ64(1//2);   h2  = AngularJ64(2//2);   h3  = AngularJ64(3//2);   h4  = AngularJ64(4//2)
        h5  = AngularJ64(5//2);   h6  = AngularJ64(6//2);   h7  = AngularJ64(7//2);   h8  = AngularJ64(8//2);   h9  = AngularJ64(9//2)
        h10 = AngularJ64(10//2);  h11 = AngularJ64(11//2);  h12 = AngularJ64(12//2);  h13 = AngularJ64(13//2);  h14 = AngularJ64(14//2)
        h15 = AngularJ64(15//2);  h16 = AngularJ64(16//2);  h17 = AngularJ64(17//2);  h18 = AngularJ64(18//2);  h19 = AngularJ64(19//2)
        h20 = AngularJ64(20//2);  h21 = AngularJ64(21//2);  h22 = AngularJ64(22//2);  h23 = AngularJ64(23//2);  h24 = AngularJ64(24//2)
        h25 = AngularJ64(25//2);  h26 = AngularJ64(26//2);  h27 = AngularJ64(27//2)
        #
        # Distinguish due to different j-values
        if       j == AngularJ64(1//2)        &&  N in [0, 2]
            wa = [ SubshellTerm(h1, h1, N, 0, h0, 0) ]
        elseif   j == AngularJ64(1//2)        &&  N in [1]
            wa = [ SubshellTerm(h1, h0, N, 1, h1, 0) ]
            #
            #
        elseif   j == AngularJ64(3//2)        &&  N in [0, 4]
            wa = [ SubshellTerm(h3, h2, N, 0, h0, 0) ]
        elseif   j == AngularJ64(3//2)        &&  N in [1, 3]
            wa = [ SubshellTerm(h3, h1, N, 1, h3, 0) ]
        elseif   j == AngularJ64(3//2)        &&  N in [2]
            wa = [ SubshellTerm(h3, h2, N, 0, h0, 0), SubshellTerm(h3, h0, N, 2, h4, 0) ]
            #
            #
        elseif   j == AngularJ64(5//2)        &&  N in [0, 6]
            wa = [ SubshellTerm(h5, h3, N, 0, h0, 0) ]
        elseif   j == AngularJ64(5//2)        &&  N in [1, 5]
            wa = [ SubshellTerm(h5, h2, N, 1, h5, 0) ]
        elseif   j == AngularJ64(5//2)        &&  N in [2, 4]
            wa = [ SubshellTerm(h5, h3, N, 0, h0, 0), SubshellTerm(h5, h1, N, 2, h4, 0), SubshellTerm(h5, h1, N, 2, h8, 0) ]
        elseif   j == AngularJ64(5//2)        &&  N in [3]
            wa = [ SubshellTerm(h5, h2, N, 1, h5, 0), SubshellTerm(h5, h0, N, 3, h3, 0), SubshellTerm(h5, h0, N, 3, h9, 0) ]
            #
            #
        elseif   j == AngularJ64(7//2)        &&  N in [0, 8]
            wa = [ SubshellTerm(h7, h4, N, 0, h0, 0) ]
        elseif   j == AngularJ64(7//2)        &&  N in [1, 7]
            wa = [ SubshellTerm(h7, h3, N, 1, h7, 0) ]
        elseif   j == AngularJ64(7//2)        &&  N in [2, 6]
            wa = [ SubshellTerm(h7, h3, N, 1, h7, 0), SubshellTerm(h7, h2, N, 2, h4, 0), SubshellTerm(h7, h2, N, 2, h8, 0),
                   SubshellTerm(h7, h2, N, 2,h12, 0) ]
        elseif   j == AngularJ64(7//2)        &&  N in [3, 5]
            wa = [ SubshellTerm(h7, h3, N, 1, h7, 0), SubshellTerm(h7, h1, N, 3, h3, 0), SubshellTerm(h7, h1, N, 3, h5, 0),
                   SubshellTerm(h7, h3, N, 1, h9, 0), SubshellTerm(h7, h1, N, 3,h11, 0), SubshellTerm(h7, h1, N, 3,h15, 0) ]
        elseif   j == AngularJ64(7//2)        &&  N in [4]
            wa = [ SubshellTerm(h7, h4, N, 0, h0, 0), SubshellTerm(h7, h2, N, 2, h4, 0), SubshellTerm(h7, h0, N, 4, h8, 0),
                   SubshellTerm(h7, h2, N, 2,h12, 0), SubshellTerm(h7, h0, N, 4, h4, 0), SubshellTerm(h7, h0, N, 4, h8, 0),
                   SubshellTerm(h7, h0, N, 4,h10, 0), SubshellTerm(h7, h0, N, 4,h16, 0) ]
            #
            #
        elseif   j == AngularJ64(9//2)        &&  N in [0, 10]
            wa = [ SubshellTerm(h9, h5, N, 0, h0, 0) ]
        elseif   j == AngularJ64(9//2)        &&  N in [1, 9]
            wa = [ SubshellTerm(h9, h4, N, 1, h9, 0) ]
        elseif   j == AngularJ64(9//2)        &&  N in [2, 8]
            wa = [ SubshellTerm(h9, h5, N, 0, h0, 0), SubshellTerm(h9, h3, N, 2, h4, 0), SubshellTerm(h9, h3, N, 2, h8, 0),
                   SubshellTerm(h9, h3, N, 2,h12, 0), SubshellTerm(h9, h3, N, 2,h16, 0) ]
        elseif   j == AngularJ64(9//2)        &&  N in [3, 7]
            wa = [ SubshellTerm(h9, h4, 1, N, h9, 0), SubshellTerm(h9, h2, N, 3, h3, 0), SubshellTerm(h9, h2, N, 3, h5, 0),
                   SubshellTerm(h9, h2, N, 3, h7, 0), SubshellTerm(h9, h2, N, 3, h9, 0), SubshellTerm(h9, h2, 3, N,h11, 0),
                   SubshellTerm(h9, h2, N, 3,h13, 0), SubshellTerm(h9, h2, N, 3,h15, 0), SubshellTerm(h9, h2, 3, N,h17, 0),
                   SubshellTerm(h9, h2, 3, N,h21, 0) ]
        elseif   j == AngularJ64(9//2)        &&  N in [4, 6]
            wa = [ SubshellTerm(h9, h5, N, 0, h0, 0), SubshellTerm(h9, h3, N, 2, h4, 0), SubshellTerm(h9, h3, N, 2, h8, 0),
                   SubshellTerm(h9, h3, N, 2,h12, 0), SubshellTerm(h9, h3, N, 2,h16, 0), SubshellTerm(h9, h1, 4, N, h0, 0),
                   SubshellTerm(h9, h1, N, 4, h4, 0), SubshellTerm(h9, h1, 4, N, h6, 0), SubshellTerm(h9, h1, N, 4, h8, 1),
                   SubshellTerm(h9, h1, 4, N, h8, 2), SubshellTerm(h9, h1, N, 4,h10, 0), SubshellTerm(h9, h1, 4, N,h12, 1),
                   SubshellTerm(h9, h1, N, 4,h12, 2), SubshellTerm(h9, h1, N, 4,h14, 0), SubshellTerm(h9, h1, N, 4,h16, 0),
                   SubshellTerm(h9, h1, N, 4,h18, 0), SubshellTerm(h9, h1, 4, N,h20, 0), SubshellTerm(h9, h1, N, 4,h24, 0) ]
        elseif   j == AngularJ64(9//2)        &&  N in [5]
            wa = [ SubshellTerm(h9, h4, 1, N, h9, 0), SubshellTerm(h9, h2, N, 3, h3, 0), SubshellTerm(h9, h2, N, 3, h5, 0),
                   SubshellTerm(h9, h2, N, 3, h7, 0), SubshellTerm(h9, h2, N, 3, h9, 0), SubshellTerm(h9, h2, 3, N,h11, 0),
                   SubshellTerm(h9, h2, N, 3,h13, 0), SubshellTerm(h9, h2, N, 3,h15, 0), SubshellTerm(h9, h2, 3, N,h17, 0),
                   SubshellTerm(h9, h2, 3, N,h21, 0), SubshellTerm(h9, h0, 5, N, h1, 0), SubshellTerm(h9, h0, 5, N, h5, 0),
                   SubshellTerm(h9, h0, N, 5, h7, 0), SubshellTerm(h9, h0, N, 5, h9, 0), SubshellTerm(h9, h0, N, 5,h11, 0),
                   SubshellTerm(h9, h0, 5, N,h13, 0), SubshellTerm(h9, h0, N, 5,h15, 0), SubshellTerm(h9, h0, N, 5,h17, 0),
                   SubshellTerm(h9, h0, N, 5,h19, 0), SubshellTerm(h9, h0, N, 5,h25, 0) ]
        else     error("stop a")
        end

        return(wa)
    end

    #######################################################################################################################
    #######################################################################################################################

    reducedCfp1half = Matrix{Tuple{Int64,Int64,Int64}}(undef,2,2);    for i=1:2  for j=1:2  reducedCfp1half[i,j] = (0,0,0)   end  end
    reducedCfp3half = Matrix{Tuple{Int64,Int64,Int64}}(undef,3,3);    for i=1:3  for j=1:3  reducedCfp3half[i,j] = (0,0,0)   end  end
    reducedCfp5half = Matrix{Tuple{Int64,Int64,Int64}}(undef,6,6);    for i=1:6  for j=1:6  reducedCfp5half[i,j] = (0,0,0)   end  end
    reducedCfp7half = Matrix{Tuple{Int64,Int64,Int64}}(undef,14,14);  for i=1:14 for j=1:14 reducedCfp7half[i,j] = (0,0,0)   end  end

    reducedCfp1half[1,1] = (-1,      4,     1)
    reducedCfp1half[1,2] = (-1,      4,     1)

    reducedCfp3half[1,1] = (-1,      4,     1);     reducedCfp3half[1,2] = (-1,      4,     1)

    W_00_one_half = Matrix{Tuple{Int64,Int64,Int64}}(undef,2,2);       for i=1:2  for j=1:2  W_00_one_half[i,j] = (0,0,0)   end  end


text:
dfg = LightDFG{NoSolverParams, VARTYPE, FACTYPE}()
DistributedFactorGraphs.DFGVariableSummary(label::Symbol) = DFGVariableSummary(label, DistributedFactorGraphs.now(), Symbol[], Dict{Symbol, MeanMaxPPE}(), :NA, 0)
DistributedFactorGraphs.DFGFactorSummary(label::Symbol) = DFGFactorSummary(label, Symbol[], 0, Symbol[])

v1 = VARTYPE(:a)
v2 = VARTYPE(:b)
f1 = FACTYPE(:f1)

#add tags for filters
append!(v1.tags, [:VARIABLE, :POSE])
append!(v2.tags, [:VARIABLE, :LANDMARK])
append!(f1.tags, [:FACTOR])

#Force softtypename
isa(v1, DFGVariableSummary) && (v1.softtypename = :Pose2)

# @testset "Creating Graphs" begin
global dfg,v1,v2,f1
addVariable!(dfg, v1)
addVariable!(dfg, v2)
addFactor!(dfg, [v1, v2], f1)
@test_throws Exception addFactor!(dfg, FACTYPE("f2"), [v1, VARTYPE("Nope")])
# end

@testset "Adding Removing Nodes" begin
    dfg2 = LightDFG{NoSolverParams, VARTYPE, FACTYPE}()
    v1 = VARTYPE(:a)
    v2 = VARTYPE(:b)
    v3 = VARTYPE(:c)
    f1 = FACTYPE(:f1)
    f2 = FACTYPE(:f2)
    # @testset "Creating Graphs" begin
    @test addVariable!(dfg2, v1)
    @test addVariable!(dfg2, v2)
    @test_throws ErrorException updateVariable!(dfg2, v3)
    @test addVariable!(dfg2, v3)
    @test_throws ErrorException addVariable!(dfg2, v3)
    @test addFactor!(dfg2, [v1, v2], f1)
    @test_throws ErrorException addFactor!(dfg2, [v1, v2], f1)
    @test_throws ErrorException updateFactor!(dfg2, f2)
    @test addFactor!(dfg2, [:b, :c], f2)
    @test deleteVariable!(dfg2, v3) == v3
    @test symdiff(ls(dfg2),[:a,:b]) == []
    @test deleteFactor!(dfg2, f2) == f2
    @test lsf(dfg2) == [:f1]
end

@testset "Listing Nodes" begin
    global dfg,v1,v2,f1
    @test length(ls(dfg)) == 2
    @test length(lsf(dfg)) == 1
    @test symdiff([:a, :b], getVariableIds(dfg)) == []
    @test getFactorIds(dfg) == [:f1]
    #
    @test lsf(dfg, :a) == [f1.label]
    # Tags
    @test ls(dfg, tags=[:POSE]) == [:a]
    @test symdiff(ls(dfg, tags=[:POSE, :LANDMARK]), ls(dfg, tags=[:VARIABLE])) == []
    # Regexes
    @test ls(dfg, r"a") == [v1.label]
    @test lsf(dfg, r"f*") == [f1.label]
    # Accessors
    @test getAddHistory(dfg) == [:a, :b] #, :f1
    @test getDescription(dfg) != nothing
    @test getLabelDict(dfg) != nothing
    # Existence
    @test exists(dfg, :a) == true
    @test exists(dfg, v1) == true
    @test exists(dfg, :nope) == false
    # Sorting of results
    # TODO - this function needs to be cleaned up
    unsorted = [:x1_3;:x1_6;:l1;:april1] #this will not work for :x1x2f1
    @test sortDFG(unsorted) == sortVarNested(unsorted)
    @test_skip sortDFG([:x1x2f1, :x1l1f1]) == [:x1l1f1, :x1x2f1]
end

# Gets
@testset "Gets, Sets, and Accessors" begin
    global dfg,v1,v2,f1

    @test getVariable(dfg, v1.label) == v1
    @test getFactor(dfg, f1.label) == f1
    @test_throws Exception getVariable(dfg, :nope)
    @test_throws Exception getVariable(dfg, "nope")
    @test_throws Exception getFactor(dfg, :nope)
    @test_throws Exception getFactor(dfg, "nope")

    # Sets
    v1Prime = deepcopy(v1)
    @test updateVariable!(dfg, v1Prime) == v1
    f1Prime = deepcopy(f1)
    @test updateFactor!(dfg, f1Prime) == f1

    # Accessors
    @test label(v1) == v1.label
    @test tags(v1) == v1.tags

    if VARTYPE == DFGVariableSummary
        @test timestamp(v1) == v1.timestamp
        @test estimates(v1) == v1.estimateDict
        @test estimate(v1, :notfound) == nothing
        @test softtype(v1) == :Pose2
        @test internalId(v1) == v1._internalId
    end
    # @test solverData(v1) === v1.solverDataDict[:default]
    # @test getData(v1) === v1.solverDataDict[:default]
    # @test solverData(v1, :default) === v1.solverDataDict[:default]
    # @test solverDataDict(v1) == v1.solverDataDict

    @test label(f1) == f1.label
    @test tags(f1) == f1.tags
    # @test solverData(f1) == f1.data
    # Deprecated functions
    # @test data(f1) == f1.data
    # @test getData(f1) == f1.data
    # Internal function
    if FACTYPE == DFGFactorSummary
        @test internalId(f1) == f1._internalId
    end

end

@testset "Updating Nodes" begin
    if VARTYPE == DFGVariableSummary
            global dfg
        #get the variable
        var = getVariable(dfg, :a)
        #make a copy and simulate external changes
        newvar = deepcopy(var)
        estimates(newvar)[:default] = MeanMaxPPE(:default, [150.0], [100.0], [50.0])
        #update
        mergeUpdateVariableSolverData!(dfg, newvar)
        #For now spot check
        # @test solverDataDict(newvar) == solverDataDict(var)
        @test estimates(newvar) == estimates(var)

        # Delete :default and replace to see if new ones can be added
        delete!(estimates(newvar), :default)
        estimates(newvar)[:second] = MeanMaxPPE(:second, [15.0], [10.0], [5.0])

        # Persist to the original variable.
        mergeUpdateVariableSolverData!(dfg, newvar)
        # At this point newvar will have only :second, and var should have both (it is the reference)
        @test symdiff(collect(keys(estimates(var))), [:default, :second]) == Symbol[]
        @test symdiff(collect(keys(estimates(newvar))), [:second]) == Symbol[]
        # Get the source too.
        @test symdiff(collect(keys(estimates(getVariable(dfg, :a)))), [:default, :second]) == Symbol[]
    end
end

# Connectivity test
@testset "Connectivity Test" begin
    global dfg,v1,v2,f1
    @test isFullyConnected(dfg) == true
    @test hasOrphans(dfg) == false
    addVariable!(dfg, VARTYPE(:orphan))
    @test isFullyConnected(dfg) == false
    @test hasOrphans(dfg) == true
end

# Adjacency matrices
@testset "Adjacency Matrices" begin
    global dfg,v1,v2,f1
    # Normal
    adjMat = getAdjacencyMatrix(dfg)
    @test size(adjMat) == (2,4)
    @test symdiff(adjMat[1, :], [nothing, :a, :b, :orphan]) == Symbol[]
    @test symdiff(adjMat[2, :], [:f1, :f1, :f1, nothing]) == Symbol[]
    #sparse
    adjMat, v_ll, f_ll = getAdjacencyMatrixSparse(dfg)
    @test size(adjMat) == (1,3)

    # Checking the elements of adjacency, its not sorted so need indexing function
    indexOf = (arr, el1) -> findfirst(el2->el2==el1, arr)
    @test adjMat[1, indexOf(v_ll, :orphan)] == 0
    @test adjMat[1, indexOf(v_ll, :a)] == 1
    @test adjMat[1, indexOf(v_ll, :b)] == 1
    @test symdiff(v_ll, [:a, :b, :orphan]) == Symbol[]
    @test symdiff(f_ll, [:f1, :f1, :f1]) == Symbol[]
end

# Deletions
@testset "Deletions" begin
    deleteFactor!(dfg, :f1)
    @test getFactorIds(dfg) == []
    deleteVariable!(dfg, :b)
    @test symdiff([:a, :orphan], getVariableIds(dfg)) == []
    #delete last also for the LightGraphs implementation coverage
    deleteVariable!(dfg, :orphan)
    @test symdiff([:a], getVariableIds(dfg)) == []
    deleteVariable!(dfg, :a)
    @test getVariableIds(dfg) == []
end

# Now make a complex graph for connectivity tests
numNodes = 10
dfg = LightDFG{NoSolverParams, VARTYPE, FACTYPE}()
verts = map(n -> VARTYPE(Symbol("x$n")), 1:numNodes)
#change ready and backendset for x7,x8 for improved tests on x7x8f1
# verts[7].ready = 1
# verts[8].backendset = 1
map(v -> addVariable!(dfg, v), verts)
map(n -> addFactor!(dfg, [verts[n], verts[n+1]], FACTYPE(Symbol("x$(n)x$(n+1)f1"))), 1:(numNodes-1))

@testset "Getting Neighbors" begin
    global dfg,verts
    # Trivial test to validate that intersect([], []) returns order of first parameter
    @test intersect([:x3, :x2, :x1], [:x1, :x2]) == [:x2, :x1]
    # Get neighbors tests
    @test getNeighbors(dfg, verts[1]) == [:x1x2f1]
    neighbors = getNeighbors(dfg, getFactor(dfg, :x1x2f1))
    @test neighbors == [:x1, :x2]
    # Testing aliases
    @test getNeighbors(dfg, getFactor(dfg, :x1x2f1)) == ls(dfg, getFactor(dfg, :x1x2f1))
    @test getNeighbors(dfg, :x1x2f1) == ls(dfg, :x1x2f1)

end

@testset "Getting Subgraphs" begin
    # Subgraphs
    dfgSubgraph = getSubgraphAroundNode(dfg, verts[1], 2)
    # Only returns x1 and x2
    @test symdiff([:x1, :x1x2f1, :x2], [ls(dfgSubgraph)..., lsf(dfgSubgraph)...]) == []
    # Test include orphan factorsVoid
    dfgSubgraph = getSubgraphAroundNode(dfg, verts[1], 1, true)
    @test symdiff([:x1, :x1x2f1], [ls(dfgSubgraph)..., lsf(dfgSubgraph)...]) == []
    # Test adding to the dfg
    dfgSubgraph = getSubgraphAroundNode(dfg, verts[1], 2, true, dfgSubgraph)
    @test symdiff([:x1, :x1x2f1, :x2], [ls(dfgSubgraph)..., lsf(dfgSubgraph)...]) == []
    #
    dfgSubgraph = getSubgraph(dfg,[:x1, :x2, :x1x2f1])
    # Only returns x1 and x2
    @test symdiff([:x1, :x1x2f1, :x2], [ls(dfgSubgraph)..., lsf(dfgSubgraph)...]) == []

    # DFG issue #95 - confirming that getSubgraphAroundNode retains order
    # REF: https://github.com/JuliaRobotics/DistributedFactorGraphs.jl/issues/95
    for fId in getVariableIds(dfg)
        # Get a subgraph of this and it's related factors+variables
        dfgSubgraph = getSubgraphAroundNode(dfg, verts[1], 2)
        # For each factor check that the order the copied graph == original
        for fact in getFactors(dfgSubgraph)
            @test fact._variableOrderSymbols == getFactor(dfg, fact.label)._variableOrderSymbols
        end
    end
end

@testset "Summaries and Summary Graphs" begin
    if VARTYPE == DFGVariableSummary
        factorFields = fieldnames(FACTYPE)
        variableFields = fieldnames(VARTYPE)

        summary = getSummary(dfg)
        @test symdiff(collect(keys(summary.variables)), ls(dfg)) == Symbol[]
        @test symdiff(collect(keys(summary.factors)), lsf(dfg)) == Symbol[]

        summaryGraph = getSummaryGraph(dfg)
        @test symdiff(ls(summaryGraph), ls(dfg)) == Symbol[]
        @test symdiff(lsf(summaryGraph), lsf(dfg)) == Symbol[]
        # Check all fields are equal for all variables
        for v in ls(summaryGraph)
            for field in variableFields
                @test getfield(getVariable(dfg, v), field) == getfield(getVariable(summaryGraph, v), field)
            end
        end
        for f in lsf(summaryGraph)
            for field in factorFields
                @test getfield(getFactor(dfg, f), field) == getfield(getFactor(summaryGraph, f), field)
            end
        end
    end
end


text:
using Clang.Generators
using Clang.Generators.JLLEnvs
import Vulkan_Headers_jll
using Wayland_jll
using Xorg_libxcb_jll
using Xorg_xorgproto_jll
using Xorg_libX11_jll
using Xorg_libXrandr_jll
using Xorg_libXrender_jll

cd(@__DIR__)

# get include directory & vulkan.h
const VK_INCLUDE = joinpath(Vulkan_Headers_jll.artifact_dir, "include", "vulkan")
const VK_HEADERS = [joinpath(VK_INCLUDE, "vulkan.h")]

# config extensions for different platforms
# X-ref: https://github.com/SaschaWillems/Vulkan/blob/master/CMakeLists.txt
const VK_LINUX_EXTENSION_COMMON = [
    # "-DVK_USE_PLATFORM_DIRECTFB_EXT",  # no JLL package
    "-DVK_USE_PLATFORM_WAYLAND_KHR",
    "-DVK_USE_PLATFORM_XCB_KHR",
    "-DVK_USE_PLATFORM_XLIB_KHR",
    "-DVK_USE_PLATFORM_XLIB_XRANDR_EXT",
]
const VK_MACOS_EXTENSION_COMMON = ["-DVK_USE_PLATFORM_MACOS_MVK", "-DVK_USE_PLATFORM_METAL_EXT"]
const VK_WIN_EXTENSION_COMMON = ["-DVK_USE_PLATFORM_WIN32_KHR"]
const VK_EXTENSIONS_MAP = Dict(
    # "aarch64-apple-darwin20" => vcat(VK_MACOS_EXTENSION_COMMON, "-DVK_USE_PLATFORM_DIRECTFB_EXT"),
    # "x86_64-apple-darwin14" => vcat(VK_MACOS_EXTENSION_COMMON, "-DVK_USE_PLATFORM_DIRECTFB_EXT"),
    "aarch64-apple-darwin20" => VK_MACOS_EXTENSION_COMMON,
    "x86_64-apple-darwin14" => VK_MACOS_EXTENSION_COMMON,
    "i686-w64-mingw32" => VK_WIN_EXTENSION_COMMON,
    "x86_64-w64-mingw32" => VK_WIN_EXTENSION_COMMON,
    # "aarch64-linux-gnu" => vcat(VK_LINUX_EXTENSION_COMMON, "-DVK_USE_PLATFORM_FUCHSIA"),
    # "aarch64-linux-musl" => vcat(VK_LINUX_EXTENSION_COMMON, "-DVK_USE_PLATFORM_FUCHSIA"),
    "aarch64-linux-gnu" => VK_LINUX_EXTENSION_COMMON,
    "aarch64-linux-musl" => VK_LINUX_EXTENSION_COMMON,
    "armv7l-linux-gnueabihf" => VK_LINUX_EXTENSION_COMMON,
    "armv7l-linux-musleabihf" => VK_LINUX_EXTENSION_COMMON,
    "x86_64-linux-gnu" => VK_LINUX_EXTENSION_COMMON,
    "x86_64-linux-musl" => VK_LINUX_EXTENSION_COMMON,
    "i686-linux-gnu" => VK_LINUX_EXTENSION_COMMON,
    "i686-linux-musl" => VK_LINUX_EXTENSION_COMMON,
    "powerpc64le-linux-gnu" => VK_LINUX_EXTENSION_COMMON,
    "x86_64-unknown-freebsd11.1" => ["-DVK_USE_PLATFORM_XCB_KHR", "-DVK_USE_PLATFORM_XLIB_KHR", "-DVK_USE_PLATFORM_XLIB_XRANDR_EXT"],
)

for target in JLLEnvs.JLL_ENV_TRIPLES
    @info "processing $target"

    # programmatically add options
    options = Dict{String,Any}(
        "general" => Dict{String,Any}(),
        "codegen" => Dict{String,Any}(),
        )
    general, codegen = options["general"], options["codegen"]
    general["library_name"] = "libvulkan"
    general["output_file_path"] = joinpath(@__DIR__, "..", "lib", "$target.jl")
    general["use_deterministic_symbol"] = true
    general["print_using_CEnum"] = false
    general["printer_blacklist"] = [
        "VKAPI_PTR",
        "VKAPI_CALL",
        ]
    general["add_fptr_methods"] = true
    codegen["add_record_constructors"] = true
    codegen["union_single_constructor"] = true
    codegen["opaque_as_mutable_struct"] = false

    # add compiler flags
    args = get_default_args(target)
    push!(args, "-I$VK_INCLUDE")

    wayland_inc = JLLEnvs.get_pkg_include_dir(Wayland_jll, target)
    !isempty(wayland_inc) && push!(args, "-isystem$wayland_inc")

    xcb_inc = JLLEnvs.get_pkg_include_dir(Xorg_libxcb_jll, target)
    !isempty(xcb_inc) && push!(args, "-isystem$xcb_inc")

    xlibx_inc = JLLEnvs.get_pkg_include_dir(Xorg_xorgproto_jll, target)
    !isempty(xlibx_inc) && push!(args, "-isystem$xlibx_inc")

    xlib_inc = JLLEnvs.get_pkg_include_dir(Xorg_libX11_jll, target)
    !isempty(xlib_inc) && push!(args, "-isystem$xlib_inc")

    xlibXrandr_inc = JLLEnvs.get_pkg_include_dir(Xorg_libXrandr_jll, target)
    !isempty(xlibXrandr_inc) && push!(args, "-isystem$xlibXrandr_inc")

    xlibXrender_inc = JLLEnvs.get_pkg_include_dir(Xorg_libXrender_jll, target)
    !isempty(xlibXrender_inc) && push!(args, "-isystem$xlibXrender_inc")

    append!(args, VK_EXTENSIONS_MAP[target])

    ctx = create_context(VK_HEADERS, args, options)

    build!(ctx)
end