text:
@testset "Axis" begin
    axis = Axis()

    a1 = Axis(2:3 => 1:2)
    axis = Axis(1:10)

    @test UnitRange(a1) == 1:2

    @test @inferred(Axis(a1)) isa typeof(a1)

    @test @inferred(Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitRange{Int}}}(1:10)) isa Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitRange{Int}}}

    @test @inferred(Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitMRange{Int}}}(1:10)) isa Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitMRange{Int}}}

    @test @inferred(Axis{Int,Int,UnitMRange{Int},SimpleAxis{Int,UnitRange{Int}}}(1:10)) isa Axis{Int,Int,UnitMRange{Int},SimpleAxis{Int,UnitRange{Int}}}

    @test @inferred(Axis{Int,Int,UnitMRange{Int},SimpleAxis{Int,UnitMRange{Int}}}(1:10)) isa Axis{Int,Int,UnitMRange{Int},SimpleAxis{Int,UnitMRange{Int}}}

    @test @inferred(Axis{UInt,Int,UnitRange{UInt},SimpleAxis{Int,UnitRange{Int}}}(1:2)) isa Axis{UInt,Int,UnitRange{UInt},SimpleAxis{Int,UnitRange{Int}}}
    @test @inferred(Axis{UInt,Int,UnitRange{UInt},SimpleAxis{Int,UnitRange{Int}}}(UnitRange(UInt(1), UInt(2)))) isa Axis{UInt,Int,UnitRange{UInt},SimpleAxis{Int,UnitRange{Int}}}

    @test Axis{String,Int,Vector{String},Base.OneTo{Int}}(Axis(["a", "b"])) isa Axis{String,Int,Vector{String},Base.OneTo{Int}}

    @test Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitRange{Int}}}(Base.OneTo(2)) isa Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitRange{Int}}}
    @test Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitRange{Int}}}(1:2) isa Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,UnitRange{Int}}}
    @test Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,Base.OneTo{Int}}}(Base.OneTo(2)) isa Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,Base.OneTo{Int}}}
    @test Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,Base.OneTo{Int}}}(1:2) isa Axis{Int,Int,UnitRange{Int},SimpleAxis{Int,Base.OneTo{Int}}}
end

   #@test @inferred(AxisIndices.to_axis(a1)) == a1

        #= TODO problems with `==` due to == on axes
        @testset "View axes" begin
            a = rand(8)
            idr = IdentityAxis(2:4)
            v = view(a, idr)
            @test axes(v) == (2:4,)
            @test v == OffsetArray(a[2:4], 2:4)

            a = rand(5, 5)
            idr2 = IdentityAxis(3:4)
            v = view(a, idr, idr2)
            @test axes(v) == (2:4, 3:4)
            @test v == OffsetArray(a[2:4, 3:4], 2:4, 3:4)
        end
        =#

@testset "AbstractAxis promotions" begin
    a1 = typeof(Axis(1:10))
    a2 = typeof(Axis(1.0:10.0))
    sa1 = typeof(SimpleAxis(1:10))
    sa2 = typeof(SimpleAxis(UnitRange(UInt(1), UInt(10))))
    p = typeof(Base.OneTo(10))
    pur = typeof(1:10)

    @test promote_type(sa1, sa2) <: SimpleAxis{UInt64,UnitRange{UInt64}}
    @test promote_type(p, sa2) <: SimpleAxis{UInt64,UnitRange{UInt64}}
    @test promote_type(sa2, p) <: SimpleAxis{UInt64,UnitRange{UInt64}}
    @test promote_type(pur, sa2) <: SimpleAxis{UInt64,UnitRange{UInt64}}
    @test promote_type(sa2, pur) <: SimpleAxis{UInt64,UnitRange{UInt64}}
    @test promote_type(p, sa1) <: SimpleAxis{Int64,UnitRange{Int64}}
    @test promote_type(sa1, p) <: SimpleAxis{Int64,UnitRange{Int64}}
    @test promote_type(pur, sa1) <: SimpleAxis{Int64,UnitRange{Int64}}
    @test promote_type(sa1, pur) <: SimpleAxis{Int64,UnitRange{Int64}}

    @test promote_type(Vector{Int}, sa1) <: AbstractVector{Int}
    @test promote_type(sa1, Vector{Int}) <: AbstractVector{Int}

    a12 = Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,ArrayInterface.OptionallyStaticUnitRange{ArrayInterface.StaticInt{1},Int64}}}
    @test promote_type(a1, a2) <: a12
    @test promote_type(a2, a1) <: a12
    @test promote_type(a1, sa1) <: Axis{Int64,Int64,UnitRange{Int64},SimpleAxis{Int64,UnitRange{Int64}}}
    @test promote_type(sa1, a1) <: Axis{Int64,Int64,UnitRange{Int64},SimpleAxis{Int64,UnitRange{Int64}}}
    @test promote_type(a2, sa1) <: Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,UnitRange{Int64}}}
    @test promote_type(sa1, a2) <: Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,UnitRange{Int64}}}
    @test promote_type(a2, pur) <: Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,UnitRange{Int64}}}
    @test promote_type(pur, a2) <: Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,UnitRange{Int64}}}

    # FIXME this is a problem with promotion on ArrayInterface.OptionallyStaticUnitRange, not AxisIndices
    @test_broken promote_type(a2, p) <: Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,UnitRange{Int64}}}
    @test_broken promote_type(p, a2) <: Axis{Float64,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},SimpleAxis{Int64,UnitRange{Int64}}}

    @test @inferred(keys(ArrayInterface.to_axis(Axis([:a, :b]), [2]))) == [:b]
end


text:
module SolveDSGE

using LinearAlgebra
using ForwardDiff
using NLsolve
using GaussQuadrature
using ChebyshevApprox
using SmolyakApprox
using PiecewiseLinearApprox
using Random
using ThreadPools
using ThreadsX

include("structures.jl")
include("parser_functions.jl")
include("auxiliary_functions.jl")
include("solution_functions.jl")
include("analysis_functions.jl")

export PerturbationScheme,
       ChebyshevSchemeStoch,
       ChebyshevSchemeDet,
       SmolyakSchemeStoch,
       SmolyakSchemeDet,
       PiecewiseLinearSchemeStoch,
       PiecewiseLinearSchemeDet,
       FirstOrderSolutionStoch,
       FirstOrderSolutionDet,
       SecondOrderSolutionStoch,
       SecondOrderSolutionDet,
       ThirdOrderSolutionStoch,
       ThirdOrderSolutionDet,
       ChebyshevSolutionStoch,
       ChebyshevSolutionDet,
       SmolyakSolutionStoch,
       SmolyakSolutionDet,
       SmolyakSolutionStoch,
       PiecewiseLinearSolutionStoch,
       PiecewiseLinearSolutionDet,
       StateSpaceEqm

export process_model,
       retrieve_processed_model,
       assign_parameters,
       compute_steady_state,
       solve_first_order,
       solve_second_order,
       solve_third_order,
       solve_nonlinear,
       solve_model,
       compute_mean,
       simulate,
       impulses,
       approximate_density,
       approximate_distribution,
       compare_solutions,
       state_space_eqm,
       euler_errors

export chebyshev_nodes,
       chebyshev_extrema,
       chebyshev_extended,
       chebyshev_gauss_lobatto,
       clenshaw_curtis_equidistant,
       chebyshev_evaluate,
       smolyak_evaluate,
       piecewise_linear_evaluate,
       chebyshev_weights,
       smolyak_weights

end

ext: jl


text:
let

tpq = 960
p = 0x4f

n = Notes([
Note(p, 100, 0, tpq),
Note(p, 100, tpq, tpq),
Note(p, 100, 2tpq, tpq),
Note(p, 100, 3tpq, tpq),
Note(p, 100, 4tpq, tpq),
Note(p, 100, 5tpq, tpq),
Note(p, 100, 6tpq, tpq),
Note(p, 100, 7tpq, tpq),
Note(p, 100, 8tpq, tpq)
], tpq)

track = MIDITrack()

addnotes!(track, n)

lyrics = [textevent(:lyric, i == 3 ? "lnt" : "l") for i in 1:3]
addevents!(track, [0, tpq, 2tpq], lyrics)

texts = [textevent(:text, i == 3 ? "tnl" : "t") for i in 1:3]
addevents!(track, [0, tpq, 2tpq], texts)

markers = [textevent(:marker, X) for X in ["A", "B"]]
addevents!(track, [0, 4tpq], markers)

midi = MIDIFile(0, tpq, [track])
cd(@__DIR__)
save("texts.mid", midi)

midi = load("texts.mid")
t = midi.tracks[1]

@test findtextevents(:marker, t)[1] == ["A", "B"]
@test findtextevents(:text, t)[1]  == ["t", "t", "tnl"]
@test findtextevents(:lyric, t)[1] == ["l", "l", "lnt"]

rm("texts.mid")

end


text:
# Branch and Bound, Eduardo Brito.
module SolverBrito

    using JuMP, Gurobi
#--------------------------------------------------------------------------------------------------
    # Struct para informacoes de um modelo generico
    mutable struct prob_lp
        A
        c
        n
        m
        xlb
        xub
        rowlb
        rowub
        vtypes
        solver
    end

#--------------------------------------------------------------------------------------------------
    # Struct para resposta de um problema generico
    mutable struct resposta_relaxado
        obj #Z*
        vars #X*
        status #Status, :Optimal, :Infeasible, ...
    end

#--------------------------------------------------------------------------------------------------
    # Struct que combina o modelo com a resposta
    mutable struct modelo_lista
        problem::prob_lp
        resp::resposta_relaxado
        modelo_lista() = new()
    end

#--------------------------------------------------------------------------------------------------
    # Funcao para extrair informacoes do modelo e passar para a struct prob_lp
    function converte_modelo(m::Model)
        md = deepcopy(m)

        if md.objSense == :Max
            @objective(md,:Min,-md.obj)
        end

        c = JuMP.prepAffObjective(md)
        A = JuMP.prepConstrMatrix(md)
        n, m = size(A)
        xlb = copy(md.colLower)
        xub = copy(md.colUpper)
        rowlb, rowub = JuMP.prepConstrBounds(md)
        solver = md.solver
        vtypes = md.colCat .!= :Cont
        problem = prob_lp(A,c,n,m,xlb,xub,rowlb,rowub,vtypes,solver)
        return problem
    end

#--------------------------------------------------------------------------------------------------
    # Funcao que resolve um modelo do struct "prob_lp", e devolve em forma do struct resp
    function solve_relax(modelo::prob_lp)
        model = Model(solver=modelo.solver)
        @variable(model, x[1:modelo.m])
        for i in 1:modelo.m
            setlowerbound(x[i], modelo.xlb[i])
            setupperbound(x[i], modelo.xub[i])
        end
        @constraint(model, modelo.rowlb .<= modelo.A*x .<= modelo.rowub)
        @objective(model, Min, dot(modelo.c,x))

        status = solve(model)
        resposta = resposta_relaxado(getobjectivevalue(model),getvalue(x),status)
        return resposta
    end

#--------------------------------------------------------------------------------------------------
    # Funcao podas. Recebe a resposta do filho e pode por inviabilidade, limite e otimalidade.
    function podas(resp ::resposta_relaxado, global_bound,vtypes)

        #Poda por Inviabilidade
        if(resp.status != :Optimal)
            return "erro"
        end
        #Poda por Limite
        if (resp.obj>global_bound[2])
            return "erro"
        end
        #Poda por Otimalidade
        if ( sum(abs.(resp.vars[vtypes] - round.(resp.vars[vtypes]))) == 0)
            return Float64(resp.obj)
        end
        return "sucesso"
    end

#--------------------------------------------------------------------------------------------------
    # Funcao exporta_model. Recebe o modelo original e atualiza os valores do z*, x, ... , e preenche listas dos nos e solucoes inteiras
    function exporta_model(m,zinf,nodes,integer_solutions,time,global_bound,iter)
        m.objVal = zinf.resp.obj
        m.colVal = zinf.resp.vars
        m.objBound = abs(global_bound[1])

        m.ext[:status] = zinf.resp.status
        m.ext[:time] = time
        m.ext[:nodes] = size(nodes)[1]
        m.ext[:intsols] = size(integer_solutions)[1]
        m.ext[:iter] = iter

        return m
    end
#--------------------------------------------------------------------------------------------------
    # Funcao que recebe a lista de problemas e retorna o indice do problema a ser trabalhado. (Para P2)
    function acha_problema(lista)
        return 1
    end

#--------------------------------------------------------------------------------------------------
    # Funcao que recebe a lista de problemas e o indice do problema selecionado e retorna o indice da variavel para o branch. (Pega a mais fracionaria)
    function acha_variavel(lista,ind_prob)
        X = lista[ind_prob].resp.vars
        Var_Cont = convert(Array{Int,1},lista[ind_prob].problem.vtypes)

        ind = indmax(abs.(X.*Var_Cont - round.(X.*Var_Cont)))
        return ind
    end

#--------------------------------------------------------------------------------------------------
    #Funcao SOLVEMIP
    function SolveMIP(m)
        tic()

        # Cria listas: De problemas, nos encontrados e solucoes inteiras. -------------------------
        lista = Vector{modelo_lista}()
        nodes = Vector{modelo_lista}()
        integer_solutions = Vector{modelo_lista}()

        # Adiciona o 1o problema de forma manual na lista. ----------------------------------------
        zinf = modelo_lista()
        zinf.problem = converte_modelo(m)
        zinf.resp = solve_relax(zinf.problem)
        global_bound = [zinf.resp.obj,+Inf]
        push!(lista,zinf)

        # Comeca o algoritmo do branch and bound. -------------------------------------------------
        iter = 0
        jg_time0 = time_ns()
        if sum(zinf.problem.vtypes) != 0 #Esta checando se todas as variaveis ja sao inteiras

            while (abs(global_bound[2] - global_bound[1]) >= exp10(-5) && size(lista)[1] != 0 && iter <= 1000 && (time_ns()-jg_time0)/1e9 < 180) #Para por limite de iteracoes, bound e tamanho da lista

                # Seleciona problema usando a funcao "acha_problema". -----------------------------
                ind_prob = acha_problema(lista)

                # Seleciona a variavel a ser modificada -------------------------------------------
                ind = acha_variavel(lista,ind_prob)

                # Branch --------------------------------------------------------------------------
                prob_LF = deepcopy(lista[ind_prob].problem)
                prob_LF.xlb[ind] = floor(lista[ind_prob].resp.vars[ind]) + 1 # Altera bounds da variavel

                prob_RT = deepcopy(lista[ind_prob].problem)
                prob_RT.xub[ind] = floor(lista[ind_prob].resp.vars[ind]) # Altera bounds da variavel

                # Soluciona os filhos ------------------------------------------------------------
                resp_LF = solve_relax(prob_LF)
                resp_RT = solve_relax(prob_RT)

                # Podas --------------------------------------------------------------------------
                poda_LF = podas(resp_LF, global_bound,prob_LF.vtypes)
                poda_RT = podas(resp_RT, global_bound,prob_RT.vtypes)

                # Monta novas variaveis da struct modelo_lista, prontas para serem adicionadas na lista
                #LEFT
                novo_LF = modelo_lista()
                novo_LF.problem = prob_LF
                novo_LF.resp = resp_LF

                #RIGHT
                novo_RT = modelo_lista()
                novo_RT.problem = prob_RT
                novo_RT.resp = resp_RT

                # Olha para as podas e atualiza o global_bound -----------------------------------
                if typeof(poda_RT) == Float64
                    if typeof(poda_LF) == Float64
                        if poda_RT <= poda_LF
                            global_bound[2] = poda_RT
                        else
                            global_bound[2] = poda_LF
                        end
                    else
                        global_bound[2] = poda_RT
                    end
                elseif typeof(poda_LF) == Float64
                        global_bound[2] = poda_LF
                end

                #Atualiza Zinf (Melhor solucao ate o momento) ----------------------------------
                if typeof(poda_LF) == Float64
                    if novo_LF.resp.obj <= global_bound[2]
                        zinf = deepcopy(novo_LF)
                    end
                end
                if typeof(poda_RT) == Float64
                    if typeof(poda_LF) == Float64
                        if novo_RT.resp.obj <= global_bound[2] && novo_RT.resp.obj <= novo_LF.resp.obj
                            zinf = deepcopy(novo_RT)
                        end
                    else
                        if novo_RT.resp.obj <= global_bound[2]
                            zinf = deepcopy(novo_RT)
                        end
                    end
                end

                #Remove o problema original e adiciona os novos ---------------------------------
                # remove
                push!(nodes,lista[ind_prob])
                deleteat!(lista,ind_prob)

                # adiciona
                if poda_LF == "sucesso"
                    push!(lista,novo_LF)
                elseif typeof(poda_LF) == Float64
                    push!(integer_solutions, novo_LF)
                end

                if poda_RT == "sucesso"
                    push!(lista,novo_RT)
                elseif typeof(poda_RT) == Float64
                    push!(integer_solutions, novo_RT)
                end
                iter += 1
            end
        end

        # Fim do while, agora verificar model.sense, corrigir caso necessario o z*.
        if m.objSense == :Max
            zinf.resp.obj = - zinf.resp.obj
        end

        time = toc() # Registro do tempo

        # Usa a funcao exporta_model para compor o modelo final usando a melhor solucao encontrada ate agora.
        model = exporta_model(m,zinf,nodes,integer_solutions,time,global_bound,iter)

        return model
    end
end


text:
using NetCDF

function ncplot(fn::String;hd=false,col::Vector=colormap("Blues"),timavg=true)
  nc = NetCDF.open(fn)
  for va in nc.vars
    v=va[2]
    totranspose=(lowercase(v.dim[1].name)[1:3]=="lon")
    if ((v.ndim==3) && (timavg==false))
      ncplotallsteps(nc,v,hd,col,totranspose)
    else
      if (v.ndim==2)
        missval = haskey(v.atts,"missing_value") ? v.atts["missing_value"] : haskey(v.atts,"_FillValue") ? v.atts["missing_value"] : typemax(eltype(v))
        su=haskey(v.atts,"units") ? v.atts["units"] : ""
        su="units = $(su)"
        ti = haskey(v.atts,"long_name") ? v.atts["long_name"] : v.name
        imageterm(v,missval=missval,hd=hd,col=col,title=ti,subtitle=su,transpose=totranspose)
      end
    end
  end
end

function ncplotallsteps(nc,v,hd,col,totranspose)
  missval = haskey(v.atts,"missing_value") ? v.atts["missing_value"] : haskey(v.atts,"_FillValue") ? v.atts["missing_value"] : typemax(eltype(v))
  for i in 1:v.dim[3].dimlen
    a=NetCDF.readvar(nc,v,[1,1,i],[-1,-1,1])[:,:,1]
    println("Time step $i of $(v.dim[3].dimlen):")
    imageterm(a,missval=missval,hd=hd,col=col)
  end
end

function readtimavg(nc::NcFile,v::NcVar)
  #Average over all time steps
        missval = haskey(v.atts,"missing_value") ? v.atts["missing_value"] : 1.0e32
        ntime=v.dim[3].dimlen
        a=NetCDF.readvar(nc,v,[1,1,1],[-1,-1,1])
        ncount=(a.!=missval)*1
        if ntime>1
          a[a.==missval]=zero(a[1,1,1])
          for i=2:ntime
            ar=NetCDF.readvar(nc,v,[1,1,i],[-1,-1,1])
            ncount=ncount+(ar.!=missval)*1
            ar[ar.==missval]=zero(ar[1,1,1])
            a=a+ar
          end
          a=(a./ncount)[:,:,1]
          a[isnan(a)]=missval
        else
          a=a[:,:,1]
        end
      return(a)
end


text:
######################################################################
# downloads and makes SARSOP
######################################################################
if ispath("appl-sarisa")
	rm("appl-sarisa", recursive=true)
end
if ispath("appl-sarisa.tar.gz")
	rm("appl-sarisa.tar.gz", recursive=true)
end
if ispath("appl-sarisa.tar")
	rm("appl-sarisa.tar", recursive=true)
end

# download and decompress
download("https://web.stanford.edu/group/sisl/resources/appl-sarisa.tar.gz", "appl-sarisa.tar.gz")
run(`gunzip appl-sarisa.tar.gz`)
run(`tar -xvf appl-sarisa.tar`)

# actually build all the variants
cd("appl-sarisa/src")
run(`bash makeall.sh`)


text:
# syntax: proto2
using Compat
using ProtoBuf
import ProtoBuf.meta
import Base: hash, isequal, ==
using ProtoBuf.GoogleProtoBuf

type CodeGeneratorRequest
    file_to_generate::Array{AbstractString,1}
    parameter::AbstractString
    proto_file::Array{FileDescriptorProto,1}
    CodeGeneratorRequest(; kwargs...) = (o=new(); fillunset(o); isempty(kwargs) || ProtoBuf._protobuild(o, kwargs); o)
end #type CodeGeneratorRequest
const __fnum_CodeGeneratorRequest = Int[1,2,15]
meta(t::Type{CodeGeneratorRequest}) = meta(t, ProtoBuf.DEF_REQ, __fnum_CodeGeneratorRequest, ProtoBuf.DEF_VAL, true, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
hash(v::CodeGeneratorRequest) = ProtoBuf.protohash(v)
isequal(v1::CodeGeneratorRequest, v2::CodeGeneratorRequest) = ProtoBuf.protoisequal(v1, v2)
==(v1::CodeGeneratorRequest, v2::CodeGeneratorRequest) = ProtoBuf.protoeq(v1, v2)

type CodeGeneratorResponse_File
    name::AbstractString
    insertion_point::AbstractString
    content::AbstractString
    CodeGeneratorResponse_File(; kwargs...) = (o=new(); fillunset(o); isempty(kwargs) || ProtoBuf._protobuild(o, kwargs); o)
end #type CodeGeneratorResponse_File
const __fnum_CodeGeneratorResponse_File = Int[1,2,15]
meta(t::Type{CodeGeneratorResponse_File}) = meta(t, ProtoBuf.DEF_REQ, __fnum_CodeGeneratorResponse_File, ProtoBuf.DEF_VAL, true, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
hash(v::CodeGeneratorResponse_File) = ProtoBuf.protohash(v)
isequal(v1::CodeGeneratorResponse_File, v2::CodeGeneratorResponse_File) = ProtoBuf.protoisequal(v1, v2)
==(v1::CodeGeneratorResponse_File, v2::CodeGeneratorResponse_File) = ProtoBuf.protoeq(v1, v2)

type CodeGeneratorResponse
    error::AbstractString
    file::Array{CodeGeneratorResponse_File,1}
    CodeGeneratorResponse(; kwargs...) = (o=new(); fillunset(o); isempty(kwargs) || ProtoBuf._protobuild(o, kwargs); o)
end #type CodeGeneratorResponse
const __fnum_CodeGeneratorResponse = Int[1,15]
meta(t::Type{CodeGeneratorResponse}) = meta(t, ProtoBuf.DEF_REQ, __fnum_CodeGeneratorResponse, ProtoBuf.DEF_VAL, true, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
hash(v::CodeGeneratorResponse) = ProtoBuf.protohash(v)
isequal(v1::CodeGeneratorResponse, v2::CodeGeneratorResponse) = ProtoBuf.protoisequal(v1, v2)
==(v1::CodeGeneratorResponse, v2::CodeGeneratorResponse) = ProtoBuf.protoeq(v1, v2)

export CodeGeneratorRequest, CodeGeneratorResponse_File, CodeGeneratorResponse


text:
import Sario: DemRsc

# TODO: make a macro for functions to always get defined for img and demrsc?

# if they pass `end` into getindex, it turns into an Int, which is already a row
# Otherwise, it's a latitude/longitude if they pass a float
_get_row(demrsc::DemRsc, x::Real) = typeof(x) <: AbstractFloat ? nearest_row(demrsc, x) : x
_get_col(demrsc::DemRsc, x::Real) = typeof(x) <: AbstractFloat ? nearest_col(demrsc, x) : x

function nearest_row(demrsc::DemRsc, lat::AbstractFloat)
    @unpack x_first, x_step, y_first, y_step = demrsc
    return Int(round(1 + (lat - y_first) / y_step))
end

function nearest_col(demrsc::DemRsc, lon::AbstractFloat)
    @unpack x_first, x_step, y_first, y_step = demrsc
    return Int(round(1 + (lon - x_first) / x_step))
end

# """Find the nearest row, col to a given lat and/or lon"""
function nearest_pixel(demrsc::DemRsc, lat::AbstractFloat, lon::AbstractFloat)
    return nearest_row(demrsc, lat), nearest_col(demrsc, lon)
end

nearest_pixel(demrsc::DemRsc,
              lats::AbstractArray{<:AbstractFloat},
              lons::AbstractArray{<:AbstractFloat}) = [nearest_pixel(demrsc, lat, lon)
                                                       for (lat, lon) in zip(lats, lons)]

nearest_row(img::MapImage, lats, lons) = nearest_row(img.demrsc, lats, lons)
nearest_col(img::MapImage, lats, lons) = nearest_col(img.demrsc, lats, lons)
nearest_pixel(img::MapImage, lats, lons) = nearest_pixel(img.demrsc, lats, lons)

nearest(arr::AbstractArray, point) = Int(round((point - first(arr)) / (arr[2] - arr[1])))

""" Takes the row, col of a pixel and finds its lat/lon """
function rowcol_to_latlon(demrsc::DemRsc, row, col)
    @unpack y_step, x_step, y_first, x_first = demrsc
    lat = y_first + (row - 1) * y_step
    lon = x_first + (col - 1) * x_step
    return lat, lon
end

# Holdover from the python function naming...
latlon_to_rowcol(demrsc::DemRsc, lat::AbstractFloat, lon::AbstractFloat) = nearest_pixel(demrsc, lat, lon)

# For passing Tuples of ranges of lats/lons (or (lat1, end) )
function latlon_to_rowcol(demrsc::DemRsc,
                          lats::Tuple{<:Real, <:Real},
                          lons::Tuple{<:Real, <:Real})::Tuple{UnitRange{Int},UnitRange{Int}}
    @unpack rows, cols = demrsc

    row_bnds = [_get_row(demrsc, l) for l in lats]
    # row_bnds[1] = typeof(row_bnds[1]) <: Colon ? 1 : row_bnds[1]
    # row_bnds[2] = typeof(row_bnds[2]) <: Colon ? rows : row_bnds[2]
    # In case they put it in backwards (since it's confusing with high lats first)
    sort!(row_bnds)
    clamp!(row_bnds, 1, rows)
    rowrange = row_bnds[1]:row_bnds[2]

    col_bnds = [_get_col(demrsc, l) for l in lons]
    sort!(col_bnds)
    clamp!(col_bnds, 1, cols)
    colrange = col_bnds[1]:col_bnds[2]
    return rowrange, colrange
end

"""
Returns:
    tuple[float]: the boundaries of the intersection box of the 2 areas in order:
    (lon_left,lon_right,lat_bottom,lat_top)
"""
function intersection_corners(dem1::DemRsc, dem2::DemRsc)
    corners1 = grid_corners(dem1)
    corners2 = grid_corners(dem2)
    lons1, lats1 = zip(corners1...)
    lons2, lats2 = zip(corners2...)
    left = _max_min(lons1, lons2)
    right = _least_common(lons1, lons2)
    bottom = _max_min(lats1, lats2)
    top = _least_common(lats1, lats2)
    return left, right, bottom, top
end

intersection_corners(img1::MapImage, img2::MapImage) = intersection_corners(img1.demrsc, img2.demrsc)

_max_min(a, b) = max(minimum(a), minimum(b))
_least_common(a, b) = min(maximum(a), maximum(b))

#
function find_overlap_idxs(asc_img, desc_img, asc_demrsc::DemRsc, desc_demrsc::DemRsc)
    left, right, bottom, top = intersection_corners(asc_demrsc, desc_demrsc)
    println(left, right, bottom, top)

    row1, col1 = nearest_pixel(asc_demrsc, top, left)
    row2, col2 = nearest_pixel(asc_demrsc, bottom, right)
    asc_idxs = (row1:row2, col1:col2)

    row1, col1 = nearest_pixel(desc_demrsc, top, left)
    row2, col2 = nearest_pixel(desc_demrsc, bottom, right)
    desc_idxs = (row1:row2, col1:col2)

    return asc_idxs, desc_idxs
end

find_overlap_idxs(asc::MapImage, desc::MapImage) = find_overlap_idxs(asc.image, desc.image, asc.demrsc, desc.demrsc)

function find_overlaps(asc_fname::AbstractString, desc_fname::AbstractString=asc_fname, dset="velos/1")
    asc_img = MapImage(asc_fname, dset)
    desc_img = MapImage(desc_fname, dset)
    return find_overlaps(asc_img, desc_img)
end

function find_overlaps(asc_img::MapImage{T, 2}, desc_img::MapImage{T, 2}) where {T}
    asc_idxs, desc_idxs = find_overlap_idxs(asc_img, desc_img)
    a, d = asc_img[asc_idxs...], desc_img[desc_idxs...]
    a, d = _mask_asc_desc(a, d)
    return a, d
end

# TODO: these should really be one... figure out
function find_overlaps(asc_img::MapImage{T, 3}, desc_img::MapImage{T, 3}) where {T}
    asc_idxs, desc_idxs = find_overlap_idxs(asc_img, desc_img)
    a, d = asc_img[asc_idxs..., :], desc_img[desc_idxs..., :]
    a, d = _mask_asc_desc(a, d)
    return a, d
end

function _mask_asc_desc(a, d)
    m1 = a .== 0;
    m2 = d .== 0;
    mask = m1 .| m2
    a[mask] .= 0;
    d[mask] .= 0;
    return a, d
end

function grid_corners(demrsc::DemRsc)
    """Takes sizes and spacing from .rsc info, finds corner points in (x, y) form

    Returns:
        list[tuple[float]]: the corners of the latlon grid in order:
        (top right, top left, bottom left, bottom right)
    """
    left, right, bot, top = grid_extent(demrsc)
    return [(right, top), (left, top), (left, bot), (right, bot)]
end
grid_corners(img::MapImage) = grid_corners(img.demrsc)

"""
Get the boundaries of a grid

Returns:
    tuple[float]: the boundaries of the latlon grid in order:
    (lon_left,lon_right,lat_bottom,lat_top)
"""
function grid_extent(demrsc::DemRsc)
    @unpack rows, cols, y_step, x_step, y_first, x_first = demrsc
    return (x_first, x_first .+ x_step * (cols - 1), y_first + y_step * (rows - 1), y_first)
end
grid_extent(img::MapImage) = grid_extent(img.demrsc)

"""Returns meshgrid-like arrays X, Y

julia> XX, YY = MapImages.grid(demrsc);

julia> XX[1:3, 1:3]
3×3 Array{Float64,2}:
 -104.0  -103.998  -103.997
 -104.0  -103.998  -103.997
 -104.0  -103.998  -103.997

julia> YY[1:3, 1:3]
3×3 Array{Float64,2}:
 31.9     31.9     31.9
 31.8983  31.8983  31.8983
 31.8967  31.8967  31.8967

julia> XX, YY = MapImages.grid(demrsc, sparse=true);
"""
function grid(demrsc::DemRsc; sparse=false)
    @unpack rows, cols, y_step, x_step, y_first, x_first = demrsc
    x = range(x_first, step=x_step, length=cols)
    y = range(y_first, step=y_step, length=rows)
    sparse && return (x, y)

    # return collect(reshape(y, :, 1) .* reshape(x, 1, :))
    return collect(ones(length(y), 1) .* reshape(x, 1, :)), collect(reshape(y, :, 1) .* ones(1, length(x)))
end
grid(img::MapImage; sparse=false) = grid(img.demrsc, sparse=sparse)

"""Find the distance between two lat/lon points on Earth

Uses the haversine formula: https://en.wikipedia.org/wiki/Haversine_formula
so it does not account for the ellopsoidal Earth shape. Will be with about
0.5-1% of the correct value.

Notes: lats and lons are in degrees, and the values used for R Earth
(6373 km) are optimized for locations around 39 degrees from the equator

Reference: https://andrew.hedges.name/experiments/haversine/
"""
function latlon_to_dist(lat_lon_start, lat_lon_end, R=6378)
    lat1, lon1 = lat_lon_start
    lat2, lon2 = lat_lon_end
    dlat = deg2rad(lat2 - lat1)
    dlon = deg2rad(lon2 - lon1)
    lat1 = deg2rad(lat1)
    lat2 = deg2rad(lat2)
    a = (sin(dlat / 2)^2) + (cos(lat1) * cos(lat2) * sin(dlon / 2)^2)
    c = 2 * atan(sqrt(a), sqrt(1 - a))
    return R * c

end

oob(row, col, out) = row < 1 || row > size(out, 1) || col < 1 || col > size(out, 2)
function bin_vals(demrsc, df; valcol=:sum15_17, loncol=:LongNAD27, latcol=:LatNAD27)
    out = zeros(size(demrsc))
    for (lon, lat, val) in eachrow(df[:, [loncol, latcol, valcol]])
        row, col = nearest_pixel(demrsc, lat, lon)
        oob(row, col, out) && continue
        out[row, col] += val
    end
    return out
end

function coarse_bin_vals(demrsc, df; digits=2, valcol=:sum15_17, loncol=:LongNAD27, latcol=:LatNAD27)
    lons, lats = coarse_grid(demrsc, digits)
    out = zeros(size(lats, 1), size(lons, 1))
    for (lon, lat, val) in eachrow(df[:, [loncol, latcol, valcol]])
        row = nearest(lats, lat)
        col = nearest(lons, lon)
        oob(row, col, out) && continue
        out[row, col] += val
    end
    return out
end

"""Create a grid from a DemRsc on the 10^(-`digits`) grid lines"""
function coarse_grid(demrsc, digits=2)
    lons, lats = MapImages.grid(demrsc, sparse=true)
    lat1, lat2 = round.(extrema(lats), digits=digits)
    lon1, lon2 = round.(extrema(lons), digits=digits)
    step = 10.0^(-digits)
    return lon1:step:lon2, lat2:(-step):lat1
end


text:
"""
    array_geometry(lon, lat, alt) -> easting, northing, elevation, mean_lon, mean_lat
    array_geometry(traces::AbstractArray{Seis.Trace}) -> easting, northing, elevation, mean_lon, mean_lat
    array_geometry(stations::AbstractArray{Seis.Station}) -> easting, northing, elevation, missing, missing

Return the local `easting` and `northing` in km of each station in the array `s` in
a transverse Mercator transform, relative to the centre of the array.  The longitude
and latitude of the array centre are given by `mean_lon` and `mean_lat` (°) respectively.

`lon` and `lat` should be in ° and `alt` in m.  Or provide an array of `Seis.Trace`s
or `Seis.Station`s.  If `traces` or `stations` is an array of `CartTrace` or
`CartStation`s, then no mean longitude or latitude are calculated and instead
they are returned as `nothing`.
"""
function array_geometry(lon, lat, alt)
    T = eltype(lon)
    length(lon) == length(lat) == length(alt) ||
        throw(DimensionMismatch("`lon` and `lat` must be the same length"))
    # Find spherical mean of the array, ignoring altitude
    x, y, z = geog2cart(lon, lat, true)
    X, Y, Z = Float64.(sum.((x, y, z)))
    mean_lon, mean_lat, r = cart2geog(X, Y, Z, true)
    # Create transform into a local TM projection
    origin = Geodesy.LLA(mean_lat, mean_lon, zero(eltype(lat)))
    transform = Geodesy.ENUfromLLA(origin, Geodesy.wgs84)
    # Do the transform.  x and y are in metres
    x = Array{T}(undef, length(lon))
    y = similar(x)
    z = similar(x)
    for (i, (lo, la, al)) in enumerate(zip(lon, lat, alt))
        p = transform(Geodesy.LLA(la, lo, al))
        x[i] = p.e/1e3
        y[i] = p.n/1e3
        z[i] = p.u/1e3
    end
    x, y, z, mean_lon, mean_lat
end

array_geometry(s::TraceArray) = array_geometry(s.sta)

array_geometry(stas::AbstractArray{<:Seis.GeogStation{T}}) where T =
    array_geometry(stas.lon, stas.lat, coalesce.(stas.elev, zero(T)))

function array_geometry(stas::AbstractArray{<:Seis.CartStation{T}}) where T
    to_km = T(0.001)
    xx = stas.x
    yy = stas.y
    zz = stas.z
    any(x -> x[1] === missing || x[2] === missing, zip(xx, yy)) &&
        throw(ArgumentError("all stations must have both x and y coordinates"))
    x = to_km*(xx .- mean(xx))
    y = to_km*(yy .- mean(yy))
    z = to_km*coalesce.((zz .- mean(zz)), zero(T))
    x, y, z, nothing, nothing
end

"""
    array_centre(lon, lat) -> midlon, midlat

Return the mean location of the array in spherical coordinates.
`lon` and `lat` are vectors in °.
"""
function array_centre(lon, lat)
    length(lon) == length(lat) ||
        throw(DimensionMismatch("`lon` and `lat` must be the same length"))
    x, y, z = geog2cart(lon, lat, true)
    X, Y, Z = sum.((x, y, z))
    mlon, mlat, r = cart2geog(X, Y, Z, true)
    mlon, mlat
end
array_centre(s::TraceArray) = array_centre(s.sta.lon, s.sta.lat)

"""
    delay_times(x, y, sx, sy) -> delays
    delay_times(s::AbstractArray{Seis.Trace}, sx, sy) -> delays

Return the delay times `delays` relative to the array centre for an incoming
plane wave with horizontal slownesses `sx` and `sy` s/km.  Stations have
coordinates `x` and `y` in the east and north directions respectively (km).
Or give an array of `Seis.Trace`s.
"""
function delay_times(x, y, sx, sy)
    length(x) == length(y) ||
        throw(ArgumentError("Length of `x` and `y` must be the same"))
    delays = Array{float(eltype(x))}(undef, length(x), length(sx), length(sy))
    @inbounds for (j, ssyy) in enumerate(sy), (i, ssxx) in enumerate(sx)
        @simd for k in eachindex(x)
            delays[k,i,j] = ssxx*x[k] + ssyy*y[k]
        end
    end
    delays
end
delay_times(s::TraceArray, sx, sy) = delay_times(array_geometry(s)[1:2]..., sx, sy)

"""
    find_maxima(sx, sy, power) -> sx_max, sy_max, slowness, backazimuth

Find `n` maxima in the beam power grid `power`, and return vectors
of `sx_max`, `sy_max`, `slowness` and `backazimuth` for each.
"""
function find_maxima(sx, sy, power::AbstractArray{T,2}, n=1) where T
    inds = sortperm(vec(power), rev=true)
    sx_max, sy_max, p, β = T[], T[], T[], T[]
    for i in 1:n
        ix, iy = Tuple(CartesianIndices(power)[inds[i]])
        push!(sx_max, sx[ix])
        push!(sy_max, sy[iy])
        push!(p, sqrt(sx[ix]^2 + sy[iy]^2))
        push!(β, rad2deg(atan(sx[ix], sy[iy])))
    end
    sx_max, sy_max, p, β
end

"List of available stacking routines"
const AVAILABLE_STACK_METHODS = (:linear, :nthroot, :phaseweight)

"""
    stack(S::AbstractArray{Seis.Trace}, time_range, align=zeros(length(S)), weight; method=:linear) -> s::Seis.Trace

Return the linear stack of all traces in `S`, aligned in time on the
value in `align`, between `times[1]` and `times[end]`.

`times` can be a Tuple, Range or Array; in all cases, only the first and last values
are used.  **N.B. If using a non-integer Range, this may not include the end value
you expect.  E.g., `0.1:0.9` by default contains only the value `0.1` because the
default step is `1`.**

`align` can be a header symbol, or an array of equal length to `S`.  If it is a Symbol,
then the values in the headers are used.

All traces must have the same sampling frequency, and an error is thrown
if not all traces can be included in the stack.

If provided, traces are weighted by the values in `weight`, which are normalised
to sum to unity.

Available stacking methods are: $(AVAILABLE_STACK_METHODS).
"""
function stack(S::AbstractArray{<:Seis.Trace{T}}, time_range,
               align::Union{Symbol,AbstractArray}=zeros(T, length(S));
               weights=ones(T, length(S))./length(S),
               method=:linear,
               n=nothing) where T
    # Convert to a vector of times if given as a symbol
    if typeof(align) == Symbol
        picks = getproperty(S.picks, align)
        any(ismissing, picks) && error("not all traces have a pick ")
        align = picks.time
    end
    all(x -> x == S[1].delta, S.delta) || error("all traces must have same delta")
    length(S) == length(weights) ||
        throw(ArgumentError("weights vector must be same length as traces"))
    delay = -align
    t1, t2 = time_range[1], time_range[end]
    weights ./= sum(weights) # Normalise weights
    if method == :linear
        stack_trace = _stack_linear([Seis.trace(s) for s in S],
            S[1].delta, S.b, delay, t1, t2, weights)
    elseif method == :nthroot
        n === nothing &&
            throw(ArgumentError("n must be provided for nthroot stacking"))
        stack_trace = _stack_nthroot([Seis.trace(s) for s in S],
            n, S[1].delta, S.b, delay, t1, t2, weights)
    elseif method == :phaseweight
        n === nothing &&
            throw(ArgumentError("n must be provided for phaseweighted stacking"))
        stack_trace = _stack_phaseweight([Seis.trace(s) for s in S],
            n, S[1].delta, S.b, delay, t1, t2, weights)
    else
        throw(ArgumentError("unrecognised stacking method"))
    end
    Seis.Trace{T}(t1, S[1].delta, stack_trace)
end

"""
    _stack_linear(a, delta, b, delay, t1, t2) -> stacked_trace::Vector{eltype(a)}

Return an array containing the linear stack of the vector of vectors `a`, whose
start times are given in `b`, and where the stack is returned between times `t1` and `t2`
(which can be negative if delay is also negative).  `delta` is the sampling interval
for all the traces.

The trace starts at time `t1` and has sample spacing `delta`.
"""
function _stack_linear(a::Vector{<:AbstractArray}, delta::Real, b::Vector,
        delay::Vector, t1::Real, t2::Real, weights)
    N, npts, b_shift, stack_trace = _stack_array(a, delta, b, delay, t1, t2)
    # stack_trace checks that we are always in array bounds
    @inbounds for j in 1:N
        ip1 = round(Int, (t1 - b_shift[j])/delta) + 1
        w = weights[j]
        @simd for i in ip1:(ip1+npts-1)
            stack_trace[i-ip1+1] += a[j][i]*w
        end
    end
    stack_trace
end

"""
    _stack_nthroot(a::Vector{<:AbstractArray}, n, delta, b, delay, t1, t2, weights)

Return an array containing the `n`throot stack of the vector of vectors `a`, whose
start times are given in `b`, and where the stack is returned between times `t1` and `t2`
(which can be negative if delay is also negative).  `delta` is the sampling interval
for all the traces.

The trace starts at time `t1` and has sample spacing `delta`.
"""
function _stack_nthroot(a::Vector{<:AbstractArray}, n, delta, b, delay, t1, t2, weights)
    N, npts, b_shift, stack_trace = _stack_array(a, delta, b, delay, t1, t2)
    @inbounds for j in 1:N
        ip1 = round(Int, (t1 - b_shift[j])/delta) + 1
        w = weights[j]
        @simd for i in ip1:(ip1+npts-1)
            stack_trace[i-ip1+1] += sign(a[j][i])*abs(a[j][i])^(1/n)*w
        end
    end
    stack_trace .= sign.(stack_trace).*abs.(stack_trace).^n
    stack_trace
end

"""
    _stack_phaseweight(a::Vector{<:AbstractArray}, delta, b, delay, t1, t2, weights)

Return an array containing the phaseweighted stack of the vector of vectors `a`, whose
start times are given in `b`, and where the stack is returned between times `t1` and `t2`
(which can be negative if delay is also negative).  `delta` is the sampling interval
for all the traces.  The weighting power is given by `n`.

The trace starts at time `t1` and has sample spacing `delta`.
"""
function _stack_phaseweight(a::Vector{<:AbstractArray{T}}, n, delta, b, delay, t1, t2, weights) where T
    N, npts, b_shift, stack_trace = _stack_array(a, delta, b, delay, t1, t2)
    # Collect shifted traces together
    traces = Array{T}(undef, npts, N)
    @inbounds for j in 1:N
        ip1 = round(Int, (t1 - b_shift[j])/delta) + 1
        w = weights[j]
        @simd for i in ip1:(ip1+npts-1)
            traces[i-ip1+1,j] = a[j][i]*w
        end
    end
    # Create instantaneous phases
    phases = DSP.hilbert(traces)
    phases .= phases./abs.(phases)
    # Stack
    @inbounds for i in 1:npts
        weight = abs(sum(phases[i,:])/N)^n
        @simd for j in 1:N
            stack_trace[i] += traces[i,j]*weight/N
        end
    end
    stack_trace
end

"""
    _stack_array(a::Vector{Vector{<:Real}}, delta, b::Vector, delay::Vector, t1, t2) -> N, npts, b_shifted, stack_trace

Return the number of traces `N`, the number of points in the output stack trace,
`npts`, an array of shifted trace start times `b_shifted` and a zeroed array
for the stack itself, `stack_trace`.  This routine also checks that the input
arrays are the required length and no points are missing from the desired stack.
"""
function _stack_array(a::Vector{A}, delta, b, delay, t1, t2) where {A<:AbstractArray}
    N = length(a)
    length(b) == length(delay) == N ||
        throw(ArgumentError("Lengths of `a`, `b` and `delay` must all be the same"))
    t1 < t2 || throw(ArgumentError("`t2` is before or the same as `t1`"))
    b_shift = b .+ delay
    e_shift = b_shift .+ (length.(a) .- 1).*delta
    all(b_shift .<= t1) && all(t2 .<= e_shift) ||
        error("Not all traces have data within time range $(t1):$(t2) s
        range in b: $(extrema(b_shift))
        range in e: $(extrema(e_shift))")
    npts = round(Int, (t2 - t1)/delta) + 1
    stack_trace = zeros(eltype(eltype(a)), npts)
    N, npts, b_shift, stack_trace
end

max_stars_repo_name: anowacki/Beamforming

max_issues_repo_name: anowacki/Beamforming

max_forks_repo_name: anowacki/Beamforming


text:
using Distributed  #this is not needed if Julia has been run with <numberOfCores> >1
@everywhere using ATA
@everywhere cd("examples")
using ATAPlot
ata_model = compact_ata(;
    settings_file = "settings_ata maximin.jl",
    bank_file = "data/bank.csv",
    constraints_file = "constraints.csv",
    overlap_file = "overlap_matrix.csv",
    solver = "siman",
    max_time = 200,
)


text:
using Test
using DataFrames
using XLSX
using UiPathOrchestratorJobSchedulingPlanCreate

# Write your own tests here.
@testset "UiPathOrchestratorJobSchedulingPlanCreate.jl 基本機能" begin
    include(joinpath(@__DIR__, "common.jl"))

    @test scheduleplan[1:schedulcolumn-1,1:schedulcolumn-1] == schedulemaster[1:schedulcolumn-1,1:schedulcolumn-1]
    @test scheduleplan[1,8:9] == schedulemaster[1,8:9]
    @test (robotn,run_unit_time,jobn,timen) == (6,15,10,8)

    output=DataFrames.DataFrame(XLSX.readtable(OutputFilePath, "v0.19.2")...)
    plan,runtime=uipathorchestratorschedulreadjustment(scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)
    plan=adjustedresultcheck(plan,runtime,scheduleplan,robotn,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)
    @test sum(Matrix(plan[:,schedulcolumn:end-1])) == sum(runtime)
    @test Matrix(plan[:,schedulcolumn:end-1]) == Matrix(output[:,schedulcolumn:end-1])
    @test Matrix(plan[1:7,schedulcolumn:end-1]) == Matrix(output[1:7,schedulcolumn:end-1])
    @test Matrix(plan[9:10,schedulcolumn:end-1]) == Matrix(output[9:10,schedulcolumn:end-1])
    @test sum(Matrix(uipathorchestratorschedulrecreate(InputFilePath,"parameters","schedule",plotengine="off")[:,schedulcolumn:end-1])) == sum(runtime)
    @test sum(Matrix(uipathorchestratorschedulrecreate(InputFilePath,"parameters","schedule",plotengine="GR")[:,schedulcolumn:end-1])) == sum(runtime)
    @test sum(Matrix(uipathorchestratorschedulrecreate(InputFilePath,"parameters","schedule",plotengine="それ以外")[:,schedulcolumn:end-1])) == sum(runtime)
    @test uipathorchestratorschedulrecreate(InputFilePath,"parameters","schedule",plotengine="それ以外",checkreturn=true)[2]
    @test createtimeset()[1][1]=="00:00"

    OutputTestFilePath=joinpath(@__DIR__, "TestUiPathOrchestratorJobSchedulingPlan.xlsx")
    uipathorchestratorschedulrecreate(InputFilePath,"parameters","schedule",plotengine="off",planexport=true,ExportExcelFilePath=OutputTestFilePath)
    @test isfile(OutputTestFilePath)
    @test DataFrames.DataFrame(XLSX.readtable(OutputTestFilePath, "REPORT_jobplan")...)[1:5,1:5] == schedulemaster[1:5,1:5]
    rm(OutputTestFilePath)

    exportplan(plan,robotn,run_unit_time,ExcelFilePath=OutputTestFilePath)
    @test isfile(OutputTestFilePath)
    @test DataFrames.DataFrame(XLSX.readtable(OutputTestFilePath, "REPORT_parameters")...)[2,2] == robotn
    @test DataFrames.DataFrame(XLSX.readtable(OutputTestFilePath, "REPORT_jobplan")...)[1:5,1:5] == schedulemaster[1:5,1:5]
    rm(OutputTestFilePath)

#=    # PlotlyJSを別途追加
    using Pkg
    Pkg.add("PlotlyJS")
    Pkg.add("Blink")
    Pkg.add("ORCA")
    using PlotlyJS
    @test sum(Matrix(uipathorchestratorschedulrecreate(InputFilePath,"parameters","schedule",plotengine="PlotlyJS")[:,schedulcolumn:end-1])) == sum(runtime)
=#
end

@testset "ジョブスケジュール作成失敗の場合のテスト" begin
    include(joinpath(@__DIR__, "common.jl"))
    robotn=1
   plan,runtime=uipathorchestratorschedulreadjustment(scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)
    @test Matrix(adjustedresultcheck(plan,runtime,scheduleplan,robotn,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)[:,schedulcolumn:end-1] ) == zeros(Int,jobn,timen)
end

@testset "特定範囲内に収める" begin
    include(joinpath(@__DIR__, "common.jl"))
    scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow=readprerequisite(InputFilePath,"parameters","schedule2")
    robotn=1
    plan,runtime=uipathorchestratorschedulreadjustment(scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)
    @test sum(Matrix(adjustedresultcheck(plan,runtime,scheduleplan,robotn,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)[:,schedulcolumn:end-1] ) )== sum(runtime)
end

@testset "ブロックタイム設定" begin
    include(joinpath(@__DIR__, "common.jl"))
    scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow=readprerequisite(InputFilePath,"parameters","schedule3")
    robotn=2
    plan,runtime=uipathorchestratorschedulreadjustment(scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)
    @test sum(Matrix(adjustedresultcheck(plan,runtime,scheduleplan,robotn,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)[:,schedulcolumn:end-1] ) )== sum(runtime)

    # ブロックタイム内に実行指定がある場合
    scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow=readprerequisite(InputFilePath,"parameters","schedule4")
    robotn=2
    blocktime_dow="schedule4"
    plan,runtime=uipathorchestratorschedulreadjustment(scheduleplan,robotn,run_unit_time,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)
    @test sum(Matrix(adjustedresultcheck(plan,runtime,scheduleplan,robotn,jobn,timen,schedule,blocktime_start,blocktime_end,blocktime_dow)[:,schedulcolumn:end-1] ) )== 0

end


text:
@testset "UniqueSortIndex" begin
    a = [3.0, 2.0, 8.0, 5.0]
    b = accelerate(a, UniqueSortIndex)

    @test 1.0 ∉ b
    @test 2.0 ∈ b
    @test 3.0 ∈ b
    @test 4.0 ∉ b
    @test 5.0 ∈ b
    @test 8.0 ∈ b
    @test 9.0 ∉ b

    @test count(isequal(1.0), b) == 0
    @test count(isequal(2.0), b) == 1
    @test count(isequal(3.0), b) == 1
    @test count(isequal(4.0), b) == 0
    @test count(isequal(5.0), b) == 1
    @test count(isequal(8.0), b) == 1
    @test count(isequal(9.0), b) == 0

    @test count(isless(1.1), b) == 0
    @test count(isless(2.0), b) == 0
    @test count(isless(2.1), b) == 1
    @test count(isless(3.1), b) == 2
    @test count(isless(8.1), b) == 4

    @test count(islessequal(1.1), b) == 0
    @test count(islessequal(2.0), b) == 1
    @test count(islessequal(2.1), b) == 1
    @test count(islessequal(3.1), b) == 2
    @test count(islessequal(8.1), b) == 4

    @test count(isgreater(1.1), b) == 4
    @test count(isgreater(2.0), b) == 3
    @test count(isgreater(2.1), b) == 3
    @test count(isgreater(3.1), b) == 2
    @test count(isgreater(8.1), b) == 0

    @test count(isgreaterequal(1.1), b) == 4
    @test count(isgreaterequal(2.0), b) == 4
    @test count(isgreaterequal(2.1), b) == 3
    @test count(isgreaterequal(3.1), b) == 2
    @test count(isgreaterequal(8.1), b) == 0

    @test count(in(1.1..1.9), b) == 0
    @test count(in(1.1..2.1), b) == 1
    @test count(in(1.1..3.1), b) == 2
    @test count(in(2.1..3.1), b) == 1

    @test findall(isequal(1.0), b)::MaybeVector == []
    @test findall(isequal(8.0), b)::MaybeVector == [3]
    @test findall(isequal(2.0), b)::MaybeVector == [2]

    @test issetequal(findall(isless(1.1), b), [])
    @test issetequal(findall(isless(2.0), b), [])
    @test issetequal(findall(isless(2.1), b), [2])
    @test issetequal(findall(isless(3.1), b), [1, 2])
    @test issetequal(findall(isless(8.1), b), [1, 2, 3, 4])

    @test issetequal(findall(islessequal(1.1), b), [])
    @test issetequal(findall(islessequal(2.0), b), [2])
    @test issetequal(findall(islessequal(2.1), b), [2])
    @test issetequal(findall(islessequal(3.1), b), [1, 2])
    @test issetequal(findall(islessequal(8.1), b), [1, 2, 3, 4])

    @test issetequal(findall(isgreater(1.1), b), [1, 2, 3, 4])
    @test issetequal(findall(isgreater(2.0), b), [1, 3, 4])
    @test issetequal(findall(isgreater(2.1), b), [1, 3, 4])
    @test issetequal(findall(isgreater(3.1), b), [3, 4])
    @test issetequal(findall(isgreater(8.1), b), [])

    @test issetequal(findall(isgreaterequal(1.1), b), [1, 2, 3, 4])
    @test issetequal(findall(isgreaterequal(2.0), b), [1, 2, 3, 4])
    @test issetequal(findall(isgreaterequal(2.1), b), [1, 3, 4])
    @test issetequal(findall(isgreaterequal(3.1), b), [3, 4])
    @test issetequal(findall(isgreaterequal(8.1), b), [])

    @test issetequal(findall(in(1.1..1.9), b), [])
    @test issetequal(findall(in(1.1..2.1), b), [2])
    @test issetequal(findall(in(1.1..3.1), b), [1, 2])
    @test issetequal(findall(in(2.1..3.1), b), [1])

    @test findfirst(isequal(1.0), b) === nothing
    @test findfirst(isequal(8.0), b) === 3
    @test findfirst(isequal(2.0), b) === 2

    @test findlast(isequal(1.0), b) === nothing
    @test findlast(isequal(8.0), b) === 3
    @test findlast(isequal(2.0), b) === 2

    @test filter(isequal(1.0), b)::MaybeVector == []
    @test filter(isequal(8.0), b)::MaybeVector == [8.0]
    @test filter(isequal(2.0), b)::MaybeVector == [2.0]

    @test issetequal(filter(isless(1.1), b), [])
    @test issetequal(filter(isless(2.0), b), [])
    @test issetequal(filter(isless(2.1), b), [2.0])
    @test issetequal(filter(isless(3.1), b), [2.0, 3.0])
    @test issetequal(filter(isless(8.1), b), [2.0, 3.0, 5.0, 8.0])

    @test issetequal(filter(islessequal(1.1), b), [])
    @test issetequal(filter(islessequal(2.0), b), [2.0])
    @test issetequal(filter(islessequal(2.1), b), [2.0])
    @test issetequal(filter(islessequal(3.1), b), [2.0, 3.0])
    @test issetequal(filter(islessequal(8.1), b), [2.0, 3.0, 5.0, 8.0])

    @test issetequal(filter(isgreater(1.1), b), [2.0, 3.0, 5.0, 8.0])
    @test issetequal(filter(isgreater(2.0), b), [3.0, 5.0, 8.0])
    @test issetequal(filter(isgreater(2.1), b), [3.0, 5.0, 8.0])
    @test issetequal(filter(isgreater(3.1), b), [5.0, 8.0])
    @test issetequal(filter(isgreater(8.1), b), [])

    @test issetequal(filter(isgreaterequal(1.1), b), [2.0, 3.0, 5.0, 8.0])
    @test issetequal(filter(isgreaterequal(2.0), b), [2.0, 3.0, 5.0, 8.0])
    @test issetequal(filter(isgreaterequal(2.1), b), [3.0, 5.0, 8.0])
    @test issetequal(filter(isgreaterequal(3.1), b), [5.0, 8.0])
    @test issetequal(filter(isgreaterequal(8.1), b), [])

    @test issetequal(filter(in(1.1..1.9), b), [])
    @test issetequal(filter(in(1.1..2.1), b), [2.0])
    @test issetequal(filter(in(1.1..3.1), b), [2.0, 3.0])
    @test issetequal(filter(in(2.1..3.1), b), [3.0])

    @test issetequal(innerjoin(identity, identity, tuple, isequal, b, [1.5, 3.0, 8.0]),
                     innerjoin(identity, identity, tuple, isequal, a, [1.5, 3.0, 8.0]))

    c = accelerate!(a, UniqueSortIndex) # a = [2.0, 3.0, 5.0, 8.0]

    @test 1.0 ∉ c
    @test 2.0 ∈ c
    @test 3.0 ∈ c
    @test 4.0 ∉ c
    @test 5.0 ∈ c
    @test 8.0 ∈ c
    @test 9.0 ∉ c

    @test count(isequal(1.0), c) == 0
    @test count(isequal(2.0), c) == 1
    @test count(isequal(3.0), c) == 1
    @test count(isequal(4.0), c) == 0
    @test count(isequal(5.0), c) == 1
    @test count(isequal(8.0), c) == 1
    @test count(isequal(9.0), c) == 0

    @test count(isless(1.1), c) == 0
    @test count(isless(2.0), c) == 0
    @test count(isless(2.1), c) == 1
    @test count(isless(3.1), c) == 2
    @test count(isless(8.1), c) == 4

    @test count(islessequal(1.1), c) == 0
    @test count(islessequal(2.0), c) == 1
    @test count(islessequal(2.1), c) == 1
    @test count(islessequal(3.1), c) == 2
    @test count(islessequal(8.1), c) == 4

    @test count(isgreater(1.1), c) == 4
    @test count(isgreater(2.0), c) == 3
    @test count(isgreater(2.1), c) == 3
    @test count(isgreater(3.1), c) == 2
    @test count(isgreater(8.1), c) == 0

    @test count(isgreaterequal(1.1), c) == 4
    @test count(isgreaterequal(2.0), c) == 4
    @test count(isgreaterequal(2.1), c) == 3
    @test count(isgreaterequal(3.1), c) == 2
    @test count(isgreaterequal(8.1), c) == 0

    @test count(in(1.1..1.9), c) == 0
    @test count(in(1.1..2.1), c) == 1
    @test count(in(1.1..3.1), c) == 2
    @test count(in(2.1..3.1), c) == 1

    @test findall(isequal(1.0), c)::MaybeVector == []
    @test findall(isequal(8.0), c)::MaybeVector == [4]
    @test findall(isequal(2.0), c)::MaybeVector == [1]

    @test issetequal(findall(isless(1.1), c), [])
    @test issetequal(findall(isless(2.0), c), [])
    @test issetequal(findall(isless(2.1), c), [1])
    @test issetequal(findall(isless(3.1), c), [1, 2])
    @test issetequal(findall(isless(8.1), c), [1, 2, 3, 4])

    @test issetequal(findall(islessequal(1.1), c), [])
    @test issetequal(findall(islessequal(2.0), c), [1])
    @test issetequal(findall(islessequal(2.1), c), [1])
    @test issetequal(findall(islessequal(3.1), c), [1, 2])
    @test issetequal(findall(islessequal(8.1), c), [1, 2, 3, 4])

    @test issetequal(findall(isgreater(1.1), c), [1, 2, 3, 4])
    @test issetequal(findall(isgreater(2.0), c), [2, 3, 4])
    @test issetequal(findall(isgreater(2.1), c), [2, 3, 4])
    @test issetequal(findall(isgreater(3.1), c), [3, 4])
    @test issetequal(findall(isgreater(8.1), c), [])

    @test issetequal(findall(isgreaterequal(1.1), c), [1, 2, 3, 4])
    @test issetequal(findall(isgreaterequal(2.0), c), [1, 2, 3, 4])
    @test issetequal(findall(isgreaterequal(2.1), c), [2, 3, 4])
    @test issetequal(findall(isgreaterequal(3.1), c), [3, 4])
    @test issetequal(findall(isgreaterequal(8.1), c), [])

    @test issetequal(findall(in(1.1..1.9), c), [])
    @test issetequal(findall(in(1.1..2.1), c), [1])
    @test issetequal(findall(in(1.1..3.1), c), [1, 2])
    @test issetequal(findall(in(2.1..3.1), c), [2])

    @test findfirst(isequal(1.0), c) === nothing
    @test findfirst(isequal(8.0), c) === 4
    @test findfirst(isequal(2.0), c) === 1

    @test findfirst(isless(1.1), c) === nothing
    @test findfirst(isless(2.0), c) === nothing
    @test findfirst(isless(2.1), c) === 1
    @test findfirst(isless(3.1), c) === 1
    @test findfirst(isless(8.1), c) === 1

    @test findfirst(islessequal(1.1), c) === nothing
    @test findfirst(islessequal(2.0), c) === 1
    @test findfirst(islessequal(2.1), c) === 1
    @test findfirst(islessequal(3.1), c) === 1
    @test findfirst(islessequal(8.1), c) === 1

    @test findfirst(isgreater(1.1), c) === 1
    @test findfirst(isgreater(2.0), c) === 2
    @test findfirst(isgreater(2.1), c) === 2
    @test findfirst(isgreater(3.1), c) === 3
    @test findfirst(isgreater(8.1), c) === nothing

    @test findfirst(isgreaterequal(1.1), c) === 1
    @test findfirst(isgreaterequal(2.0), c) === 1
    @test findfirst(isgreaterequal(2.1), c) === 2
    @test findfirst(isgreaterequal(3.1), c) === 3
    @test findfirst(isgreaterequal(8.1), c) === nothing

    @test findfirst(in(1.1..1.9), c) === nothing
    @test findfirst(in(1.1..2.1), c) === 1
    @test findfirst(in(1.1..3.1), c) === 1
    @test findfirst(in(2.1..3.1), c) === 2

    @test findlast(isequal(1.0), c) === nothing
    @test findlast(isequal(8.0), c) === 4
    @test findlast(isequal(2.0), c) === 1

    @test findlast(isless(1.1), c) === nothing
    @test findlast(isless(2.0), c) === nothing
    @test findlast(isless(2.1), c) === 1
    @test findlast(isless(3.1), c) === 2
    @test findlast(isless(8.1), c) === 4

    @test findlast(islessequal(1.1), c) === nothing
    @test findlast(islessequal(2.0), c) === 1
    @test findlast(islessequal(2.1), c) === 1
    @test findlast(islessequal(3.1), c) === 2
    @test findlast(islessequal(8.1), c) === 4

    @test findlast(isgreater(1.1), c) === 4
    @test findlast(isgreater(2.0), c) === 4
    @test findlast(isgreater(2.1), c) === 4
    @test findlast(isgreater(3.1), c) === 4
    @test findlast(isgreater(8.1), c) === nothing

    @test findlast(isgreaterequal(1.1), c) === 4
    @test findlast(isgreaterequal(2.0), c) === 4
    @test findlast(isgreaterequal(2.1), c) === 4
    @test findlast(isgreaterequal(3.1), c) === 4
    @test findlast(isgreaterequal(8.1), c) === nothing

    @test findlast(in(1.1..1.9), c) === nothing
    @test findlast(in(1.1..2.1), c) === 1
    @test findlast(in(1.1..3.1), c) === 2
    @test findlast(in(2.1..3.1), c) === 2

    @test filter(isequal(1.0), c)::MaybeVector == []
    @test filter(isequal(8.0), c)::MaybeVector == [8.0]
    @test filter(isequal(2.0), c)::MaybeVector == [2.0]

    @test issetequal(filter(isless(1.1), c), [])
    @test issetequal(filter(isless(2.0), c), [])
    @test issetequal(filter(isless(2.1), c), [2.0])
    @test issetequal(filter(isless(3.1), c), [2.0, 3.0])
    @test issetequal(filter(isless(8.1), c), [2.0, 3.0, 5.0, 8.0])

    @test issetequal(filter(islessequal(1.1), c), [])
    @test issetequal(filter(islessequal(2.0), c), [2.0])
    @test issetequal(filter(islessequal(2.1), c), [2.0])
    @test issetequal(filter(islessequal(3.1), c), [2.0, 3.0])
    @test issetequal(filter(islessequal(8.1), c), [2.0, 3.0, 5.0, 8.0])

    @test issetequal(filter(isgreater(1.1), c), [2.0, 3.0, 5.0, 8.0])
    @test issetequal(filter(isgreater(2.0), c), [3.0, 5.0, 8.0])
    @test issetequal(filter(isgreater(2.1), c), [3.0, 5.0, 8.0])
    @test issetequal(filter(isgreater(3.1), c), [5.0, 8.0])
    @test issetequal(filter(isgreater(8.1), c), [])

    @test issetequal(filter(isgreaterequal(1.1), c), [2.0, 3.0, 5.0, 8.0])
    @test issetequal(filter(isgreaterequal(2.0), c), [2.0, 3.0, 5.0, 8.0])
    @test issetequal(filter(isgreaterequal(2.1), c), [3.0, 5.0, 8.0])
    @test issetequal(filter(isgreaterequal(3.1), c), [5.0, 8.0])
    @test issetequal(filter(isgreaterequal(8.1), c), [])

    @test issetequal(filter(in(1.1..1.9), c), [])
    @test issetequal(filter(in(1.1..2.1), c), [2.0])
    @test issetequal(filter(in(1.1..3.1), c), [2.0, 3.0])
    @test issetequal(filter(in(2.1..3.1), c), [3.0])

    @test issetequal(innerjoin(identity, identity, tuple, isequal, c, [1.5, 3.0, 8.0]),
                     innerjoin(identity, identity, tuple, isequal, a, [1.5, 3.0, 8.0]))
end


text:
module LightGraphs

using SimpleTraits

### Remove the following line once #915 is closed
using ArnoldiMethod
using Statistics: mean

using Inflate: InflateGzipStream
using DataStructures: IntDisjointSets, PriorityQueue, dequeue!, dequeue_pair!, enqueue!, heappop!, heappush!, in_same_set, peek, union!
using Distributed: @distributed
using LinearAlgebra: I, Symmetric, diagm, eigen, eigvals, norm, rmul!, tril, triu
import LinearAlgebra: Diagonal, issymmetric, mul!
# import Markdown
using Random: AbstractRNG, GLOBAL_RNG, MersenneTwister, randperm, randsubseq!, seed!, shuffle, shuffle!
using SharedArrays: SharedMatrix, SharedVector, sdata
using SparseArrays: SparseMatrixCSC, nonzeros, nzrange, rowvals
import SparseArrays: blockdiag, sparse

import Base: adjoint, write, ==, <, *, ≈, convert, isless, issubset, union, intersect,
            reverse, reverse!, isassigned, getindex, setindex!, show,
            print, copy, in, sum, size, eltype, length, ndims, transpose,
            join, iterate, eltype, get, Pair, Tuple, zero

export
# Interface
AbstractGraph, AbstractEdge, AbstractEdgeIter,
Edge, Graph, SimpleGraph, SimpleGraphFromIterator, DiGraph, SimpleDiGraphFromIterator,
SimpleDiGraph, vertices, edges, edgetype, nv, ne, src, dst,
is_directed, IsDirected,
has_vertex, has_edge, inneighbors, outneighbors,

# core
is_ordered, add_vertices!, indegree, outdegree, degree,
Δout, Δin, δout, δin, Δ, δ, degree_histogram,
neighbors, all_neighbors, common_neighbors,
has_self_loops, num_self_loops, density, squash, weights,

# simplegraphs
add_edge!, add_vertex!, add_vertices!, rem_edge!, rem_vertex!, rem_vertices!,

# decomposition
core_number, k_core, k_shell, k_crust, k_corona,

# distance
eccentricity, diameter, periphery, radius, center,

# distance between graphs
spectral_distance, edit_distance,

# edit path cost functions
MinkowskiCost, BoundedMinkowskiCost,

# operators
complement, reverse, reverse!, blockdiag, union, intersect,
difference, symmetric_difference,
join, tensor_product, cartesian_product, crosspath,
induced_subgraph, egonet, merge_vertices!, merge_vertices,

# graph visit
AbstractGraphVisitor,
discover_vertex!, close_vertex!,
examine_neighbor!, traverse_graph,

# bfs
gdistances, gdistances!, bfs_tree, bfs_parents, has_path,

# bipartition
is_bipartite, bipartite_map,

# dfs
is_cyclic, topological_sort_by_dfs, dfs_tree,

# random
randomwalk, saw, non_backtracking_randomwalk,

# diffusion
diffusion, diffusion_rate,

# coloring
greedy_color,

# connectivity
connected_components, strongly_connected_components, weakly_connected_components,
is_connected, is_strongly_connected, is_weakly_connected, period,
condensation, attracting_components, neighborhood, neighborhood_dists,
isgraphical,

# cycles
simplecycles_hawick_james, maxsimplecycles, simplecycles, simplecycles_iter,
simplecyclescount, simplecycleslength, karp_minimum_cycle_mean, cycle_basis,
simplecycles_limited_length,

# maximum_adjacency_visit
MaximumAdjacency, AbstractMASVisitor, mincut, maximum_adjacency_visit,

# a-star, dijkstra, bellman-ford, floyd-warshall
a_star, dijkstra_shortest_paths, bellman_ford_shortest_paths,
has_negative_edge_cycle, enumerate_paths, johnson_shortest_paths,
floyd_warshall_shortest_paths, transitiveclosure!, transitiveclosure, transitivereduction,
yen_k_shortest_paths,

# centrality
betweenness_centrality, closeness_centrality, degree_centrality,
indegree_centrality, outdegree_centrality, katz_centrality, pagerank,
eigenvector_centrality, stress_centrality, radiality_centrality,

# spectral
adjacency_matrix, laplacian_matrix, adjacency_spectrum, laplacian_spectrum,
non_backtracking_matrix, incidence_matrix, nonbacktrack_embedding, Nonbacktracking,
contract,

# persistence
loadgraph, loadgraphs, savegraph, LGFormat,

# randgraphs
erdos_renyi, expected_degree_graph, watts_strogatz, random_regular_graph, random_regular_digraph,
random_configuration_model, random_tournament_digraph, StochasticBlockModel, make_edgestream,
nearbipartiteSBM, blockcounts, blockfractions, stochastic_block_model, barabasi_albert,
barabasi_albert!, static_fitness_model, static_scale_free, kronecker, dorogovtsev_mendes, random_orientation_dag,

#community
modularity, core_periphery_deg,
local_clustering,local_clustering_coefficient, global_clustering_coefficient, triangles,
label_propagation, maximal_cliques, clique_percolation,

#generators
CompleteGraph, StarGraph, PathGraph, WheelGraph, CycleGraph,

CompleteBipartiteGraph, CompleteMultipartiteGraph, TuranGraph, CompleteDiGraph, StarDiGraph,
PathDiGraph, Grid, WheelDiGraph, CycleDiGraph, BinaryTree, DoubleBinaryTree, RoachGraph,
CliqueGraph, LadderGraph, CircularLadderGraph, BarbellGraph, LollipopGraph,

#smallgraphs
smallgraph,

# Euclidean graphs
euclidean_graph,

#minimum_spanning_trees
kruskal_mst, prim_mst,

#biconnectivity and articulation points
articulation, biconnected_components, bridges,

#graphcut
normalized_cut, karger_min_cut, karger_cut_cost, karger_cut_edges,

#dominatingset
dominating_set,

#independentset
independent_set,

#vertexcover
vertex_cover

"""
    LightGraphs

An optimized graphs package.

Simple graphs (not multi- or hypergraphs) are represented in a memory- and
time-efficient manner with adjacency lists and edge sets. Both directed and
undirected graphs are supported via separate types, and conversion is available
from directed to undirected.

The project goal is to mirror the functionality of robust network and graph
analysis libraries such as NetworkX while being simpler to use and more
efficient than existing Julian graph libraries such as Graphs.jl. It is an
explicit design decision that any data not required for graph manipulation
(attributes and other information, for example) is expected to be stored
outside of the graph structure itself. Such data lends itself to storage in
more traditional and better-optimized mechanisms.

[Full documentation](http://codecov.io/github/JuliaGraphs/LightGraphs.jl) is available,
and tutorials are available at the
[JuliaGraphsTutorials repository](https://github.com/JuliaGraphs/JuliaGraphsTutorials).
"""
LightGraphs
include("interface.jl")
include("utils.jl")
include("deprecations.jl")
include("core.jl")

include("SimpleGraphs/SimpleGraphs.jl")
using .SimpleGraphs
"""
    Graph

A datastruture representing an undirected graph.
"""
const Graph = LightGraphs.SimpleGraphs.SimpleGraph
"""
    DiGraph

A datastruture representing a directed graph.
"""
const DiGraph = LightGraphs.SimpleGraphs.SimpleDiGraph
"""
    Edge

A datastruture representing an edge between two vertices in
a `Graph` or `DiGraph`.
"""
const Edge = LightGraphs.SimpleGraphs.SimpleEdge

include("degeneracy.jl")
include("digraph/transitivity.jl")
include("cycles/johnson.jl")
include("cycles/hawick-james.jl")
include("cycles/karp.jl")
include("cycles/basis.jl")
include("cycles/limited_length.jl")
include("traversals/bfs.jl")
include("traversals/bipartition.jl")
include("traversals/greedy_color.jl")
include("traversals/dfs.jl")
include("traversals/maxadjvisit.jl")
include("traversals/randomwalks.jl")
include("traversals/diffusion.jl")
include("connectivity.jl")
include("distance.jl")
include("edit_distance.jl")
include("shortestpaths/astar.jl")
include("shortestpaths/bellman-ford.jl")
include("shortestpaths/dijkstra.jl")
include("shortestpaths/johnson.jl")
include("shortestpaths/floyd-warshall.jl")
include("shortestpaths/yen.jl")
include("linalg/LinAlg.jl")
include("operators.jl")
include("persistence/common.jl")
include("persistence/lg.jl")
include("centrality/betweenness.jl")
include("centrality/closeness.jl")
include("centrality/stress.jl")
include("centrality/degree.jl")
include("centrality/katz.jl")
include("centrality/pagerank.jl")
include("centrality/eigenvector.jl")
include("centrality/radiality.jl")
include("community/modularity.jl")
include("community/label_propagation.jl")
include("community/core-periphery.jl")
include("community/clustering.jl")
include("community/cliques.jl")
include("community/clique_percolation.jl")
include("spanningtrees/kruskal.jl")
include("spanningtrees/prim.jl")
include("biconnectivity/articulation.jl")
include("biconnectivity/biconnect.jl")
include("biconnectivity/bridge.jl")
include("graphcut/normalized_cut.jl")
include("graphcut/karger_min_cut.jl")
include("dominatingset/degree_dom_set.jl")
include("dominatingset/minimal_dom_set.jl")
include("independentset/degree_ind_set.jl")
include("independentset/maximal_ind_set.jl")
include("vertexcover/degree_vertex_cover.jl")
include("vertexcover/random_vertex_cover.jl")
include("Experimental/Experimental.jl")
include("Parallel/Parallel.jl")

using .LinAlg
end # module


text:
abstract type InverterComponent <: DynamicComponent end

"""
    mutable struct DynamicInverter{
        C <: Converter,
        O <: OuterControl,
        IC <: InnerControl,
        DC <: DCSource,
        P <: FrequencyEstimator,
        F <: Filter,
    } <: DynamicInjection
        name::String
        ω_ref::Float64
        converter::C
        outer_control::O
        inner_control::IC
        dc_source::DC
        freq_estimator::P
        filter::F
        base_power::Float64
        n_states::Int
        states::Vector{Symbol}
        ext::Dict{String, Any}
        internal::InfrastructureSystemsInternal
    end

A dynamic inverter is composed by 6 components, namely a Converter, an Outer Control, an Inner Control,
a DC Source, a Frequency Estimator and a Filter. It requires a Static Injection device that is attached to it.

# Arguments
- `name::String`: Name of inverter.
- `ω_ref::Float64`: Frequency reference set-point in pu.
- `converter <: Converter`: Converter model for the PWM transformation.
- `outer_control <: OuterControl`: Outer-control controller model.
- `inner_control <: InnerControl`: Inner-control controller model.
- `dc_source <: DCSource`: DC Source model.
- `freq_estimator <: FrequencyEstimator`: Frequency Estimator (typically a PLL) model.
- `filter <: Filter`: Filter model.
- `base_power::Float64`: Base power
- `n_states::Int`: Number of states (will depend on the components).
- `states::Vector{Symbol}`: Vector of states (will depend on the components).
- `ext::Dict{String, Any}`
- `internal::InfrastructureSystemsInternal`: power system internal reference, do not modify
"""
mutable struct DynamicInverter{
    C <: Converter,
    O <: OuterControl,
    IC <: InnerControl,
    DC <: DCSource,
    P <: FrequencyEstimator,
    F <: Filter,
} <: DynamicInjection
    name::String
    ω_ref::Float64
    converter::C
    outer_control::O
    inner_control::IC
    dc_source::DC
    freq_estimator::P
    filter::F
    base_power::Float64
    n_states::Int
    states::Vector{Symbol}
    ext::Dict{String, Any}
    internal::InfrastructureSystemsInternal
end

function DynamicInverter(
    name::String,
    ω_ref::Float64,
    converter::C,
    outer_control::O,
    inner_control::IC,
    dc_source::DC,
    freq_estimator::P,
    filter::F,
    base_power::Float64 = 100.0,
    ext::Dict{String, Any} = Dict{String, Any}(),
) where {
    C <: Converter,
    O <: OuterControl,
    IC <: InnerControl,
    DC <: DCSource,
    P <: FrequencyEstimator,
    F <: Filter,
}
    n_states = _calc_n_states(
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        filter,
    )
    states = _calc_states(
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        filter,
    )

    return DynamicInverter{C, O, IC, DC, P, F}(
        name,
        ω_ref,
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        filter,
        base_power,
        n_states,
        states,
        ext,
        InfrastructureSystemsInternal(),
    )
end

function DynamicInverter(;
    name::String,
    ω_ref::Float64,
    converter::C,
    outer_control::O,
    inner_control::IC,
    dc_source::DC,
    freq_estimator::P,
    filter::F,
    base_power::Float64 = 100.0,
    n_states = _calc_n_states(
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        filter,
    ),
    states = _calc_states(
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        filter,
    ),
    ext::Dict{String, Any} = Dict{String, Any}(),
    internal = IS.InfrastructureSystemsInternal(),
) where {
    C <: Converter,
    O <: OuterControl,
    IC <: InnerControl,
    DC <: DCSource,
    P <: FrequencyEstimator,
    F <: Filter,
}
    return DynamicInverter(
        name,
        ω_ref,
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        filter,
        base_power,
        n_states,
        states,
        ext,
        internal,
    )
end

function DynamicInverter(
    name::String,
    ω_ref::Float64,
    converter::C,
    active_power_control::AC,
    reactive_power_control::RC,
    inner_control::IC,
    dc_source::DC,
    freq_estimator::P,
    ac_filter::F,
) where {
    C <: Converter,
    AC <: ActivePowerControl,
    RC <: ReactivePowerControl,
    IC <: InnerControl,
    DC <: DCSource,
    P <: FrequencyEstimator,
    F <: Filter,
}
    outer_control = OuterControl(active_power_control, reactive_power_control)
    return DynamicInverter(
        name,
        ω_ref,
        converter,
        outer_control,
        inner_control,
        dc_source,
        freq_estimator,
        ac_filter,
    )
end

get_name(device::DynamicInverter) = device.name
get_ω_ref(device::DynamicInverter) = device.ω_ref
get_ext(device::DynamicInverter) = device.ext
get_states(device::DynamicInverter) = device.states
get_n_states(device::DynamicInverter) = device.n_states
get_converter(device::DynamicInverter) = device.converter
get_outer_control(device::DynamicInverter) = device.outer_control
get_inner_control(device::DynamicInverter) = device.inner_control
get_dc_source(device::DynamicInverter) = device.dc_source
get_freq_estimator(device::DynamicInverter) = device.freq_estimator
get_filter(device::DynamicInverter) = device.filter
get_base_power(device::DynamicInverter) = device.base_power
get_internal(device::DynamicInverter) = device.internal
get_P_ref(value::DynamicInverter) = get_P_ref(get_active_power(get_outer_control(value)))
get_V_ref(value::DynamicInverter) = get_V_ref(get_reactive_power(get_outer_control(value)))

set_base_power!(value::DynamicInverter, val) = value.base_power = val

function _calc_n_states(
    converter,
    outer_control,
    inner_control,
    dc_source,
    freq_estimator,
    filter,
)
    return converter.n_states +
           outer_control.n_states +
           inner_control.n_states +
           dc_source.n_states +
           freq_estimator.n_states +
           filter.n_states
end

function _calc_states(
    converter,
    outer_control,
    inner_control,
    dc_source,
    freq_estimator,
    filter,
)
    return vcat(
        converter.states,
        outer_control.states,
        inner_control.states,
        dc_source.states,
        freq_estimator.states,
        filter.states,
    )
end

function get_REControlB_states(Q_Flag::Int)
    if Q_Flag == 0
        return [:Vt_filt, :I_icv]
    elseif Q_Flag == 1
        return [:Vt_filt, :ξ_icv]
    else
        error("Unsupported value of Q_Flag")
    end
end

function get_activeRETypeAB_states(Freq_Flag::Int)
    if Freq_Flag == 1
        return [:p_flt, :ξ_P, :p_ext, :p_ord], 4
    elseif Freq_Flag == 0
        return [:p_ord], 1
    else
        error("Unsupported value of Freq_Flag")
    end
end

function get_reactiveRETypeAB_states(Ref_Flag::Int, PF_Flag::Int, V_Flag::Int)
    if (Ref_Flag == 0) && ((PF_Flag == 1) && (V_Flag == 1))
        return [:pr_flt, :ξ_Q], 2
    elseif (Ref_Flag == 0) && ((PF_Flag == 1) && (V_Flag == 0))
        return [:pr_flt], 1
    elseif (Ref_Flag == 0) && ((PF_Flag == 0) && (V_Flag == 1))
        return [:q_flt, :ξq_oc, :q_LL, :ξ_Q], 4
    elseif (Ref_Flag == 0) && ((PF_Flag == 0) && (V_Flag == 0))
        return [:q_flt, :ξq_oc, :q_LL], 3
    elseif (Ref_Flag == 1) && ((PF_Flag == 1) && (V_Flag == 1))
        return [:pr_flt, :ξ_Q], 2
    elseif (Ref_Flag == 1) && ((PF_Flag == 1) && (V_Flag == 0))
        return [:pr_flt], 1
    elseif (Ref_Flag == 1) && ((PF_Flag == 0) && (V_Flag == 1))
        return [:V_cflt, :ξq_oc, :q_LL, :ξ_Q], 4
    elseif (Ref_Flag == 1) && ((PF_Flag == 0) && (V_Flag == 0))
        return [:V_cflt, :ξq_oc, :q_LL], 3
    else
        error("Unsupported value of Ref_Flag, PF_Flag or V_Flag")
    end
end


text:
__precompile__()
module Enrichr

import JSON
using DataFrames
using PyCall

const requests = PyNULL()

function __init__()
    copy!(requests, pyimport("requests"))
end

export enrichr_genesets, load_genelist, view_genelist, enrichment
include("enrichr_term_lists.jl")
include("enrichr_api.jl")

end


text:
# This file is a part of DataDeps.jl. License is MIT.
# Code for doing checksums etc.

##################
# Terminal methods

"""
    run_checksum(checksum, path)

THis runs the checksum on the files at the fetched_path.
And returns true or false base on if the checksum matchs.
(always true if no target sum given)
It is kinda flexible and accepts different kinds of behavour
to give different kinds of results.

If path (the second parameter) is a Vector,
then unless checksum is also a Vector,
the result is the xor of the all the file checksums.
"""
function run_checksum(hash::Tuple{<:Any, <:AbstractString}, path)
    hasher, target = hash
    hexchecksum(hasher, path) == target
end

"""
Use `Any` to mark as not caring about the hash.
Use this for data that can change
"""
run_checksum(::Type{Any}, path) =  true

"""
If only a function is provided then assume the user is a developer,
wanting to know what hash-line to add to the Registration line.
"""
function run_checksum(hasher, path)
    res = hexchecksum(hasher, path)
    @warn("Checksum not provided, add to the Datadep Registration the following hash line",
          hash = repr(hasher==sha2_256 ? res : (hasher, res))
         )
    return true
end

##############
# Non terminal methods
# These evetually redirect to one of the terminal methods

"""
Providing only a hash string,
results in defaulting to sha2_256,
with that string being the target
"""
run_checksum(hash::AbstractString, path) = run_checksum((sha2_256, hash), path)

"""
If `nothing` is provided then assume the user is a developer,
wanting to know what sha2_256 hash-line to add to the Registration line.
"""
run_checksum(::Nothing, path) = run_checksum(sha2_256, path)

"""
If a vector of paths is provided
and a vector of hashing methods (of any form)
then they are all required to match.
"""
function run_checksum(hash::AbstractVector, path::AbstractVector)
    all(run_checksum.(hash, path))
end

######################
# Actual executor

"""
    checksum(hasher=sha2_256, filename[/s])

Executes the hasher, on the file/files,
and returns a UInt8 array of the hash.
xored if there are multiple files
"""
checksum(hasher, filename) = open(hasher, filename, "r")
checksum(hasher, filenames::AbstractVector) = xor.(checksum.(hasher, filenames)...)

hexchecksum(hasher, filename) = bytes2hex(checksum(hasher, filename))


text:
using Random
using ForwardDiff
using LinearAlgebra

const ∇ = ForwardDiff.gradient

function DSGD!(net::Chain, c::Function, traindata::AbstractArray, testdata::AbstractArray, epochs::Integer, minibatchsize::Integer; η_max::Real=50)
    t = test(net, testdata)
    n = length(traindata)
    η = η_max
    for i in 1:epochs
        minibatchs = [shuffle!(traindata)[k-minibatchsize+1:k] for k in minibatchsize:minibatchsize:n]
        for minibatch in minibatchs
            GD!(net, c, minibatch, η=η)
        end
        nt = test(net, testdata)
        println(nt)
        if nt <= t
            η /= 2
        end
        t = nt
    end
    η
end

function test(net::Chain, testdata::AbstractArray)
    count = 0
    for xy in testdata
        py = net(xy[1])
        findmax(xy[2])[2] == findmax(py)[2] && (count+=1)
    end
    return count/length(testdata)
end

function SGD!(net::Chain, c::Function, traindata::AbstractArray, epochs::Integer, minibatchsize::Integer; η::Real=1)
    n = length(traindata)
    for i in 1:epochs
        minibatchs = [shuffle!(traindata)[k-minibatchsize+1:k] for k in minibatchsize:minibatchsize:n]
        for minibatch in minibatchs
            GD!(net, c, minibatch, η=η)
        end
    end
end

function GD!(net::Chain, c::Function, traindata::AbstractArray, epochs::Integer; η::Real=1)
    for i in 1:epochs
        GD!(net, c, traindata, η=η)
    end
end

function GD!(net::Chain, c::Function, traindata::AbstractArray; η::Real=1)
    dW = [zeros(size(l.W)) for l in net.layers]
    db = [zeros(size(l.b)) for l in net.layers]
    for xy in traindata
        ∂b, ∂W = backprop(net, c, xy...)
        for i in eachindex(dW)
            dW[i] += ∂W[i]
            db[i] += ∂b[i]
        end
    end
    for i in eachindex(dW)
        net.layers[i].W .-= dW[i]*η/length(traindata)
        net.layers[i].b .-= db[i]*η/length(traindata)
    end
end

function backprop(net::Chain, c::Function, x::Vector{R}, y::Vector{R})where R<:Real
    a = x
    as = Vector{Vector{R}}()
    zs = Vector{Vector{R}}()
    push!(as, deepcopy(a))
    for l in net.layers
        z = l(a)
        a = l.f.(z)
        push!(zs, z)
        push!(as, deepcopy(a))
    end
    δs = similar(zs)
    δs[end] = ∇(a->cost(c, a, y), as[end]) .* dσ.(zs[end])
    for i in length(net.layers)-1
        δs[end-i] = transpose(net.layers[end-i+1].W) * δs[end-i+1] .* dσ.(zs[end-i])
    end
    ∂W = Vector{Matrix{R}}()
    for k in eachindex(δs)
        push!(∂W, zeros(length(δs[k]), length(as[k])))
        for i in eachindex(δs[k]), j in eachindex(as[k])
            ∂W[k][i, j] = δs[k][i] * as[k][j]
        end
    end
    return δs, ∂W # δs equals to δs
end

function cost(c::Function, a::Vector{R}, y::Vector{R})where R<:Real
    return sum(c, y.-a)/length(a)
end