text:
module test_jive_useinside

using Test
using Jive

module A
val_in_A = 1
end

ret = @useinside module B
val_in_B = 1
42
end

@test val_in_B == 1
@test ret == 42

end # module test_jive_useinside

module test_jive_useinside_isdefined

using Test
using ..test_jive_useinside

@test !isdefined(Main, :test_jive_useinside)
@test isdefined(test_jive_useinside, :A)
@test !isdefined(test_jive_useinside, :B)

end # module test_jive_useinside_isdefined


text:
using PyPlot
using PyCall
using ADCME
using DifferentialEquations

function generate_data(n, Δt)
    function lorenz(du,u,p,t)
        du[1] = sin(u[2])
        du[2] = 2*cos(u[1])
    end

    t = n*Δt
    u0 = [0.0;0.0]
    tspan = (0,t)
    prob = ODEProblem(lorenz,u0,tspan)
    sol = DifferentialEquations.solve(prob)
    sol = sol( (0:n-1)*Δt )
    hcat(sol.u...)[1,:], hcat(sol.u...)[2,:], collect((0:n-1)*Δt)
end

function nn1(t0, t1, x, y)
    ipt = reshape([t0;t1;x;y], 1,:)
    out = ae(ipt, [20,2], "nn1")
    out[1,1], out[1,2]
end

function nn2(t0, t1, x, y)
    ipt = reshape([t0;t1;x;y], 1,:)
    out = ae(ipt,[20,2], "nn2")
    out = reshape([x;y],1,2) + (t1-t0)*out
    out[1,1], out[1,2]
end

function loss(xset, yset, tset, nn)
    n = size(xset,1)
    m = size(xset,2)
    loss = constant(0.0)
    for i = 1:m
        t = tset[:,i]
        x = xset[:,i]
        y = yset[:,i]
        for j = 2:n
            @show j
            x_, y_ = nn(t[j-1], t[j], x[j-1], y[j-1])
            loss += (x[j]-x_)^2+(y[j]-y_)^2
        end
    end
    loss
end

function predict(xset, yset, tset, nn)
    t = convert_to_tensor(tset)
    x = convert_to_tensor(xset)
    y = convert_to_tensor(yset)
    resx = [convert_to_tensor(x[1])]
    resy = [convert_to_tensor(y[1])]
    x_, y_ = x[1], y[1]
    for j = 2:length(tset)
        x_, y_ = nn(t[j-1], t[j], x_, y_)
        push!(resx, x_)
        push!(resy, y_)
    end
    vcat(resx...), vcat(resy...)
end
reset_default_graph()
x1, y1, t1 = generate_data(200, 0.15)
x2, y2, t2 = generate_data(200, 0.075)
y1 = y1 .* ( 1 .+ 0.01*randn(length(y1)))
y2 = y2 .* ( 1 .+ 0.01*randn(length(y2)))
# l1 = loss([x1 x2], [y1 y2], [t1 t2], nn1)
# l2 = loss([x1 x2], [y1 y2], [t1 t2], nn2)

l1 = loss(x1, y1, t1, nn1)
l2 = loss(x1, y1, t1, nn2)

v1 = predict(x1, y1, t1, nn1)
v2 = predict(x1, y1, t1, nn2)
v3 = predict(x2, y2, t2, nn1)
v4 = predict(x2, y2, t2, nn2)

sess = Session(); ADCME.init(sess)
BFGS!(sess, l1)
BFGS!(sess, l2)

close("all")
figure(figsize=(12,5))

subplot(121)
pred_x1, pred_y1 = run(sess,v1[1]), run(sess, v1[2])
pred_x2, pred_y2 = run(sess,v2[1]), run(sess, v2[2])
plot(pred_x1, pred_y1, "*-", label="Direct")
plot(pred_x2, pred_y2, "+-", label="Derivative")
plot(x1, y1,"--", label="Exact")
legend()
axis("equal")
title("Δt = $(t1[2]-t1[1])")

subplot(122)
pred_x1, pred_y1 = run(sess,v3[1]), run(sess, v3[2])
pred_x2, pred_y2 = run(sess,v4[1]), run(sess, v4[2])
plot(pred_x1, pred_y1, "*-", label="Direct")
plot(pred_x2, pred_y2, "+-", label="Derivative")
plot(x2, y2,"--", label="Exact")
legend()
axis("equal")
title("Δt = $(t2[2]-t2[1])")

ext: jl


text:
using OrdinaryDiffEq
using Trixi

###############################################################################
# semidiscretization of the compressible ideal GLM-MHD equations

gamma = 5/3
equations = IdealGlmMhdEquations2D(gamma)

initial_condition = initial_condition_convergence_test

# Get the DG approximation space
volume_flux = (flux_central, flux_nonconservative_powell)
solver = DGSEM(polydeg=3, surface_flux=(flux_lax_friedrichs, flux_nonconservative_powell),
               volume_integral=VolumeIntegralFluxDifferencing(volume_flux))

# Get the curved quad mesh from a mapping function
# Mapping as described in https://arxiv.org/abs/1809.01178
function mapping(xi_, eta_)
  # Transform input variables between -1 and 1 onto [0, sqrt(2)]
  # Note, we use the domain [0, sqrt(2)]^2 for the Alfvén wave convergence test case
  xi = 0.5 * sqrt(2) * xi_ + 0.5 * sqrt(2)
  eta = 0.5 * sqrt(2) * eta_ + 0.5 * sqrt(2)

  y = eta + sqrt(2)/12 * (cos(1.5 * pi * (2 * xi - sqrt(2))/sqrt(2)) *
                          cos(0.5 * pi * (2 * eta - sqrt(2))/sqrt(2)))

  x = xi + sqrt(2)/12 * (cos(0.5 * pi * (2 * xi - sqrt(2))/sqrt(2)) *
                         cos(2 * pi * (2 * y - sqrt(2))/sqrt(2)))

  return SVector(x, y)
end

cells_per_dimension = (4, 4)
mesh = StructuredMesh(cells_per_dimension, mapping)

# create the semi discretization object
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)

###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 2.0)
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
                                     extra_analysis_integrals=(entropy, energy_total,
                                                               energy_kinetic, energy_internal,
                                                               energy_magnetic, cross_helicity))

alive_callback = AliveCallback(analysis_interval=analysis_interval)

save_solution = SaveSolutionCallback(interval=100,
                                     save_initial_solution=true,
                                     save_final_solution=true,
                                     solution_variables=cons2prim)
cfl = 2.0
stepsize_callback = StepsizeCallback(cfl=cfl)

glm_speed_callback = GlmSpeedCallback(glm_scale=0.5, cfl=cfl)

callbacks = CallbackSet(summary_callback,
                        analysis_callback,
                        alive_callback,
                        save_solution,
                        stepsize_callback,
                        glm_speed_callback)

###############################################################################
# run the simulation

sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
            dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
            save_everystep=false, callback=callbacks);
summary_callback() # print the timer summary


text:
if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@optlevel"))
    @eval Base.Experimental.@optlevel 1
end

using FileWatching, REPL, Distributed, UUIDs
import LibGit2
using Base: PkgId
using Base.Meta: isexpr
using Core: CodeInfo

export revise, includet, entr, MethodSummary

"""
    Revise.watching_files[]

Returns `true` if we watch files rather than their containing directory.
FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch
directories.
"""
const watching_files = Ref(Sys.KERNEL === :FreeBSD)

"""
    Revise.polling_files[]

Returns `true` if we should poll the filesystem for changes to the files that define
loaded code. It is preferable to avoid polling, instead relying on operating system
notifications via `FileWatching.watch_file`. However, NFS-mounted
filesystems (and perhaps others) do not support file-watching, so for code stored
on such filesystems you should turn polling on.

See the documentation for the `JULIA_REVISE_POLL` environment variable.
"""
const polling_files = Ref(false)
function wait_changed(file)
    try
        polling_files[] ? poll_file(file) : watch_file(file)
    catch err
        if Sys.islinux() && err isa Base.IOError && err.code == -28  # ENOSPC
            @warn """Your operating system has run out of inotify capacity.
            Check the current value with `cat /proc/sys/fs/inotify/max_user_watches`.
            Set it to a higher level with, e.g., `echo 65536 | sudo tee -a /proc/sys/fs/inotify/max_user_watches`.
            This requires having administrative privileges on your machine (or talk to your sysadmin).
            See https://github.com/timholy/Revise.jl/issues/26 for more information."""
        end
        rethrow(err)
    end
    return nothing
end

"""
    Revise.tracking_Main_includes[]

Returns `true` if files directly included from the REPL should be tracked.
The default is `false`. See the documentation regarding the `JULIA_REVISE_INCLUDE`
environment variable to customize it.
"""
const tracking_Main_includes = Ref(false)

include("relocatable_exprs.jl")
include("types.jl")
include("utils.jl")
include("parsing.jl")
include("lowered.jl")
include("pkgs.jl")
include("git.jl")
include("recipes.jl")
include("logging.jl")
include("callbacks.jl")

### Globals to keep track of state

"""
    Revise.watched_files

Global variable, `watched_files[dirname]` returns the collection of files in `dirname`
that we're monitoring for changes. The returned value has type [`Revise.WatchList`](@ref).

This variable allows us to watch directories rather than files, reducing the burden on
the OS.
"""
const watched_files = Dict{String,WatchList}()

"""
    Revise.revision_queue

Global variable, `revision_queue` holds `(pkgdata,filename)` pairs that we need to revise, meaning
that these files have changed since we last processed a revision.
This list gets populated by callbacks that watch directories for updates.
"""
const revision_queue = Set{Tuple{PkgData,String}}()

"""
    Revise.queue_errors

Global variable, maps `(pkgdata, filename)` pairs that errored upon last revision to
`(exception, backtrace)`.
"""
const queue_errors = Dict{Tuple{PkgData,String},Tuple{Exception, Any}}()

"""
    Revise.NOPACKAGE

Global variable; default `PkgId` used for files which do not belong to any
package, but still have to be watched because user callbacks have been
registered for them.
"""
const NOPACKAGE = PkgId(nothing, "")

"""
    Revise.pkgdatas

`pkgdatas` is the core information that tracks the relationship between source code
and julia objects, and allows re-evaluation of code in the proper module scope.
It is a dictionary indexed by PkgId:
`pkgdatas[id]` returns a value of type [`Revise.PkgData`](@ref).
"""
const pkgdatas = Dict{PkgId,PkgData}(NOPACKAGE => PkgData(NOPACKAGE))

const moduledeps = Dict{Module,DepDict}()
function get_depdict(mod::Module)
    if !haskey(moduledeps, mod)
        moduledeps[mod] = DepDict()
    end
    return moduledeps[mod]
end

"""
    Revise.included_files

Global variable, `included_files` gets populated by callbacks we register with `include`.
It's used to track non-precompiled packages and, optionally, user scripts (see docs on
`JULIA_REVISE_INCLUDE`).
"""
const included_files = Tuple{Module,String}[]  # (module, filename)

"""
    Revise.basesrccache

Full path to the running Julia's cache of source code defining `Base`.
"""
const basesrccache = normpath(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base.cache"))

"""
    Revise.basebuilddir

Julia's top-level directory when Julia was built, as recorded by the entries in
`Base._included_files`.
"""
const basebuilddir = begin
    sysimg = filter(x->endswith(x[2], "sysimg.jl"), Base._included_files)[1][2]
    dirname(dirname(sysimg))
end

"""
    Revise.juliadir

Constant specifying full path to julia top-level source directory.
This should be reliable even for local builds, cross-builds, and binary installs.
"""
const juliadir = begin
    local jldir = basebuilddir
    try
        isdir(joinpath(jldir, "base")) || throw(ErrorException("$(jldir) does not have \"base\""))
    catch
        # Binaries probably end up here. We fall back on Sys.BINDIR
        jldir = joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia")
        if !isdir(joinpath(jldir, "base"))
            while true
                trydir = joinpath(jldir, "base")
                isdir(trydir) && break
                trydir = joinpath(jldir, "share", "julia", "base")
                if isdir(trydir)
                    jldir = joinpath(jldir, "share", "julia")
                    break
                end
                jldirnext = dirname(jldir)
                jldirnext == jldir && break
                jldir = jldirnext
            end
        end
    end
    normpath(jldir)
end
const cache_file_key = Dict{String,String}() # corrected=>uncorrected filenames
const src_file_key   = Dict{String,String}() # uncorrected=>corrected filenames

"""
    Revise.dont_watch_pkgs

Global variable, use `push!(Revise.dont_watch_pkgs, :MyPackage)` to prevent Revise
from tracking changes to `MyPackage`. You can do this from the REPL or from your
`.julia/config/startup.jl` file.

See also [`Revise.silence`](@ref).
"""
const dont_watch_pkgs = Set{Symbol}()
const silence_pkgs = Set{Symbol}()
const depsdir = joinpath(dirname(@__DIR__), "deps")
const silencefile = Ref(joinpath(depsdir, "silence.txt"))  # Ref so that tests don't clobber

##
## The inputs are sets of expressions found in each file.
## Some of those expressions will generate methods which are identified via their signatures.
## From "old" expressions we know their corresponding signatures, but from "new"
## expressions we have not yet computed them. This makes old and new asymmetric.
##
## Strategy:
## - For every old expr not found in the new ones,
##     + delete the corresponding methods (using the signatures we've previously computed)
##     + remove the sig entries from CodeTracking.method_info  (")
##   Best to do all the deletion first (across all files and modules) in case a method is
##   simply being moved from one file to another.
## - For every new expr found among the old ones,
##     + update the location info in CodeTracking.method_info
## - For every new expr not found in the old ones,
##     + eval the expr
##     + extract signatures
##     + add to the ModuleExprsSigs
##     + add to CodeTracking.method_info
##
## Interestingly, the ex=>sigs link may not be the same as the sigs=>ex link.
## Consider a conditional block,
##     if Sys.islinux()
##         f() = 1
##         g() = 2
##     else
##         g() = 3
##     end
## From the standpoint of Revise's diff-and-patch functionality, we should look for
## diffs in this entire block. (Really good backedge support---or a variant of `lower` that
## links back to the specific expression---might change this, but for
## now this is the right strategy.) From the standpoint of CodeTracking, we should
## link the signature to the actual method-defining expression (either :(f() = 1) or :(g() = 2)).

if isdefined(Core, :MethodMatch)
    get_method_from_match(mm::Core.MethodMatch) = mm.method
else
    get_method_from_match(mm::Core.SimpleVector) = mm[3]::Method
end

function delete_missing!(exs_sigs_old::ExprsSigs, exs_sigs_new)
    with_logger(_debug_logger) do
        for (ex, sigs) in exs_sigs_old
            haskey(exs_sigs_new, ex) && continue
            # ex was deleted
            sigs === nothing && continue
            for sig in sigs
                ret = Base._methods_by_ftype(sig, -1, typemax(UInt))
                success = false
                if !isempty(ret)
                    m = get_method_from_match(ret[end])   # the last method returned is the least-specific that matches, and thus most likely to be type-equal
                    methsig = m.sig
                    if sig <: methsig && methsig <: sig
                        locdefs = get(CodeTracking.method_info, sig, nothing)
                        if isa(locdefs, Vector{Tuple{LineNumberNode,Expr}})
                            if length(locdefs) > 1
                                # Just delete this reference but keep the method
                                line = firstline(ex)
                                ld = map(pr->linediff(line, pr[1]), locdefs)
                                idx = argmin(ld)
                                @assert ld[idx] < typemax(eltype(ld))
                                deleteat!(locdefs, idx)
                                continue
                            else
                                @assert length(locdefs) == 1
                            end
                        end
                        @debug "DeleteMethod" _group="Action" time=time() deltainfo=(sig, MethodSummary(m))
                        # Delete the corresponding methods
                        for p in workers()
                            try  # guard against serialization errors if the type isn't defined on the worker
                                remotecall(Core.eval, p, Main, :(delete_method_by_sig($sig)))
                            catch
                            end
                        end
                        Base.delete_method(m)
                        # Remove the entries from CodeTracking data
                        delete!(CodeTracking.method_info, sig)
                        # Remove frame from JuliaInterpreter, if applicable. Otherwise debuggers
                        # may erroneously work with outdated code (265-like problems)
                        if haskey(JuliaInterpreter.framedict, m)
                            delete!(JuliaInterpreter.framedict, m)
                        end
                        if isdefined(m, :generator)
                            # defensively delete all generated functions
                            empty!(JuliaInterpreter.genframedict)
                        end
                        success = true
                    end
                end
                if !success
                    @debug "FailedDeletion" _group="Action" time=time() deltainfo=(sig,)
                end
            end
        end
    end
    return exs_sigs_old
end

const empty_exs_sigs = ExprsSigs()
function delete_missing!(mod_exs_sigs_old::ModuleExprsSigs, mod_exs_sigs_new)
    for (mod, exs_sigs_old) in mod_exs_sigs_old
        exs_sigs_new = get(mod_exs_sigs_new, mod, empty_exs_sigs)
        delete_missing!(exs_sigs_old, exs_sigs_new)
    end
    return mod_exs_sigs_old
end

function eval_rex(rex::RelocatableExpr, exs_sigs_old::ExprsSigs, mod::Module; mode::Symbol=:eval)
    sigs, includes = nothing, nothing
    with_logger(_debug_logger) do
        rexo = getkey(exs_sigs_old, rex, nothing)
        # extract the signatures and update the line info
        if rexo === nothing
            ex = rex.ex
            # ex is not present in old
            @debug "Eval" _group="Action" time=time() deltainfo=(mod, ex)
            sigs, deps, includes, thunk = eval_with_signatures(mod, ex; mode=mode)  # All signatures defined by `ex`
            if VERSION < v"1.3.0" || !isexpr(thunk, :thunk)
                thunk = ex
            end
            if myid() == 1
                for p in workers()
                    p == myid() && continue
                    try   # don't error if `mod` isn't defined on the worker
                        remotecall(Core.eval, p, mod, thunk)
                    catch
                    end
                end
            end
            storedeps(deps, rex, mod)
        else
            sigs = exs_sigs_old[rexo]
            # Update location info
            ln, lno = firstline(unwrap(rex)), firstline(unwrap(rexo))
            if sigs !== nothing && !isempty(sigs) && ln != lno
                @debug "LineOffset" _group="Action" time=time() deltainfo=(sigs, lno=>ln)
                for sig in sigs
                    locdefs = CodeTracking.method_info[sig]
                    ld = map(pr->linediff(lno, pr[1]), locdefs)
                    idx = argmin(ld)
                    if ld[idx] === typemax(eltype(ld))
                        # println("Missing linediff for $lno and $(first.(locdefs)) with ", rex.ex)
                        idx = length(locdefs)
                    end
                    methloc, methdef = locdefs[idx]
                    locdefs[idx] = (newloc(methloc, ln, lno), methdef)
                end
            end
        end
    end
    return sigs, includes
end

# These are typically bypassed in favor of expression-by-expression evaluation to
# allow handling of new `include` statements.
function eval_new!(exs_sigs_new::ExprsSigs, exs_sigs_old, mod::Module; mode::Symbol=:eval)
    includes = Vector{Pair{Module,String}}()
    for rex in keys(exs_sigs_new)
        sigs, _includes = eval_rex(rex, exs_sigs_old, mod; mode=mode)
        if sigs !== nothing
            exs_sigs_new[rex] = sigs
        end
        if _includes !== nothing
            append!(includes, _includes)
        end
    end
    return exs_sigs_new, includes
end

function eval_new!(mod_exs_sigs_new::ModuleExprsSigs, mod_exs_sigs_old; mode::Symbol=:eval)
    includes = Vector{Pair{Module,String}}()
    for (mod, exs_sigs_new) in mod_exs_sigs_new
        # Allow packages to override the supplied mode
        if isdefined(mod, :__revise_mode__)
            mode = getfield(mod, :__revise_mode__)::Symbol
        end
        exs_sigs_old = get(mod_exs_sigs_old, mod, empty_exs_sigs)
        _, _includes = eval_new!(exs_sigs_new, exs_sigs_old, mod; mode=mode)
        append!(includes, _includes)
    end
    return mod_exs_sigs_new, includes
end

"""
    CodeTrackingMethodInfo(ex::Expr)

Create a cache for storing information about method definitions.
Adding signatures to such an object inserts them into `CodeTracking.method_info`,
which maps signature Tuple-types to `(lnn::LineNumberNode, ex::Expr)` pairs.
Because method signatures are unique within a module, this is the foundation for
identifying methods in a manner independent of source-code location.

It also has the following fields:

- `exprstack`: used when descending into `@eval` statements (via `push_expr` and `pop_expr!`)
  `ex` (used in creating the `CodeTrackingMethodInfo` object) is the first entry in the stack.
- `allsigs`: a list of all method signatures defined by a given expression
- `deps`: list of top-level named objects (`Symbol`s and `GlobalRef`s) that method definitions
  in this block depend on. For example, `if Sys.iswindows() f() = 1 else f() = 2 end` would
  store `Sys.iswindows` here.
- `includes`: a list of `module=>filename` for any `include` statements encountered while the
  expression was parsed.
"""
struct CodeTrackingMethodInfo
    exprstack::Vector{Expr}
    allsigs::Vector{Any}
    deps::Set{Union{GlobalRef,Symbol}}
    includes::Vector{Pair{Module,String}}
end
CodeTrackingMethodInfo(ex::Expr) = CodeTrackingMethodInfo([ex], Any[], Set{Union{GlobalRef,Symbol}}(), Pair{Module,String}[])

function add_signature!(methodinfo::CodeTrackingMethodInfo, @nospecialize(sig), ln)
    locdefs = get(CodeTracking.method_info, sig, nothing)
    locdefs === nothing && (locdefs = CodeTracking.method_info[sig] = Tuple{LineNumberNode,Expr}[])
    newdef = unwrap(methodinfo.exprstack[end])
    if newdef !== nothing
        if !any(locdef->locdef[1] == ln && isequal(RelocatableExpr(locdef[2]), RelocatableExpr(newdef)), locdefs)
            push!(locdefs, (fixpath(ln), newdef))
        end
        push!(methodinfo.allsigs, sig)
    end
    return methodinfo
end
push_expr!(methodinfo::CodeTrackingMethodInfo, mod::Module, ex::Expr) = (push!(methodinfo.exprstack, ex); methodinfo)
pop_expr!(methodinfo::CodeTrackingMethodInfo) = (pop!(methodinfo.exprstack); methodinfo)
function add_dependencies!(methodinfo::CodeTrackingMethodInfo, edges::CodeEdges, src, musteval)
    isempty(src.code) && return methodinfo
    stmt1 = first(src.code)
    if (isexpr(stmt1, :gotoifnot) && (dep = (stmt1::Expr).args[1]; isa(dep, Union{GlobalRef,Symbol}))) ||
       (is_GotoIfNot(stmt1) && (dep = stmt1.cond; isa(dep, Union{GlobalRef,Symbol})))
        # This is basically a hack to look for symbols that control definition of methods via a conditional.
        # It is aimed at solving #249, but this will have to be generalized for anything real.
        for (stmt, me) in zip(src.code, musteval)
            me || continue
            if hastrackedexpr(stmt)[1]
                push!(methodinfo.deps, dep)
                break
            end
        end
    end
    # for (dep, lines) in be.byname
    #     for ln in lines
    #         stmt = src.code[ln]
    #         if isexpr(stmt, :(=)) && stmt.args[1] == dep
    #             continue
    #         else
    #             push!(methodinfo.deps, dep)
    #         end
    #     end
    # end
    return methodinfo
end
function add_includes!(methodinfo::CodeTrackingMethodInfo, mod::Module, filename)
    push!(methodinfo.includes, mod=>filename)
    return methodinfo
end

# Eval and insert into CodeTracking data
function eval_with_signatures(mod, ex::Expr; mode=:eval, kwargs...)
    methodinfo = CodeTrackingMethodInfo(ex)
    docexprs = DocExprs()
    frame = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; mode=mode, kwargs...)[2]
    return methodinfo.allsigs, methodinfo.deps, methodinfo.includes, frame
end

function instantiate_sigs!(modexsigs::ModuleExprsSigs; mode=:sigs, kwargs...)
    for (mod, exsigs) in modexsigs
        for rex in keys(exsigs)
            is_doc_expr(rex.ex) && continue
            sigs, deps, _ = eval_with_signatures(mod, rex.ex; mode=mode, kwargs...)
            exsigs[rex] = sigs
            storedeps(deps, rex, mod)
        end
    end
    return modexsigs
end

function storedeps(deps, rex, mod)
    for dep in deps
        if isa(dep, GlobalRef)
            haskey(moduledeps, dep.mod) || continue
            ddict, sym = get_depdict(dep.mod), dep.name
        else
            ddict, sym = get_depdict(mod), dep
        end
        if !haskey(ddict, sym)
            ddict[sym] = Set{DepDictVals}()
        end
        push!(ddict[sym], (mod, rex))
    end
    return rex
end

# This is intended for testing purposes, but not general use. The key problem is
# that it doesn't properly handle methods that move from one file to another; there is the
# risk you could end up deleting the method altogether depending on the order in which you
# process these.
# See `revise` for the proper approach.
function eval_revised(mod_exs_sigs_new, mod_exs_sigs_old)
    delete_missing!(mod_exs_sigs_old, mod_exs_sigs_new)
    eval_new!(mod_exs_sigs_new, mod_exs_sigs_old)  # note: drops `includes`
    instantiate_sigs!(mod_exs_sigs_new)
end

"""
    Revise.init_watching(files)
    Revise.init_watching(pkgdata::PkgData, files)

For every filename in `files`, monitor the filesystem for updates. When the file is
updated, either [`Revise.revise_dir_queued`](@ref) or [`Revise.revise_file_queued`](@ref) will
be called.

Use the `pkgdata` version if the files are supplied using relative paths.
"""
function init_watching(pkgdata::PkgData, files=srcfiles(pkgdata))
    udirs = Set{String}()
    for file in files
        dir, basename = splitdir(file)
        dirfull = joinpath(basedir(pkgdata), dir)
        already_watching_dir = haskey(watched_files, dirfull)
        already_watching_dir || (watched_files[dirfull] = WatchList())
        watchlist = watched_files[dirfull]
        current_id = get(watchlist.trackedfiles, basename, nothing)
        new_id = pkgdata.info.id
        if new_id != NOPACKAGE || current_id === nothing
            # Allow the package id to be updated
            push!(watchlist, basename=>pkgdata)
            if watching_files[]
                fwatcher = TaskThunk(revise_file_queued, (pkgdata, file))
                schedule(Task(fwatcher))
            else
                already_watching_dir || push!(udirs, dir)
            end
        end
    end
    for dir in udirs
        dirfull = joinpath(basedir(pkgdata), dir)
        updatetime!(watched_files[dirfull])
        if !watching_files[]
            dwatcher = TaskThunk(revise_dir_queued, (dirfull,))
            schedule(Task(dwatcher))
        end
    end
    return nothing
end
init_watching(files) = init_watching(pkgdatas[NOPACKAGE], files)

"""
    revise_dir_queued(dirname)

Wait for one or more of the files registered in `Revise.watched_files[dirname]` to be
modified, and then queue the corresponding files on [`Revise.revision_queue`](@ref).
This is generally called via a [`Revise.TaskThunk`](@ref).
"""
@noinline function revise_dir_queued(dirname)
    @assert isabspath(dirname)
    if !isdir(dirname)
        sleep(0.1)   # in case git has done a delete/replace cycle
    end
    stillwatching = true
    while stillwatching
        if !isdir(dirname)
            with_logger(SimpleLogger(stderr)) do
                @warn "$dirname is not an existing directory, Revise is not watching"
            end
            break
        end

        latestfiles, stillwatching = watch_files_via_dir(dirname)  # will block here until file(s) change
        for (file, id) in latestfiles
            key = joinpath(dirname, file)
            if key in keys(user_callbacks_by_file)
                union!(user_callbacks_queue, user_callbacks_by_file[key])
                notify(revision_event)
            end
            if id != NOPACKAGE
                pkgdata = pkgdatas[id]
                if hasfile(pkgdata, key)  # issue #228
                    push!(revision_queue, (pkgdata, relpath(key, pkgdata)))
                    notify(revision_event)
                end
            end
        end
    end
    return
end

# See #66.
"""
    revise_file_queued(pkgdata::PkgData, filename)

Wait for modifications to `filename`, and then queue the corresponding files on [`Revise.revision_queue`](@ref).
This is generally called via a [`Revise.TaskThunk`](@ref).

This is used only on platforms (like BSD) which cannot use [`Revise.revise_dir_queued`](@ref).
"""
function revise_file_queued(pkgdata::PkgData, file)
    if !isabspath(file)
        file = joinpath(basedir(pkgdata), file)
    end
    if !file_exists(file)
        sleep(0.1)  # in case git has done a delete/replace cycle
    end

    dirfull, basename = splitdir(file)
    stillwatching = true
    while stillwatching
        if !file_exists(file) && !isdir(file)
            let file=file
                with_logger(SimpleLogger(stderr)) do
                    @warn "$file is not an existing file, Revise is not watching"
                end
            end
            notify(revision_event)
            break
        end
        try
            wait_changed(file)  # will block here until the file changes
        catch e
            # issue #459
            (isa(e, InterruptException) && throwto_repl(e)) || throw(e)
        end

        if file in keys(user_callbacks_by_file)
            union!(user_callbacks_queue, user_callbacks_by_file[file])
            notify(revision_event)
        end

        # Check to see if we're still watching this file
        stillwatching = haskey(watched_files, dirfull)
        PkgId(pkgdata) != NOPACKAGE && push!(revision_queue, (pkgdata, relpath(file, pkgdata)))
    end
    return
end

# Because we delete first, we have to make sure we've parsed the file
function handle_deletions(pkgdata, file)
    fi = maybe_parse_from_cache!(pkgdata, file)
    maybe_extract_sigs!(fi)
    mexsold = fi.modexsigs
    filep = normpath(joinpath(basedir(pkgdata), file))
    topmod = first(keys(mexsold))
    fileok = file_exists(filep)
    mexsnew = fileok ? parse_source(filep, topmod) : ModuleExprsSigs(topmod)
    if mexsnew !== nothing
        delete_missing!(mexsold, mexsnew)
    end
    if !fileok
        @warn("$filep no longer exists, deleted all methods")
        idx = fileindex(pkgdata, file)
        deleteat!(pkgdata.fileinfos, idx)
        deleteat!(pkgdata.info.files, idx)
        wl = get(watched_files, basedir(pkgdata), nothing)
        if isa(wl, WatchList)
            delete!(wl.trackedfiles, file)
        end
    end
    return mexsnew, mexsold
end

"""
    Revise.revise_file_now(pkgdata::PkgData, file)

Process revisions to `file`. This parses `file` and computes an expression-level diff
between the current state of the file and its most recently evaluated state.
It then deletes any removed methods and re-evaluates any changed expressions.
Note that generally it is better to use [`revise`](@ref) as it properly handles methods
that move from one file to another.

`id` must be a key in [`Revise.pkgdatas`](@ref), and `file` a key in
`Revise.pkgdatas[id].fileinfos`.
"""
function revise_file_now(pkgdata::PkgData, file)
    # @assert !isabspath(file)
    i = fileindex(pkgdata, file)
    if i === nothing
        println("Revise is currently tracking the following files in $(PkgId(pkgdata)): ", srcfiles(pkgdata))
        error(file, " is not currently being tracked.")
    end
    mexsnew, mexsold = handle_deletions(pkgdata, file)
    if mexsnew != nothing
        _, includes = eval_new!(mexsnew, mexsold)
        fi = fileinfo(pkgdata, i)
        pkgdata.fileinfos[i] = FileInfo(mexsnew, fi)
        maybe_add_includes_to_pkgdata!(pkgdata, file, includes; eval_now=true)
    end
    nothing
end

"""
    Revise.errors()

Report the errors represented in [`Revise.queue_errors`](@ref).
Errors are automatically reported the first time they are encountered, but this function
can be used to report errors again.
"""
function errors(revision_errors=keys(queue_errors))
    printed = Set{eltype(revision_errors)}()
    for item in revision_errors
        item in printed && continue
        push!(printed, item)
        pkgdata, file = item
        (err, bt) = queue_errors[(pkgdata, file)]
        fullpath = joinpath(basedir(pkgdata), file)
        if err isa ReviseEvalException
            @error "Failed to revise $fullpath" exception=err
        else
            @error "Failed to revise $fullpath" exception=(err, trim_toplevel!(bt))
        end
    end
end

"""
    Revise.retry()

Attempt to perform previously-failed revisions. This can be useful in cases of order-dependent errors.
"""
function retry()
    for (k, v) in queue_errors
        push!(revision_queue, k)
    end
    revise()
end

"""
    revise(; throw=false)

`eval` any changes in the revision queue. See [`Revise.revision_queue`](@ref).
If `throw` is `true`, throw any errors that occur during revision or callback;
otherwise these are only logged.
"""
function revise(; throw=false)
    sleep(0.01)  # in case the file system isn't quite done writing out the new files
    have_queue_errors = !isempty(queue_errors)

    # Do all the deletion first. This ensures that a method that moved from one file to another
    # won't get redefined first and deleted second.
    revision_errors = Tuple{PkgData,String}[]
    queue = sort!(collect(revision_queue); lt=pkgfileless)
    finished = eltype(revision_queue)[]
    mexsnews = ModuleExprsSigs[]
    interrupt = false
    for (pkgdata, file) in queue
        try
            push!(mexsnews, handle_deletions(pkgdata, file)[1])
            push!(finished, (pkgdata, file))
        catch err
            throw && Base.throw(err)
            interrupt |= isa(err, InterruptException)
            push!(revision_errors, (pkgdata, file))
            queue_errors[(pkgdata, file)] = (err, catch_backtrace())
        end
    end
    # Do the evaluation
    for ((pkgdata, file), mexsnew) in zip(finished, mexsnews)
        defaultmode = PkgId(pkgdata).name == "Main" ? :evalmeth : :eval
        i = fileindex(pkgdata, file)
        i === nothing && continue   # file was deleted by `handle_deletions`
        fi = fileinfo(pkgdata, i)
        try
            for (mod, exsnew) in mexsnew
                mode = defaultmode
                # Allow packages to override the supplied mode
                if isdefined(mod, :__revise_mode__)
                    mode = getfield(mod, :__revise_mode__)::Symbol
                end
                mode ∈ (:sigs, :eval, :evalmeth, :evalassign) || error("unsupported mode ", mode)
                exsold = get(fi.modexsigs, mod, empty_exs_sigs)
                for rex in keys(exsnew)
                    sigs, includes = eval_rex(rex, exsold, mod; mode=mode)
                    if sigs !== nothing
                        exsnew[rex] = sigs
                    end
                    if includes !== nothing
                        maybe_add_includes_to_pkgdata!(pkgdata, file, includes; eval_now=true)
                    end
                end
            end
            pkgdata.fileinfos[i] = FileInfo(mexsnew, fi)
            delete!(queue_errors, (pkgdata, file))
        catch err
            throw && Base.throw(err)
            interrupt |= isa(err, InterruptException)
            push!(revision_errors, (pkgdata, file))
            queue_errors[(pkgdata, file)] = (err, catch_backtrace())
        end
    end
    if interrupt
        for pkgfile in finished
            haskey(queue_errors, pkgfile) || delete!(revision_queue, pkgfile)
        end
    else
        empty!(revision_queue)
    end
    errors(revision_errors)
    if !isempty(queue_errors)
        if !have_queue_errors    # only print on the first time errors occur
            io = IOBuffer()
            println(io, "\n") # better here than in the triple-quoted literal, see https://github.com/JuliaLang/julia/issues/34105
            for (pkgdata, file) in keys(queue_errors)
                println(io, "  ", joinpath(basedir(pkgdata), file))
            end
            str = String(take!(io))
            @warn """The running code does not match the saved version for the following files:$str
            If the error was due to evaluation order, it can sometimes be resolved by calling `Revise.retry()`.
            Use Revise.errors() to report errors again. Only the first error in each file is shown.
            Your prompt color may be yellow until the errors are resolved."""
            maybe_set_prompt_color(:warn)
        end
    else
        maybe_set_prompt_color(:ok)
    end
    tracking_Main_includes[] && queue_includes(Main)

    process_user_callbacks!(throw=throw)

    nothing
end
revise(backend::REPL.REPLBackend) = revise()

"""
    revise(mod::Module)

Reevaluate every definition in `mod`, whether it was changed or not. This is useful
to propagate an updated macro definition, or to force recompiling generated functions.
"""
function revise(mod::Module)
    mod == Main && error("cannot revise(Main)")
    id = PkgId(mod)
    pkgdata = pkgdatas[id]
    for (i, file) in enumerate(srcfiles(pkgdata))
        fi = fileinfo(pkgdata, i)
        for (mod, exsigs) in fi.modexsigs
            for def in keys(exsigs)
                ex = def.ex
                exuw = unwrap(ex)
                isexpr(exuw, :call) && exuw.args[1] === :include && continue
                try
                    Core.eval(mod, ex)
                catch err
                    @show mod
                    display(ex)
                    rethrow(err)
                end
            end
        end
    end
    return true  # fixme try/catch?
end

"""
    Revise.track(mod::Module, file::AbstractString)
    Revise.track(file::AbstractString)

Watch `file` for updates and [`revise`](@ref) loaded code with any
changes. `mod` is the module into which `file` is evaluated; if omitted,
it defaults to `Main`.

If this produces many errors, check that you specified `mod` correctly.
"""
function track(mod::Module, file::AbstractString; mode=:sigs, kwargs...)
    isfile(file) || error(file, " is not a file")
    # Determine whether we're already tracking this file
    id = PkgId(mod)
    if haskey(pkgdatas, id)
        pkgdata = pkgdatas[id]
        relfile = relpath(abspath(file), pkgdata)
        hasfile(pkgdata, relfile) && return nothing
        # Use any "fixes" provided by relpath
        file = joinpath(basedir(pkgdata), relfile)
    else
        # Check whether `track` was called via a @require. Ref issue #403 & #431.
        st = stacktrace(backtrace())
        if any(sf->sf.func === :listenpkg && endswith(String(sf.file), "require.jl"), st)
            nameof(mod) === :Plots || Base.depwarn("Revise@2.4 or higher automatically handles `include` statements in `@require` expressions.\nPlease do not call `Revise.track` from such blocks.", :track)
            return nothing
        end
        file = abspath(file)
    end
    # Set up tracking
    fm = parse_source(file, mod; mode=mode)
    if fm !== nothing
        if mode === :includet
            mode = :sigs   # we already handled evaluation in `parse_source`
        end
        instantiate_sigs!(fm; mode=mode, kwargs...)
        if !haskey(pkgdatas, id)
            # Wait a bit to see if `mod` gets initialized
            sleep(0.1)
        end
        pkgdata = get(pkgdatas, id, nothing)
        if pkgdata === nothing
            pkgdata = PkgData(id, pathof(mod))
        end
        if !haskey(CodeTracking._pkgfiles, id)
            CodeTracking._pkgfiles[id] = pkgdata.info
        end
        push!(pkgdata, relpath(file, pkgdata)=>FileInfo(fm))
        init_watching(pkgdata, (file,))
        pkgdatas[id] = pkgdata
    end
    return nothing
end

function track(file::AbstractString; kwargs...)
    startswith(file, juliadir) && error("use Revise.track(Base) or Revise.track(<stdlib module>)")
    track(Main, file; kwargs...)
end

"""
    includet(filename)

Load `filename` and track future changes. `includet` is intended for quick "user scripts"; larger or more
established projects are encouraged to put the code in one or more packages loaded with `using`
or `import` instead of using `includet`. See https://timholy.github.io/Revise.jl/stable/cookbook/
for tips about setting up the package workflow.

By default, `includet` only tracks modifications to *methods*, not *data*. See the extended help for details.
Note that this differs from packages, which evaluate all changes by default.
This default behavior can be overridden; see [Configuring the revise mode](@ref).

# Extended help

## Behavior and justification for the default revision mode (`:evalmeth`)

`includet` uses a default `__revise_mode__ = :evalmeth`. The consequence is that if you change

```
a = [1]
f() = 1
```
to
```
a = [2]
f() = 2
```
then Revise will update `f` but not `a`.

This is the default choice for `includet` because such files typically mix method definitions and data-handling.
Data often has many untracked dependencies; later in the same file you might `push!(a, 22)`, but Revise cannot
determine whether you wish it to re-run that line after redefining `a`.
Consequently, the safest default choice is to leave the user in charge of data.

## Workflow tips

If you have a series of computations that you want to run when you redefine your methods, consider separating
your method definitions from your computations:

- method definitions go in a package, or a file that you `includet` *once*
- the computations go in a separate file, that you re-`include` (no "t" at the end) each time you want to rerun
  your computations.

This can be automated using [`entr`](@ref).

## Internals

`includet` is essentially shorthand for

    Revise.track(Main, filename; mode=:eval, skip_include=false)

Do *not* use `includet` for packages, as those should be handled by `using` or `import`.
If `using` and `import` aren't working, you may have packages in a non-standard location;
try fixing it with something like `push!(LOAD_PATH, "/path/to/my/private/repos")`.
(If you're working with code in Base or one of Julia's standard libraries, use
`Revise.track(mod)` instead, where `mod` is the module.)

`includet` is deliberately non-recursive, so if `filename` loads any other files,
they will not be automatically tracked.
(See [`Revise.track`](@ref) to set it up manually.)
"""
function includet(mod::Module, file::AbstractString)
    prev = Base.source_path(nothing)
    if prev === nothing
        file = abspath(file)
    else
        file = normpath(joinpath(dirname(prev), file))
    end
    tls = task_local_storage()
    tls[:SOURCE_PATH] = file
    try
        track(mod, file; mode=:includet, skip_include=false)
        if prev === nothing
            delete!(tls, :SOURCE_PATH)
        else
            tls[:SOURCE_PATH] = prev
        end
    catch err
        if prev === nothing
            delete!(tls, :SOURCE_PATH)
        else
            tls[:SOURCE_PATH] = prev
        end
        if isa(err, ReviseEvalException)
            printstyled(stderr, "ERROR: "; color=Base.error_color());
            showerror(stderr, err; blame_revise=false)
            println(stderr, "\nin expression starting at ", err.loc)
        else
            throw(err)
        end
    end
    return nothing
end
includet(file::AbstractString) = includet(Main, file)

"""
    Revise.silence(pkg)

Silence warnings about not tracking changes to package `pkg`.
"""
function silence(pkg::Symbol)
    push!(silence_pkgs, pkg)
    if !isdir(depsdir)
        mkpath(depsdir)
    end
    open(silencefile[], "w") do io
        for p in silence_pkgs
            println(io, p)
        end
    end
    nothing
end
silence(pkg::AbstractString) = silence(Symbol(pkg))

## Utilities

"""
    success = get_def(method::Method)

As needed, load the source file necessary for extracting the code defining `method`.
The source-file defining `method` must be tracked.
If it is in Base, this will execute `track(Base)` if necessary.

This is a callback function used by `CodeTracking.jl`'s `definition`.
"""
function get_def(method::Method; modified_files=revision_queue)
    yield()   # magic bug fix for the OSX test failures. TODO: figure out why this works (prob. Julia bug)
    if method.file === :none && String(method.name)[1] == '#'
        # This is likely to be a kwarg method, try to find something with location info
        method = bodymethod(method)
    end
    filename = fixpath(String(method.file))
    if startswith(filename, "REPL[")
        isdefined(Base, :active_repl) || return false
        fi = add_definitions_from_repl(filename)
        hassig = false
        for (mod, exs) in fi.modexsigs
            for sigs in values(exs)
                hassig |= !isempty(sigs)
            end
        end
        return hassig
    end
    id = get_tracked_id(method.module; modified_files=modified_files)
    id === nothing && return false
    pkgdata = pkgdatas[id]
    filename = relpath(filename, pkgdata)
    if hasfile(pkgdata, filename)
        def = get_def(method, pkgdata, filename)
        def !== nothing && return true
    end
    # Lookup can fail for macro-defined methods, see https://github.com/JuliaLang/julia/issues/31197
    # We need to find the right file.
    if method.module == Base || method.module == Core || method.module == Core.Compiler
        @warn "skipping $method to avoid parsing too much code"
        CodeTracking.method_info[method.sig] = missing
        return false
    end
    parentfile, included_files = modulefiles(method.module)
    if parentfile !== nothing
        def = get_def(method, pkgdata, relpath(parentfile, pkgdata))
        def !== nothing && return true
        for modulefile in included_files
            def = get_def(method, pkgdata, relpath(modulefile, pkgdata))
            def !== nothing && return true
        end
    end
    # As a last resort, try every file in the package
    for file in srcfiles(pkgdata)
        def = get_def(method, pkgdata, file)
        def !== nothing && return true
    end
    @warn "$(method.sig) was not found"
    # So that we don't call it again, store missingness info in CodeTracking
    CodeTracking.method_info[method.sig] = missing
    return false
end

function get_def(method, pkgdata, filename)
    maybe_extract_sigs!(maybe_parse_from_cache!(pkgdata, filename))
    return get(CodeTracking.method_info, method.sig, nothing)
end

function get_tracked_id(id::PkgId; modified_files=revision_queue)
    # Methods from Base or the stdlibs may require that we start tracking
    if !haskey(pkgdatas, id)
        recipe = id.name === "Compiler" ? :Compiler : Symbol(id.name)
        recipe === :Core && return nothing
        _track(id, recipe; modified_files=modified_files)
        @info "tracking $recipe"
        if !haskey(pkgdatas, id)
            @warn "despite tracking $recipe, $id was not found"
            return nothing
        end
    end
    return id
end
get_tracked_id(mod::Module; modified_files=revision_queue) =
    get_tracked_id(PkgId(mod); modified_files=modified_files)

function get_expressions(id::PkgId, filename)
    get_tracked_id(id)
    pkgdata = pkgdatas[id]
    fi = maybe_parse_from_cache!(pkgdata, filename)
    maybe_extract_sigs!(fi)
    return fi.modexsigs
end

function add_definitions_from_repl(filename)
    hist_idx = parse(Int, filename[6:end-1])
    hp = (Base.active_repl::REPL.LineEditREPL).interface.modes[1].hist::REPL.REPLHistoryProvider
    src = hp.history[hp.start_idx+hist_idx]
    id = PkgId(nothing, "@REPL")
    pkgdata = pkgdatas[id]
    mexs = ModuleExprsSigs(Main::Module)
    parse_source!(mexs, src, filename, Main::Module)
    instantiate_sigs!(mexs)
    fi = FileInfo(mexs)
    push!(pkgdata, filename=>fi)
    return fi
end

function update_stacktrace_lineno!(trace)
    local nrep
    for i = 1:length(trace)
        t = trace[i]
        has_nrep = !isa(t, StackTraces.StackFrame)
        if has_nrep
            t, nrep = t
        end
        t = t::StackTraces.StackFrame
        if t.linfo isa Core.MethodInstance
            m = t.linfo.def
            sigt = m.sig
            # Why not just call `whereis`? Because that forces tracking. This is being
            # clever by recognizing that these entries exist only if there have been updates.
            updated = get(CodeTracking.method_info, sigt, nothing)
            if updated !== nothing
                lnn = updated[1][1]     # choose the first entry by default
                lineoffset = lnn.line - m.line
                t = StackTraces.StackFrame(t.func, lnn.file, t.line+lineoffset, t.linfo, t.from_c, t.inlined, t.pointer)
                trace[i] = has_nrep ? (t, nrep) : t
            end
        end
    end
    return trace
end

function method_location(method::Method)
    # Why not just call `whereis`? Because that forces tracking. This is being
    # clever by recognizing that these entries exist only if there have been updates.
    updated = get(CodeTracking.method_info, method.sig, nothing)
    if updated !== nothing
        lnn = updated[1][1]
        return lnn.file, lnn.line
    end
    return method.file, method.line
end

# Set the prompt color to indicate the presence of unhandled revision errors
const original_repl_prefix = Ref{Union{String,Function,Nothing}}(nothing)
function maybe_set_prompt_color(color)
    if isdefined(Base, :active_repl)
        repl = Base.active_repl
        if isa(repl, REPL.LineEditREPL)
            if color === :warn
                # First save the original setting
                if original_repl_prefix[] === nothing
                    original_repl_prefix[] = repl.mistate.current_mode.prompt_prefix
                end
                repl.mistate.current_mode.prompt_prefix = "\e[33m"  # yellow
            else
                color = original_repl_prefix[]
                color === nothing && return nothing
                repl.mistate.current_mode.prompt_prefix = color
                original_repl_prefix[] = nothing
            end
        end
    end
    return nothing
end

# On Julia 1.5.0-DEV.282 and higher, we can just use an AST transformation
# This uses invokelatest not for reasons of world age but to ensure that the call is made at runtime.
# This allows `revise_first` to be compiled without compiling `revise` itself, and greatly
# reduces the overhead of using Revise.
function revise_first(ex)
    # Special-case `exit()` (issue #562)
    if isa(ex, Expr)
        exu = unwrap(ex)
        isa(exu, Expr) && exu.head === :call && length(exu.args) == 1 && exu.args[1] === :exit && return ex
    end
    # Check for queued revisions, and if so call `revise` first before executing the expression
    return Expr(:toplevel, :(isempty($revision_queue) || Base.invokelatest($revise)), ex)
end

@noinline function run_backend(backend)
    while true
        tls = task_local_storage()
        tls[:SOURCE_PATH] = nothing
        ast, show_value = take!(backend.repl_channel)
        if show_value == -1
            # exit flag
            break
        end
        # Process revisions, skipping `exit()` (issue #327)
        if !isa(ast, Expr) || length(ast.args) < 2 || (ex = ast.args[2]; !isexpr(ex, :call)) || length(ex.args) != 1 || ex.args[1] != :exit
            revise(backend)
        end
        # Now eval the input
        REPL.eval_user_input(ast, backend)
    end
    # tell outer backend loop to exit as well
    put!(backend.repl_channel, (nothing, -1))
    nothing
end

"""
    steal_repl_backend(backend = Base.active_repl_backend)

Replace the REPL's normal backend with one that calls [`revise`](@ref) before executing
any REPL input.
"""
function steal_repl_backend(backend = Base.active_repl_backend)
    if VERSION >= v"1.5.0-DEV.282"
        # @warn "See Revise documentation for recommended configuration changes"
        pushfirst!(backend.ast_transforms, revise_first)
    else
        @async begin
            # When we return back to the backend loop, tell it to start
            # running our backend loop next, which differs only
            # by processing the revision queue before evaluating each input.
            put!(backend.repl_channel, (:($run_backend($backend)), 1))
        end
    end
    nothing
end

function wait_steal_repl_backend()
    iter = 0
    # wait for active_repl_backend to exist
    while !isdefined(Base, :active_repl_backend) && iter < 20
        sleep(0.05)
        iter += 1
    end
    if isdefined(Base, :active_repl_backend)
        steal_repl_backend(Base.active_repl_backend)
    else
        @warn "REPL initialization failed, Revise is not in automatic mode. Call `revise()` manually."
    end
end

"""
    Revise.async_steal_repl_backend()

Wait for the REPL to complete its initialization, and then call [`Revise.steal_repl_backend`](@ref).
This is necessary because code registered with `atreplinit` runs before the REPL is
initialized, and there is no corresponding way to register code to run after it is complete.
"""
function async_steal_repl_backend()
    mode = get(ENV, "JULIA_REVISE", "auto")
    if mode == "auto"
        atreplinit() do repl
            @async wait_steal_repl_backend()
        end
    end
    return nothing
end

"""
    Revise.init_worker(p)

Define methods on worker `p` that Revise needs in order to perform revisions on `p`.
Revise itself does not need to be running on `p`.
"""
function init_worker(p)
    remotecall(Core.eval, p, Main, quote
        function whichtt(sig)
            ret = Base._methods_by_ftype(sig, -1, typemax(UInt))
            isempty(ret) && return nothing
            m = ret[end][3]::Method   # the last method returned is the least-specific that matches, and thus most likely to be type-equal
            methsig = m.sig
            (sig <: methsig && methsig <: sig) || return nothing
            return m
        end
        function delete_method_by_sig(sig)
            m = whichtt(sig)
            isa(m, Method) && Base.delete_method(m)
        end
    end)
end

function __init__()
    run_on_worker = get(ENV, "JULIA_REVISE_WORKER_ONLY", "0")
    if !(myid() == 1 || run_on_worker == "1")
        return nothing
    end
    # Check Julia paths (issue #601)
    if !isdir(juliadir)
        major, minor = Base.VERSION.major, Base.VERSION.minor
        @warn """Expected non-existent $juliadir to be your Julia directory.
                 Certain functionality will be disabled.
                 To fix this, try deleting Revise's cache files in ~/.julia/compiled/v$major.$minor/Revise, then restart Julia and load Revise.
                 If this doesn't fix the problem, please report an issue at https://github.com/timholy/Revise.jl/issues."""
    end
    if isfile(silencefile[])
        pkgs = readlines(silencefile[])
        for pkg in pkgs
            push!(silence_pkgs, Symbol(pkg))
        end
    end
    mode = get(ENV, "JULIA_REVISE", "auto")
    if mode == "auto"
        if isdefined(Base, :active_repl_backend)
            if VERSION >= v"1.5.0-DEV.282"
                pushfirst!(Base.active_repl_backend.ast_transforms, revise_first)
            else
                steal_repl_backend(Base.active_repl_backend::REPL.REPLBackend)
            end
        elseif isdefined(Main, :IJulia)
            Main.IJulia.push_preexecute_hook(revise)
        elseif VERSION >= v"1.5.0-DEV.282"
            pushfirst!(REPL.repl_ast_transforms, revise_first)
        end
        if isdefined(Main, :Atom)
            Atom = getfield(Main, :Atom)
            if Atom isa Module && isdefined(Atom, :handlers)
                setup_atom(Atom)
            end
        end
    end
    polling = get(ENV, "JULIA_REVISE_POLL", "0")
    if polling == "1"
        polling_files[] = watching_files[] = true
    end
    rev_include = get(ENV, "JULIA_REVISE_INCLUDE", "0")
    if rev_include == "1"
        tracking_Main_includes[] = true
    end
    # Correct line numbers for code moving around
    Base.update_stackframes_callback[] = update_stacktrace_lineno!
    if isdefined(Base, :methodloc_callback)
        Base.methodloc_callback[] = method_location
    end
    # Add `includet` to the compiled_modules (fixes #302)
    for m in methods(includet)
        push!(JuliaInterpreter.compiled_methods, m)
    end
    # Set up a repository for methods defined at the REPL
    id = PkgId(nothing, "@REPL")
    pkgdatas[id] = pkgdata = PkgData(id, nothing)
    # Set the lookup callbacks
    CodeTracking.method_lookup_callback[] = get_def
    CodeTracking.expressions_callback[] = get_expressions

    # Watch the manifest file for changes
    mfile = manifest_file()
    if mfile === nothing
        @warn "no Manifest.toml file found, static paths used"
    else
        wmthunk = TaskThunk(watch_manifest, (mfile,))
        schedule(Task(wmthunk))
    end
    push!(Base.include_callbacks, watch_includes)
    push!(Base.package_callbacks, swap_watch_package)
    return nothing
end

function swap_watch_package(id::PkgId)
    # `Base.package_callbacks` fire immediately after module initialization, and would fire
    # on Revise itself. This is not necessary for most users, and has the downside that
    # the user doesn't get to the REPL prompt until `watch_package` finishes compiling.
    # To prevent this, Revise pushes a stub, `swap_watch_package`, which just replaces
    # itself with `watch_package` in the list of package callbacks.
    # This delays compilation of everything that `watch_package` requires, leading to
    # faster perceived startup times.
    idx = findfirst(isidentical(swap_watch_package), Base.package_callbacks)
    Base.package_callbacks[idx] = watch_package
end

function setup_atom(atommod::Module)::Nothing
    handlers = getfield(atommod, :handlers)
    for x in ["eval", "evalall", "evalshow", "evalrepl"]
        if haskey(handlers, x)
            old = handlers[x]
            Main.Atom.handle(x) do data
                revise()
                old(data)
            end
        end
    end
    return nothing
end

function add_revise_deps()
    # Populate CodeTracking data for dependencies and initialize watching on code that Revise depends on
    for mod in (CodeTracking, OrderedCollections, JuliaInterpreter, LoweredCodeUtils, Revise)
        id = PkgId(mod)
        pkgdata = parse_pkg_files(id)
        init_watching(pkgdata, srcfiles(pkgdata))
        pkgdatas[id] = pkgdata
    end
    return nothing
end

include("precompile.jl")
_precompile_()


text:
## Load Packages
using FreeRay, DataFrames, CSV
using Base.Threads

## Impedance input
# create an empty dataframe
df1 = DataFrame( freq = Float64[],   # 1/3-octave freq.
                sigma = Float64[],  # flow resistivity
                impedMl = Int32[],
                Zc = Complex{Float64}[])

#Zc = Complex{Float64}
# 1/3-octave, freq 15 >2000 Hz
CenterFreq = getCenterFreq(bandwidth=3,lim=(15,2000))

# Impedance models
sigma = [10,200,2000]*1000 # Pa s/m2 (See Table 5.1 Bies and Hansen)

for i_1=1:length(CenterFreq)
        for i_2=1:length(sigma)

                f = CenterFreq[i_1]; σ = sigma[i_2]; Ω = 0.5 # porosity
                local Zc = [  impedanceM1(f,σ),     # tested!
                        impedanceM2(f,σ),     # tested!
                        impedanceM3(f,σ,Ω),    # tested!
                        impedanceM4(f,σ,Ω),    # tested!
                        impedanceM5(f,σ,Ω),    # tested!
                        impedanceM6(f,σ,Ω),    # tested!
                        impedanceM7(f,σ,Ω),    # tested!
                        impedanceM8(f,σ,Ω),    # tested!
                        impedanceM9b(f,σ,Ω)     ]

                for i in 1:9
                        push!(df1, (f, σ, i,Zc[i]))
                end

        end # end i_2
end # end i_1

## Wind input
df2 = DataFrame( alpha = Float64[],   # 1/3-octave freq.
                z0 = Float64[],  # flow resistivity
                windMl = Int32[])

# wind models
alpha = [0.13, 0.21, 0.39]
zz0 = [0.002, 0.03, 0.26]

vref = 2.7; href = 10 # wind speed and ref wind
h = range(0,1000,step = 1.0) # height step

rows = length(h); cols = length(alpha)*8
vh = Array{Float64,2}(undef, rows, cols)

for i_1 = 1:length(alpha)

    α = alpha[i_1]; z0 = zz0[i_1]
    k = (i_1-1)*8 + 1

    # log Linear - law model
    for i=2:length(h)
        vh[i,k] = windprofile_logLL(h[i],z0,vref,href)
    end

    # logarithmic law model
    vh2 = similar(h) # pre-allocation
    for i=2:length(h)
        vh[i,k+1]  = windprofile_logL(h[i],z0,vref,href)
    end

    # DH log model
    vh3 = similar(h) # pre-allocation
    for i=2:length(h)
        vh[i,k+2] = windprofile_DH(h[i],z0,vref,href)
    end

    # power law model
    vh4 = similar(h) # pre-allocation
    for i=2:length(h)
        vh[i,k+3] = windprofile_PL_α(h[i],1/7,vref,href)
    end

    # power law model mean anual
    vh5 = similar(h) # pre-allocation
    for i=2:length(h)
        vh[i,k+4] = windprofile_PL_α(h[i],α,vref,href)
    end

    # power law model Counihan model
    vh6 = similar(h) # pre-allocation
    αC = 0.24 + 0.096*log(z0) + 0.016*(log(z0))^2
    for i=2:length(h)
        vh[i,k+5] = windprofile_PL_α(h[i],αC,vref,href)
    end

    # power law SH model
    vh7 = similar(h) # pre-allocation
    αSH = 1.03 + 0.31*log(z0) + 0.03*(log(z0))^2
    for i=2:length(h)
        vh[i,k+6] = windprofile_PL_α(h[i],αSH,vref,href)
    end

    # power law αP model
    vh8 = similar(h) # pre-allocation
    for i=2:length(h)
        vh[i,k+7] = windprofile_PL_α_P(h[i],z0,vref,href)
    end

    for i in 1:8
        push!(df2, (α, z0, i))
    end

end # end i_1
vh[1,:] .= 0.0
df2.idxWindprofile = 1:24

## Combine dataframe for easily control

df = crossjoin(df1, df2, makeunique = true)
TLoss = Array{Float32,2}(undef, nrow(df), 20)

## Bellhop input setup

function inforEx(df,simID) # extract information in dataframe
    freqSim = df.freq[simID]
    ZcSim = df.Zc[simID]
    idxWind = df.idxWindprofile[simID]
    return freqSim, ZcSim, idxWind
end

# Multiple recievers on grid
receiver = Receiver(
    depth_point = 1,
    range_point = 20,
    depth = Vec2(-1.5f0,-1.5f0),
    range = Vec2(0.5f0,10f0)
)

# Terrain geometry
terrain = Terrain(
    interp_type = "L",
    profile = (Vec2([0f0,5f0,10f0],[0f0,0f0,0f0]))
)

@time @threads for simID = 1:1#nrow(df)
    println(simID)

    freqSim, ZcSim, idxWind = inforEx(df,simID)

    # Point source at 80 m
    source = Source(
        frequency = Float32.(freqSim), # frequency dependence
        height = -80f0
    )

    ### Boundary condition
    Theta, Rmag, Rphase = R_coeff(ZcSim;len=100) # Zc dependence
    brc = Vec3(Theta,Rmag,Rphase)
    trc = Vec3([0f0,45f0,90f0],[0f0,0f0,0f0],[0f0,0f0,0f0])
    reflection = Reflection_Coeff(
        top_coeff = trc,
        bottom_coeff = brc
    )

    # Sound speed profile
    hneg = -h[end:-1:1]
    soundspeed = 343 .+ vh[:,idxWind][end:-1:1]
    sspl = Vec2(Float32.(hneg),Float32.(soundspeed)) # sound speed profile
    ssp = SSP(sound_speed_profile = sspl)

    ### Analysis
    opt = Analysis(
        filename = "Uncertainty_case1_$simID",
        analyse = "CB",
        option1 = "CFW",
        option2 = "F*",
        num_ray = 16001,
        alpha = Vec2(-80.0f0,80.0f0),
        box = Vec2(10f0,1000.0f0),
        step= 0.1
    )

    # Create environment files
    Environment(opt,source, receiver,ssp,terrain,reflection)

    # Run Bellhop
    fn= opt.filename
    filename = "temp\\$fn"
    run_bellhop = `bellhop $filename`
    run(run_bellhop)
    rmfile(filename)

    #p1 = PlotRay("$filename.ray",
    #        xlabs = "Range, m",
    #        ylabs = "Height, m")

    #p2 = PlotShd("$filename.shd";
    #        xlabs = "Range, m",
    #        ylabs = "Transmission loss, dB",
    #        cblabs = "dB",
    #        climb = (40,80))

    #PlotTlr("$filename.shd", -1.5;
    #        xlabs = "Range, m",
    #        ylabs = "Transmission loss, dB",
    #        xlim = (0,10000),
    #        ylim = (20,120))

    pressure, Pos_r_r, Pos_r_z = read_shd("$filename.shd")
    tlt = -20.0f0 * log10.(abs.(pressure))

    TLoss[simID,:] = tlt

end # end loop simID

CSV.write("df.csv", df)
CSV.write("TLoss.csv",  DataFrame(TLoss), writeheader=false)


text:
module ModalParam

include("modal.jl")

export mac, modalparam, modalparami

end # module


text:
using Plots
using ColorSchemes
using Printf

pyplot()

function meshgrid(x, y)
    x = reshape(x, 1, length(x))
    y = reshape(y, length(y), 1)
    X = repeat(x, length(y), 1)
    Y = repeat(y, 1, length(x))
    return X, Y
end

function plot_growth_slice(filenc, filejson, fileplot)

    # read json file
    json_string = JSON3.read(read(filejson, String))

    Umax = json_string.jet.Umax
    L    = json_string.jet.L
    fz   = json_string.physics.fz

    # read NetCDF file
    ωs, modes, ks, ms, iEigs, ys = load_spectrum(filenc)

    print("\n--> Plotting the growth rates for a fixed k versus m in ", fileplot, "\n")

    growth = zeros(size(ωs))
    for (ik, k) in enumerate(ks)
        for (im, m) in enumerate(ms)
            for iEig in iEigs
                growth[iEig, im, ik] = max(0, imag(ωs/fz)[iEig, im, ik])
            end
        end
    end

    # scaling wavenumber for plotting
    m = L/(2*π)*ms
    plt = plot()
    for iEig in iEigs
        plot!(plt, m, growth[iEig, :, 1],
                label = @sprintf("%d", iEig),
            linewidth = 2,
            xlim = (m[1], m[end]),
               xlabel = "L m/(2*π)",
                title = "Growth rates for k = 0",
               legend = :topright)
    end
    savefig(plt, fileplot)
end

function plot_growth(filenc, filejson, fileplot)

    # read json file
    json_string = JSON3.read(read(filejson, String))

    Umax = json_string.jet.Umax
    L    = json_string.jet.L
    fz   = json_string.physics.fz

    # read NetCDF file
    ωs, modes, ks, ms, iEigs, ys = load_spectrum(filenc)

    print("\n--> Plotting the growth rates versus (k, m) in ", fileplot, "\n")

    growth = zeros(size(ωs)[2:3])
    for (ik, k) in enumerate(ks)
        for (im, m) in enumerate(ms)
            growth[im, ik] = max(0, imag(ωs/fz)[1, im, ik])
        end
    end

    # scaling wavenumber for plotting
    k = L/(2*π)*ks
    m = L/(2*π)*ms

    kwargs = (
        xlabel = "k",
        ylabel = "m",
          fill = true,
        levels = 20,
     linewidth = 0,
         color = :haline,
      colorbar = true,
          xlim = (k[1], k[end]),
          ylim = (m[1], m[end])
    )

    plt = contour(k, m, growth, title="Growth rates for 1st mode"; kwargs...)
    savefig(plt, fileplot)
end

function plot_modes_1D(filenc, filejson, fileplot, Neigs)

        # read json file
        json_string = JSON3.read(read(filejson, String))

        Umax = json_string.jet.Umax
        L    = json_string.jet.L
        fz   = json_string.physics.fz
        Ny   = json_string.grid.Ny
        Ly   = json_string.grid.Ly

        # read NetCDF file
        ωs, modes, ks, ms, iEigs, ys = load_spectrum(filenc)

        print("\n--> Plotting 1D modes in the file ", fileplot, "\n")

        # scaling wavenumber for plotting
        y = ys[1:Ny+1]
        k = L/(2*π)*ks
        m = L/(2*π)*ms

        ik = 1    # most unstable mode

        for iEig in collect(1:Neigs)
            tmp              = findmax(imag(ωs/fz)[iEig, :, ik])
            max_growth, indm = tmp[1], tmp[2]

            @printf("\n iEig = %2d  k = %10.6f  m = %10.6f  growth = %10.6f",
                    iEig, float(k[ik]), float(m[indm]), max_growth)

            vvec          = zeros(ComplexF64, Ny+1)
            uvec          = modes[1:Ny+1, iEig, indm, ik]
            vvec[2:end-1] = modes[Ny+2:2*Ny, iEig, indm, ik]
            bvec          = modes[2*Ny+1:3*Ny+1, iEig, indm, ik]

            u_plt = plot(y/ L, real(uvec), line=:solid, color=:blue, linewidth=2, label="Re(u)")
            plot!(u_plt, y/ L, imag(uvec), line=:solid, color=:red,  linewidth=2, label="Im(u)")
            v_plt = plot(y/ L, real(vvec), line=:solid, color=:blue, linewidth=2, label="Re(v)")
            plot!(v_plt, y/ L, imag(uvec), line=:solid, color=:red,  linewidth=2, label="Im(v)")
            b_plt = plot(y/ L, real(bvec), line=:solid, color=:blue, linewidth=2, label="Re(b)")
            plot!(b_plt, y/ L, imag(bvec), line=:solid, color=:red,  linewidth=2, label="Im(b)")
            plt = plot(u_plt, v_plt, b_plt, layout=(3,1), size=(800, 600))
            file = string(fileplot, iEig, ".png")
            savefig(plt, file)
        end
end

function plot_modes_2D(filenc, filejson, fileplot, Neigs)

    # read json file
    json_string = JSON3.read(read(filejson, String))

    Umax = json_string.jet.Umax
    L    = json_string.jet.L
    fz   = json_string.physics.fz
    Ny   = json_string.grid.Ny
    Ly   = json_string.grid.Ly

    # read NetCDF file
    ωs, modes, ks, ms, iEigs, ys = load_spectrum(filenc)

    print("\n--> Plotting 2D modes in the file ", fileplot, "\n")

    # scaling wavenumber for plotting
    y = ys[1:Ny+1]
    k = L/(2*π)*ks
    m = L/(2*π)*ms

    ik = 1    # most unstable mode
    iEig = 1
    indm = findmax(imag(ωs/fz)[ik, :, iEig])[2]
    Nz = Ny
    Lz = 2*π/ms[indm]
    dz = Lz/Nz
    z = collect(0:dz:Lz)
    Y,Z = meshgrid(y, z)

    kwargs = (
        xlabel = "y",
        ylabel = "z",
          fill = true,
        levels = 20,
     linewidth = 0,
         color = :balance,
      colorbar = true,
#          xlim = (k[1], k[end]),
#          ylim = (m[1], m[end])
    )

    for iEig in collect(1:Neigs)
        tmp              = findmax(imag(ωs/fz)[iEig, :, ik])
        max_growth, indm = tmp[1], tmp[2]

        @printf("\n iEig = %2d  k = %10.6f  m = %10.6f  growth = %10.6f",
                iEig, float(k[ik]), float(m[indm]), max_growth)

        # 1D structures
        uvec            = zeros(ComplexF64, 1, Ny+1)
        vvec            = zeros(ComplexF64, 1, Ny+1)
        bvec            = zeros(ComplexF64, 1, Ny+1)
        uvec[1,:]       = modes[1:Ny+1, iEig, indm, ik]
        vvec[1,2:end-1] = modes[Ny+2:2*Ny, iEig, indm, ik]
        bvec[1,:]       = modes[2*Ny+1:3*Ny+1, iEig, indm, ik]

        # 2D structures
        U = real(repeat(uvec, Nz+1, 1) .* exp.(1im * ms[indm] * Z))
        V = real(repeat(vvec, Nz+1, 1) .* exp.(1im * ms[indm] * Z))
        B = real(repeat(bvec, Nz+1, 1) .* exp.(1im * ms[indm] * Z))

        u_plt = contour(Y/1e3, Z/1e3, U, title="u"; kwargs...)
        v_plt = contour(Y/1e3, Z/1e3, V, title="v"; kwargs...)
        b_plt = contour(Y/1e3, Z/1e3, B, title="b"; kwargs...)
        plt = plot(u_plt, v_plt, b_plt, layout=(1,3), size=(1600, 400))
        file = string(fileplot, iEig, ".png")
        savefig(plt, file)
    end
end

#=
# choose y label to be either y or ϕ, depending on the geometry
function plot_basic_state(grid, background, geometry, file)

    if geometry == Cartesian()
        y = grid.y/1e3
        xlabel = "y (km)"
    elseif geometry == Spherical()
        y = rad2deg.(grid.ϕ)
        xlabel = "ϕ (°)"
    end

    solution = background[geometry]

     p1 = plot(
         y,
         solution.u,
         linewidth = 3,
         label = "u",
         title = "Velocity",
         ylabel = "(m/s)",
         xlims = (y[end], y[1])
         )

     p2 = plot(
         y,
         solution.η,
         linewidth = 3,
         label = "η",
         xlabel = xlabel,
         ylabel = "(m)",
         title = "Free-Surface",
         xlims = (y[end], y[1])
         )

     plt1 = plot(p1, p2, layout = (2,1))
     savefig(plt1, file)
end

#ticks = collect(0:0.2:1)
#ticklabels = [ @sprintf("%5.1f",x) for x in ticks ]
#plot(x,y)
#plot!(xticks=(ticks,ticklabels))

function  make_tick_labels(fs, Nfs)
    fs_max = maximum([real(fs); imag(fs)])
    fs_min = minimum([real(fs); imag(fs)])
    dfs = (fs_max - fs_min)/Nfs

    ticks = collect(fs_min:dfs:fs_max)
    ticklabels = [ @sprintf("%f",f) for f in ticks]

    return ticks, ticklabels
end

function plot_1D_streamfunction(k_index, k, phys, σmodes, geometry, mode_number, file)

    if geometry == Cartesian()
        y = phys[geometry].grid.y/1e3
        xlabel = "y (km)"
    elseif geometry == Spherical()
        y = rad2deg.(phys[geometry].grid.ϕ)
        xlabel = "ϕ (°)"
    end

    N = phys[geometry].grid.N
    Lx = 2*pi/k;
    Nx = N;
     x = LinRange(0, Lx, Nx+1)
  X, Y = meshgrid(x,y)

  uvec = σmodes[(k,geometry)][    1:N+1,   mode_number];
  vvec = σmodes[(k,geometry)][  N+2:2*N+2, mode_number];
  ηvec = σmodes[(k,geometry)][2*N+1:3*N+1, mode_number];

    p1 = plot(
      y,
      real(uvec),
      linewidth = 3,
      label = "real",
      title = "u",
      yticks=make_tick_labels(uvec, 3),
      xlims = (y[end], y[1])
      )
  plot!(
      p1,
      y,
      linewidth = 3,
      imag(uvec),
      label = "imag")
  p2 = plot(
      y,
      real(vvec),
      linewidth = 3,
      label = "real",
      title = "v",
      yticks=make_tick_labels(vvec, 3),
       xlims = (y[end], y[1])
      )
  plot!(
      p2,
      y,
      linewidth = 3,
      imag(vvec),
      label = "imag")
  p3 = plot(
      y,
      real(ηvec),
      linewidth = 3,
      label = "real",
      title = "η",
      yticks=make_tick_labels(ηvec, 3),
      xlabel = xlabel,
      xlims = (y[end], y[1])
      )
  plot!(
      p3,
      y,
      linewidth = 3,
      imag(ηvec),
      label = "imag")
  plt = plot(p1, p2, p3, layout = (3,1))
  savefig(plt, file)
  #display(plt)

end

function plot_2D_streamfunction(k_index, k, phys, σmodes, geometry, mode_number, file)

    N = phys[geometry].grid.N
    Nx = N;
    Lx = 2*pi/k;
    x = LinRange(0, Lx, Nx+1)

    if geometry == Cartesian()
        y = phys[geometry].grid.y/1e3
        ylabel = "y (km)"
        x = x./1e3
        kscaled = k*1e3
        xlabel = "x (km)"
    elseif geometry == Spherical()
        y = rad2deg.(phys[geometry].grid.ϕ)
        ylabel = "ϕ (°)"
        x = rad2deg.(x)
        kscaled = k * π / 180
        xlabel = "λ (°)"
     end

  X, Y = meshgrid(x,y)

  uvec = σmodes[(k,geometry)][    1:N+1,   mode_number];
  vvec = σmodes[(k,geometry)][  N+2:2*N+2, mode_number];
  ηvec = σmodes[(k,geometry)][2*N+1:3*N+1, mode_number];

    u =     repeat(real(uvec),1,Nx+1).*cos.(kscaled*X) -    repeat(imag(uvec),1,Nx+1).*sin.(kscaled*X);
    v = -k.*repeat(imag(vvec),1,Nx+1).*cos.(kscaled*X) - k.*repeat(real(vvec),1,Nx+1).*sin.(kscaled*X);
    η =     repeat(real(ηvec),1,Nx+1).*cos.(kscaled*X) -    repeat(imag(ηvec),1,Nx+1).*sin.(kscaled*X);

    kwargs = (
        xlabel = "x (km)",
        ylabel = ylabel,
          fill = true,
        levels = 20,
     linewidth = 0,
         color = :balance,
      colorbar = true,
          xlim = (x[1],   x[end]),
          ylim = (y[end], y[1])
    )

    #print("x ticks = ", make_tick_labels(x, 2), "\n")

    u_plt = contour(x, y[end:-1:1], u[:, end:-1:1], title="u"; kwargs...)
    v_plt = contour(x, y[end:-1:1], v[:, end:-1:1], title="v"; kwargs...)
    η_plt = contour(x, y[end:-1:1], η[:, end:-1:1], title="η"; kwargs...)

    plt1 = plot(u_plt, v_plt, η_plt, layout = (1,3))
    savefig(plt1, file)

end

function plot_2D_vorticity(phys, k_index, k, σmodes, geometry, mode_number, file)

    N = phys[geometry].grid.N
    Lx = 2*pi/k;
    Nx = N;
    x = LinRange(0, Lx, Nx+1)

    if geometry == Cartesian()
        y = phys[geometry].grid.y/1e3
        ylabel = "y (km)"
        Dy = phys[geometry].grid.D
        x = x./1e3
        kscaled = k*1e3
        xlabel = "x (km)"
    elseif geometry == Spherical()
        y = rad2deg.(phys[geometry].grid.ϕ)
        ylabel = "ϕ (°)"
        Dy = phys[geometry].grid.D/phys[geometry].grid.a
        x = rad2deg.(x)
        kscaled = k * π / 180
        xlabel = "λ (°)"
    end

    X, Y = meshgrid(x,y)

  uvec = σmodes[(k,geometry)][    1:N+1,   mode_number];
  vvec = σmodes[(k,geometry)][  N+2:2*N+2, mode_number];

    ζvec = im * k * vvec - Dy * uvec
    ζ    = repeat(real(ζvec),1,Nx+1).*cos.(kscaled*X) - repeat(imag(ζvec),1,Nx+1).*sin.(kscaled*X);

    kwargs = (
        xlabel = xlabel,
        ylabel = ylabel,
          fill = true,
        levels = 20,
     linewidth = 0,
         color = :balance,
      colorbar = true,
          xlim = (  x[1], x[end]),
         ylim = (y[end],  y[1])
    )

    plt = contour(x, y[end:-1:1], ζ[:, end:-1:1], title="ζ"; kwargs...)

    savefig(plt, file)

end
=#

#string.(fieldnames(typeof(gridC)))

# Plot vertical slices as well?E


text:
#Random Sampling Baseline
clc()
h=waitbar(0,"sampling")
Nodes=3
sum=0
n=0

# create the seed
rainbow=zeros(Nodes,3)

# Load properties
load("TrialProperties10.mat")

# Result allocation
Result=zeros(10,3)

for trial=1:10
    A=TrialProp{trial,1}
    B=TrialProp{trial,2}
    C=TrialProp{trial,3}
    for Sample=1:1000
        while n<Nodes
            # Count
            n=n+1
            # Pass the added node from the tree search script()
            # Select Search Method
            run PureRandom.m

        end
        ## Evaluation
        # Evaluate the graph, this should probably be passed from another script()
        # For now do additive properties of single edges
        run ColorScore.m
        sum=sum()+sqrt(GraphScore[1]^2+GraphScore[2]^2+GraphScore[3]^2)
        rainbow=zeros(Nodes,3)
        n=0
        waitbar(Sample/1000,h,strcat("Sample_',num2str(trial),'_Exp_",num2str(Nodes)))
    end
    Result[trial,1]=sum()/1000
        sum=0
end

Nodes=5
for trial=1:10
    A=TrialProp{trial,1}
    B=TrialProp{trial,2}
    C=TrialProp{trial,3}
    for Sample=1:1000
        while n<Nodes
            # Count
            n=n+1
            # Pass the added node from the tree search script()
            # Select Search Method
            run PureRandom.m

        end
        ## Evaluation
        # Evaluate the graph, this should probably be passed from another script()
        # For now do additive properties of single edges
        run ColorScore.m
        sum=sum()+sqrt(GraphScore[1]^2+GraphScore[2]^2+GraphScore[3]^2)
        rainbow=zeros(Nodes,3)
        n=0
        waitbar(Sample/1000,h,strcat("Sample_',num2str(trial),'_Exp_",num2str(Nodes)))
    end
    Result[trial,2]=sum()/1000
        sum=0
end

Nodes=10
for trial=1:10
    A=TrialProp{trial,1}
    B=TrialProp{trial,2}
    C=TrialProp{trial,3}
    for Sample=1:1000
        while n<Nodes
            # Count
            n=n+1
            # Pass the added node from the tree search script()
            # Select Search Method
            run PureRandom.m

        end
        ## Evaluation
        # Evaluate the graph, this should probably be passed from another script()
        # For now do additive properties of single edges
        run ColorScore.m
        sum=sum()+sqrt(GraphScore[1]^2+GraphScore[2]^2+GraphScore[3]^2)
        rainbow=zeros(Nodes,3)
        n=0
        waitbar(Sample/1000,h,strcat("Sample_',num2str(trial),'_Exp_",num2str(Nodes)))
    end
    Result[trial,3]=sum()/1000
        sum=0
end
ControlTrials=Result
save("PureRandomControl.mat','ControlTrials")
    close(h)
Result

max_stars_repo_name: DesignEngrLab/ColorGraph

max_issues_repo_name: DesignEngrLab/ColorGraph

max_forks_repo_name: DesignEngrLab/ColorGraph


text:
struct NaturalPolicyGradient{DT<:AbstractFloat,S,P,V,VF,VOP}
    envsampler::S
    policy::P

    value::V
    valuefit!::VF
    value_feature_op::VOP

    Hmax::Int
    N::Int
    Nmean::Int

    norm_step_size::DT
    gamma::DT
    gaelambda::DT
    whiten_advantages::Bool
    bootstrapped_nstep_returns::Bool

    max_cg_iter::Int
    cg_tol::DT

    vanilla_pg::Vector{DT} # nparams
    natural_pg::Vector{DT} # nparams

    fvp_op::FVP{DT, Array{DT, 2}}
    cg_op::CG{DT}

    advantages_vec::Vector{DT} # N
    returns_vec::Vector{DT} # N
end

"""
    NaturalPolicyGradient{DT<:AbstractFloat}(args...; kwargs...) -> NaturalPolicyGradient
    NaturalPolicyGradient(args...; kwargs...) -> NaturalPolicyGradient

Construct an instance of `NaturalPolicyGradient` with `args` and `kwargs`, where
`DT <: AbstractFloat` is the element type used for pre-allocated buffers, which defaults to
Float32.

In the following explanation of the `NaturalPolicyGradient` constructor, we use the
following notation/definitions:
- `dim_o = length(observationspace(env))`
- `dim_a = length(actionspace(env))`
- "terminal" (e.g. terminal observation) refers to timestep `T + 1` for a length `T` trajectory.

# Arguments

- `env_tconstructor`: a function with signature `env_tconstructor(n)` that returns `n`
    instances of `T`, where `T <: AbstractEnvironment`.
- `policy`: a function mapping observations to actions, with the following signatures:
    - `policy(obs::AbstractVector)` --> `action::AbstractVector`,
        where `size(obs) == (dim_o, )` and `size(action) == (dim_a, )`.
    - `policy(obs::AbstractMatrix)` --> `action::AbstractMatrix`,
        where `size(obs) == (dim_o, N)` and `size(action) == (dim_a, N)`.
- `value`: a function mapping observations to scalar rewards, with the following signatures:
    - `value(obs::AbstractVector)` --> `reward::Real`, where `size(obs) == (dim_o, )`
    - `value(obs::AbstractMatrix)` --> `reward::AbstractVector`,
        where `size(obs) == (dim_o, N)` and `size(reward) == (N, )`.
- `valuefit!`: a function with signature `valuefit!(value, obs::AbstractMatrix, returns::AbstractVector)`,
    where `size(obs) == (dim_o, N)` and `size(returns) == (N, )`, that fits `value` to
    `obs` and `returns`.

# Keywords

- `Hmax::Integer`: Maximum trajectory length for environments rollouts.
- `N::Integer`: Total number of data samples used for each policy gradient step.
- `Nmean::Integer`: Total number of data samples for the mean policy (without stochastic
    noise). Mean rollouts are used for evaluating `policy` and not used to improve `policy`
    in any form.
- `norm_step_size::Real`: Scaling for the applied gradient update after gradient normalization
    has occured. This process makes training much more stable to step sizes;
    see equation 5 in [this paper](https://arxiv.org/pdf/1703.02660.pdf) for more details.
- `gamma::Real`: Reward discount, applied as `gamma^(t - 1) * reward[t]`.
- `gaelambda::Real`: Generalized Advantage Estimate parameter, balances bias and variance when
    computing advantages. See [this paper](https://arxiv.org/pdf/1506.02438.pdf) for details.
- `max_cg_iter::Integer`: Maximum number of Conjugate Gradient iterations when estimating
    `natural_gradient = alpha * inv(FIM) * gradient`, where `FIM` is the Fisher Information Matrix.
- `cg_tol::Real`: Numerical tolerance for Conjugate Gradient convergence.
- `whiten_advantages::Bool`: if `true`, apply statistical whitening to calculated advantages
    (resulting in `mean(returns) ≈ 0 && std(returns) ≈ 1`).
- `bootstrapped_nstep_returns::Bool`: if `true`, bootstrap the returns calculation starting
    `value(terminal_observation)` instead of 0. See "Reinforcement Learning" by
    Sutton & Barto for further information.
- `value_feature_op`: a function with the below signatures that transforms environment
    observations to a set of "features" to be consumed by `value` and `valuefit!`:
    - `value_feature_op(observations::AbstractVector{<:AbstractMatrix}) --> AbstractMatrix`
    - `value_feature_op(terminal_observations::AbstractMatrix, trajlengths::Vector{<:Integer}) --> AbstractMatrix`,
        where `observations` is a vector of observations from each trajectory,
        `terminal_observations` has size `(dim_o, number_of_trajectories)`, and `trajlengths`
        contains the lengths of each trajectory
        (such that `trajlengths[i] == size(observations[i], 2)`).

For some continuous control tasks, one may consider the following notes when applying
`NaturalPolicyGradient` to new tasks and environments:

1) For two policies that both learn to complete a task satisfactorially, the larger one
    may not perform significantly better. A minimum amount of representational power is
    necessary, but larger networks may not offer quantitative benefits. The same goes for
    the value function approximator.
2) `Hmax` needs to be sufficiently long for the correct behavior to emerge; `N` needs to be
    sufficiently large that the agent samples useful data. They may also be surprisingly
    small for simple tasks. These parameters are the main tunables when applying `NaturalPolicyGradient`.
3) One might consider the `norm_step_size` and `max_cg_iter` parameters as the next most
    important when initially testing `NaturalPolicyGradient` on new tasks, assuming `Hmax` and `N` are
    appropriately chosen for the task. `gamma` has interaction with `Hmax`,
    while the default value for `gaelambda` has been empirically found to be stable for a
    wide range of tasks.

For more details, see Algorithm 1 in
[Towards Generalization and Simplicity in Continuous Control](https://arxiv.org/pdf/1703.02660.pdf).
"""
function NaturalPolicyGradient{DT}(
    env_tconstructor,
    policy,
    value,
    valuefit!;
    Hmax::Integer = 1024,
    N::Integer = 10 * Hmax,
    Nmean::Integer = min(3*Hmax, N),
    norm_step_size::Real = 0.05,
    gamma::Real = 0.995,
    gaelambda::Real = 0.98,
    max_cg_iter::Integer = 15,
    cg_tol::Real = sqrt(eps(real(DT))),
    whiten_advantages::Bool = false,
    bootstrapped_nstep_returns::Bool = false,
    value_feature_op = nothing
) where {DT <: AbstractFloat}
    e = first(env_tconstructor(1))
    np = nparams(policy)
    z(d...) = zeros(DT, d...)

    0 < np || throw(ArgumentError("policy has no parameters"))
    0 < Hmax <= N || throw(ArgumentError("Hmax must be in interval (0, N]"))
    0 < N || throw(ArgumentError("N must be > 0"))
    0 < Nmean <= N || throw(ArgumentError("Nmean must be in interval (0, N]"))
    0 < norm_step_size || throw(ArgumentError("norm_step_size must be > 0"))
    0 < gamma <= 1 || throw(ArgumentError("gamma must be in interval (0, 1]"))
    0 < gaelambda <= 1 || throw(ArgumentError("gaelambda must be in interval (0, 1]"))
    0 < max_cg_iter || throw(ArgumentError("max_cg_iter must be > 0"))
    0 < cg_tol || throw(ArgumentError("cg_tol must be > 0"))

    envsampler = EnvironmentSampler(env_tconstructor, dtype = DT)
    fvp_op = FVP(z(np, N), true) # `true` computes FVPs with 1/N normalization
    cg_op = CG{DT}(np, N)

    NaturalPolicyGradient(
        envsampler,
        policy,
        value,
        valuefit!,
        value_feature_op,
        Hmax,
        N,
        Nmean,
        DT(norm_step_size),
        DT(gamma),
        DT(gaelambda),
        whiten_advantages,
        bootstrapped_nstep_returns,
        max_cg_iter,
        DT(cg_tol),
        z(np),
        z(np),
        fvp_op,
        cg_op,
        z(N),
        z(N)
    )
end

function NaturalPolicyGradient(args...; kwargs...)
    NaturalPolicyGradient{Float32}(args...; kwargs...)
end

function Base.iterate(npg::NaturalPolicyGradient{DT}, i = 1) where {DT}

    @unpack envsampler, policy, value, valuefit!, Hmax, N, gamma, gaelambda = npg
    @unpack vanilla_pg, natural_pg = npg
    @unpack advantages_vec, returns_vec = npg
    @unpack fvp_op, cg_op = npg

    # Perform rollouts with last policy
    elapsed_sample = @elapsed begin
        batch = @closure sample(envsampler, N, reset! = randreset!, Hmax = Hmax, dtype=DT) do a, o
            sample!(a, policy, o)
        end
    end

    @unpack O, A, R, oT = batch
    O_mat = SpecialArrays.flatten(O)::AbstractMatrix
    A_mat = SpecialArrays.flatten(A)::AbstractMatrix
    oT_mat = SpecialArrays.flatten(oT)::AbstractMatrix
    advantages  = batchlike(advantages_vec, batch)
    returns     = batchlike(returns_vec, batch)

    if npg.value_feature_op !== nothing
        # TODO change this
        feat_mat = npg.value_feature_op(O_mat)
        termfeat_mat = npg.value_feature_op(oT_mat, map(length, batch))
    else
        feat_mat = O_mat
        termfeat_mat = oT_mat
    end

    # Get baseline and terminal values for the current batch using the last value function
    baseline_vec = dropdims(value(feat_mat), dims = 1)
    baseline = batchlike(baseline_vec, batch)
    termvals = dropdims(value(termfeat_mat), dims = 1)
    R = batchlike(R, batch)

    # Compute normalized GAE advantages and returns
    GAEadvantages!(advantages, baseline, R, termvals, gamma, gaelambda)
    npg.whiten_advantages && whiten!(advantages_vec)
    if npg.bootstrapped_nstep_returns
        bootstrapped_nstep_returns!(returns, R, termvals, gamma)
    else
        nstep_returns!(returns, R, gamma)
    end

    # Fit value function to the current batch
    elapsed_valuefit = @elapsed foreach(noop, valuefit!(value, feat_mat, returns_vec))

    # Compute ∇log π_θ(at | ot)
    elapsed_gradll = @elapsed grad_loglikelihood!(
                                                  fvp_op.glls,
                                                  policy,
                                                  A_mat,
                                                  O_mat,
                                                 )

    # Compute the "vanilla" policy gradient as 1/T * grad_loglikelihoods * advantages_vec
    elapsed_vpg = @elapsed mul!(
                                vanilla_pg,
                                fvp_op.glls,
                                advantages_vec,
                                one(DT) / N,
                                zero(DT)
                               )

    # solve for natural_pg = FIM * vanilla_pg using conjugate gradients
    # where the full FIM is avoiding using Fisher Vector Products
    fill!(natural_pg, zero(DT))
    elapsed_cg = @elapsed cg_op(
                                natural_pg,
                                fvp_op,
                                vanilla_pg;
                                tol = npg.cg_tol,
                                maxiter = npg.max_cg_iter,
                                initiallyzero = true
                               )

    # update policy parameters: θ += alpha * natural_pg
    alpha = sqrt(npg.norm_step_size / dot(natural_pg, vanilla_pg))
    lmul!(alpha, natural_pg)
    any(isnan, natural_pg) && throw(DomainError(natural_pg, "NaN detected in gradient"))
    flatupdate!(policy, natural_pg)

    result = (
        elapsed = (
            sampled = elapsed_sample,
            gradll = elapsed_gradll,
            vpg = elapsed_vpg,
            cg = elapsed_cg,
            valuefit = elapsed_valuefit,
        ),
        batch = batch,
        vpgnorm = norm(vanilla_pg),
        npgnorm = norm(natural_pg),
    )

    return result, i + 1
end

@inline function batchlike(A::AbsVec, B::AbsVec{<:AbsVec}) # TODO document
    offsets = Vector{Int}(undef, length(B) + 1)
    offsets[1] = 0
    for i in LinearIndices(B)
        offset = offsets[i] + length(B[i])
        checkbounds(A, offset)
        offsets[i + 1] = offset
    end
    BatchedVector(A, offsets)
end

@inline function batchlike(A::AbsVec, B::LyceumBase.TrajectoryBuffer) # TODO document
    BatchedVector(A, copy(B.offsets))
end


text:
################################################################################################
# OpenCV.jl
#
# The OpenCV interface (C++) for Julia
#
# If OpenCV is already installed in your computer, or you wish to use different libraries,
# change the library and header paths to your local directories, e.g.,
#      /usr/local/lib/
#      /usr/local/include/
#
# To add more libraries, make sure to add the name to "opencv_libraries" in /src/OpenCV_libs.jl
#
# OpenCV lib version: OpenCV 3.2.0
#
# DOCUMENTATION
# opencv.org
# http://opencv.org
# Doxygen
# http://docs.opencv.org/master/index.html
#
#################################################################################################

# Convenient swap file extension function
swapext(f, new_ext) = "$(splitext(f)[1])$new_ext"

# Check Julia version before continuing (also checked later in Cxx)
(VERSION >= v"0.6-") ? nothing :
     throw(ErrorException("Julia $VERSION does not support C++ FFI"))

# Load Cxx
using Cxx

# Load config variables for loading OpenCV shared libraries
include(joinpath(Pkg.dir("OpenCV"), "./src/OpenCV_libs.jl"))
opencv_libraries = getFullLibNames();

# Autosearch for OpenCV installations using pkg-config
(so,si,pr) = readandwrite(`pkg-config --libs opencv`)
output = readstring(so)
close(so)

# Check lib installation path
@static if is_apple()
  failed_to_get_path = match(Regex(chop(cvlibdir)), output) == nothing
end

@static if is_linux()
  failed_to_get_path = match(Regex("libopencv|lopencv"), output) == nothing
end

# Check if all libraries specified in config are indeed installed
missing_libs = false
for i in libNames
    if !contains(output, i)
        missingmissing_libs = true
        println("$(i) is not found in pkg-config")
    end
end

# Load pre-built DYLD libraries (OSX ONLY), else throw an ErrorException
if failed_to_get_path || missing_libs
    cvlibdir = is_apple() ? joinpath(Pkg.dir("OpenCV"), "./deps/usr/lib/") : throw(ErrorException("No pre-installed libraries. Set path manually or install OpenCV."))
    cvheaderdir = is_apple() ? joinpath(Pkg.dir("OpenCV"), "./deps/usr/include/") : throw(ErrorException("No pre-installed headers. Set path manually or install OpenCV."))
end

addHeaderDir(cvheaderdir, kind = C_System)

# Load OpenCV shared libraries (default file extension is .dylib)
# TO DO: ensure compatible path/extension for Windows OS
for i in opencv_libraries
    if is_linux()
         i = swapext(i[1:end-6], ".so")
    end
    # Must link symbols accross libraries with RTLD_GLOBAL
    Libdl.dlopen(joinpath(cvlibdir,i), Libdl.RTLD_GLOBAL)
end

# Now include C++ header files
addHeaderDir(joinpath(cvheaderdir,"opencv2"), kind = C_System )
addHeaderDir(joinpath(cvheaderdir,"opencv2/core"), kind = C_System )
cxxinclude(joinpath(cvheaderdir,"opencv2/opencv.hpp"))
    # => opencv.hpp calls all the main headers
    #include "opencv2/core.hpp"
    #include "opencv2/imgproc.hpp"
    #include "opencv2/photo.hpp"
    #include "opencv2/video.hpp"
    #include "opencv2/features2d.hpp"
    #include "opencv2/objdetect.hpp"
    #include "opencv2/calib3d.hpp"
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/videoio.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/ml.hpp"

cxxinclude(joinpath(cvheaderdir,"opencv2/core/opengl.hpp"))            # enable OpenGL
cxxinclude(joinpath(cvheaderdir,"opencv2/core/ocl.hpp"))               # enable OpenCL
cxxinclude(joinpath(cvheaderdir,"opencv2/video/background_segm.hpp"))  # enable bg/fg segmentation
cxxinclude(joinpath(cvheaderdir,"opencv2/video/tracking.hpp"))         # enable tracking
cxxinclude(joinpath(cvheaderdir,"opencv2/shape.hpp"))
cxxinclude(joinpath(cvheaderdir,"opencv2/stitching.hpp"))
cxxinclude(joinpath(cvheaderdir,"opencv2/superres.hpp"))
cxxinclude(joinpath(cvheaderdir,"opencv2/videostab.hpp"))

# TO DO: we need to include paths for opencv-contrib folders/subfolders inside opencv main directory
# Currently system-specific!
# try
#     cxxinclude("/Users/maximilianosuster/opencv/opencv_contrib-master/modules/bgsegm/include/opencv2/bgsegm.hpp")
# catch
#     warn("opencv_contrib module headers could not be found, set the paths manually in src/OpenCV.jl to use advanced functions")
# end

# Include C++ headers
cxx"""
 #include <iostream>
 #include <unistd.h>
 #include <cstdlib>
 #include <cstdio>
 #include <cstddef>
 #include <cstring>
 #include <cfloat>
 #include <vector>
 #include <ctime>
 #include <map>
 #include <utility>
 #include <exception>
"""

# Load Qt framework
# BUG: see deps/Qt_support.jl, so currently disabled
# include(joinpath(Pkg.dir("OpenCV"), "./deps/Qt_support.jl"))

# Load header constants and typedefs
include(joinpath(Pkg.dir("OpenCV"), "./src/OpenCV_hpp.jl"))

# Load OpenCV bindings
include(joinpath(Pkg.dir("OpenCV"), "./src/OpenCV_modules.jl"))

# Load custom utility functions
include(joinpath(Pkg.dir("OpenCV"), "./src/OpenCV_util.jl"))

# Load Videoplayer
include(joinpath(Pkg.dir("OpenCV"), "./src/Videoprocessor.jl"))

# Load demos -- currently Cxx versions
function run_tests()
    include(joinpath(Pkg.dir("OpenCV"), "./test/cxx/demos.jl"))
end