text:
# Practical Review: Extremum Estimators (Ch. 13)

##
#= 	Let's start with a simple problem, where we can get the the answers using
	analytic methods. This will let us apply numeric methods, and we will be
	able to verify that they give us the correct results. To start, let's
	define some data, and do the basic OLS computations
=#
using Econometrics
n = 5
x = [ones(n) 1:n]
β = [10., -1.]
ϵ = randn(n)
y = x*β + ϵ
ols(y,x);

##

#= 	OK, now let's do this as we would if we did not have access to
	the analytic results. First, we compute the extremum estimator
	using numeric minimization.
=#
using Statistics, Optim
objᵢ = β -> (y-x*β).^2.  	# iᵗʰ obs contrib to sum of squares
obj = β -> mean(objᵢ(β))	# average objective
tol = 1e-08
βhat = Optim.optimize(obj, zeros(2), LBFGS(),
        	Optim.Options(g_tol = tol,x_tol=tol,f_tol=tol);
			autodiff=:forward).minimizer

##

#=	How do we get the estimated standard errors, using extremum estimation
	theory? We need estimators of ℐ, the covariance of the score
	contributions, and 𝒥, the Hessian matrix.
=#

# the calculations to get ℐ hat

# first, we need the score contributions, the matrix that collects
# the derivatives of each observation's contribution to the objective

# Note to self: remember crtl-enter will evaluate a single line.

using ForwardDiff
sc =  ForwardDiff.jacobian(objᵢ, βhat) # get the score contributions
# from theory, we know that sc = -2x.*ϵcat. Let's use this verify
# that the automatic differentiation worked.
sc - (-2x.*(y - x*βhat))

##

# Next, we use the score contributions to estimate ℐ
ℐhat = zeros(2,2)
for i = 1:n
	ℐhat .+= sc[i,:]*sc[i,:]'
end
ℐhat ./= n
# you could also use simply cov(sc), which converges to the same thing.

##

# next, we need the estimate of the limiting Hessian, which is just
# the Hessian of the objective function, at the estimate
𝒥hat = ForwardDiff.hessian(obj, βhat)

# We know that this should be 2x'x/n. Let's check:
2x'x/n
##

# now, we can compute the estimated standard errors, to compare to what we saw
# from the analytic results, above
using LinearAlgebra
v∞ = inv(𝒥hat)*ℐhat*inv(𝒥hat) # this is the estimate of the limiting var of √n(β-β⁰)
se = sqrt.(diag(v∞/n))   # to get small sample est. variance, divide by n

##

# the last problem is for a correctly specified model, Case I in the notes.
# Let's look at an incorrectly specified model, Case III in the notes, to
# verify that the extremum theory works here, too.

# Let's work with Problem 1, in the exercises at the end of Chapter 13.
# In this problem, the true model is quadratic, but we erroneously estimate
# a linear model. The problem asks for an analytic solution. Here, let's
# approach it numerically.
# generate data
using Plots
function dgp(n)
	x = sort(rand(n))
	y = 1. .- x.^2 + randn(n)
	x,y
end
x,y = dgp(100)
scatter(x,y, legend=false, title="y = 1 - x² + N(0,1)")

##

# Here's an OLS fit, just to have a look
using Econometrics
n = 100
x,y = dgp(n)
X = [ones(n) x] # define regressor matrix for linear approximation about 0
b, junk = ols(y,X)
fitted = X*b
plot!(x,fitted)

##

# We know that the pseudo-true parameter values are 7/6 and -1. Let's verify
# that the OLS estimates are asymptotically normally distributed about these
# values. To do this, we will construct asymptotic 100x(1-α)% confidence intervals,
# and verify that the pseudo-true values lie inside them approximately 100x(1-α)% of
# the times we repeat the procedure, at least when n is large enough
using LinearAlgebra, Statistics, Distributions
n = 20 				# try small and large values here,
					# to see accuracy of asymptotic approximation
reps = 10000
α = 0.05			# try out 90, 95 and 99% CIs
crit = quantile(Normal(),1-α/2)
β⁰ = [7/6; -1.]
inci = zeros(reps, 2)
for i = 1:reps
	x,y = dgp(n)
	X = [ones(n) x] # define regressor matrix for linear approximation about 0
	βhat, vβhat, junk = ols(y, X, silent=true)
	se = sqrt.(diag(vβhat))
	inci[i,:] = (β⁰ .>= βhat .- crit*se) .& (β⁰ .<= βhat .+ crit*se)
end
ci = Int64(100*(1-α))
println("Coverage of $ci% CIs")
mean(inci, dims=1) # these should be approximately 1-α, at least when n is large enough

##

# To conclude, this summary has shown that the extremum estimation theory is
# working for two cases, a correctly specified model, and an incorrectly specified
# model. We have seen how to get I and J, now to compute the asymptotic variance,
# and how to verify that confidence intervals (which are computed using I and J)
# have correct coverage, at least asymptotically.

max_stars_repo_name: mcreel/EconometricsNotes

max_issues_repo_name: mcreel/EconometricsNotes

max_forks_repo_name: mcreel/EconometricsNotes


text:
using ACE, Printf, BenchmarkTools

using ACE: evaluate, evaluate!, evaluate_d, evaluate_d!
using ACEbase: acquire_B!, acquire_dB!

TX = ACE.PositionState{Float64}
B1p = ACE.Utils.RnYlm_1pbasis()
Rn = B1p.bases[1]
cfg = ACEConfig(rand(TX, Rn, 30))

##

degrees = Dict(2 => [7, 10, 12],
               3 => [7, 9, 11] )

Adegrees = [7, 10, 13, 16, 19]

# degrees = Dict(2 => [7, 12, 17],
#                3 => [7, 11, 15],
#                4 => [7, 10, 13],
#                5 => [7, 9, 11] )

wL = 1.5

##

Agroup = BenchmarkGroup()
Agroup["evaluate"] = BenchmarkGroup()
Agroup["evaluate_d"] = BenchmarkGroup()
Agroup["evaluate!"] = BenchmarkGroup()
Agroup["evaluate_d!"] = BenchmarkGroup()

for deg in Adegrees
   local B1p
   Bsel = SparseBasis(; maxorder = 1, p = 1, default_maxdeg = deg,
                        weight = Dict(:n => 1.0, :l => wL))
   B1p = ACE.Utils.RnYlm_1pbasis(maxdeg = deg,
                                 maxL = ceil(Int, deg / wL),
                                 Bsel = Bsel)
   A = evaluate(B1p, cfg)
   dA = evaluate_d(B1p, cfg)

   Agroup["evaluate"][deg] = @benchmarkable evaluate($B1p, $cfg)
   Agroup["evaluate_d"][deg] = @benchmarkable evaluate_d($B1p, $cfg)
   Agroup["evaluate!"][deg] = @benchmarkable evaluate!($A, $B1p, $cfg)
   Agroup["evaluate_d!"][deg] = @benchmarkable evaluate_d!($dA, $B1p, $cfg)
end

##

Bgroup = BenchmarkGroup()
Bgroup["evaluate"] = BenchmarkGroup()
Bgroup["evaluate_d"] = BenchmarkGroup()
Bgroup["evaluate!"] = BenchmarkGroup()
Bgroup["evaluate_d!"] = BenchmarkGroup()

for ord = keys(degrees), deg in degrees[ord]
   local B1p
   Bsel = SparseBasis(; maxorder = 1, p = 1, default_maxdeg = deg,
                        weight = Dict(:n => 1.0, :l => wL))
   B1p = ACE.Utils.RnYlm_1pbasis(maxdeg = deg,
                                 maxL = ceil(Int, deg / wL),
                                 Bsel = Bsel)
   basis = ACE.SymmetricBasis(ACE.Invariant(), B1p, Bsel)
   B = acquire_B!(basis, cfg)
   dB = acquire_dB!(basis, cfg)

   Bgroup["evaluate"][ord, deg] = @benchmarkable evaluate($basis, $cfg)
   Bgroup["evaluate_d"][ord, deg] = @benchmarkable evaluate_d($basis, $cfg)
   Bgroup["evaluate!"][ord, deg] = @benchmarkable evaluate!($B, $basis, $cfg)
   Bgroup["evaluate_d!"][ord, deg] = @benchmarkable evaluate_d!($dB, $basis, $cfg)
end

##

basis_suite = BenchmarkGroup()
basis_suite["A"] = Agroup
basis_suite["B"] = Bgroup

##

@info("Tune")
tune!(basis_suite)

@info("Run")
results = run(basis_suite, verbose = true)

ext: jl


text:
# [src/rk4.jl]

"""
Runge-Kutta 4th order integration method.

Implements numerical propagation of an ordinary differential equation
from some initial value over the desired range. This function is similar
to MATLAB's variable-step ODE propagators (e.g., ode45), but uses a
fixed step method. This is useful either when one knows an appropriate
step size or when a process is interrupted frequently (ode45 and the
similar functions in MATLAB will always make at least a certain number of
steps between ts(1) and ts(2), which may be very many more than are
necessary).

    t, x = rk4(ode, ts, x0, options);

Inputs:

    ode     Ordinary differential equation function
    ts      Time span, [t_start, t_end]
    x0      Initial state (column vector)
    options One can specify an options structure instead of dt
            so that this function is compatible with ode45 and its ilk. The
            only valid fields are MaxStep (the time step) and OutputFcn
            (if dt is passed in rather than options, an empty ODE_SET is initialized
            with the provided time step dt)

Outputs:

    t       Time history
    x       State history, with each row containing the state corresponding to
                the time in the same row of t.

Example:

    # Simulate an undamped harmonic oscillator for 10s with a 0.1s time
    #     step, starting from an initial state of [1; 0]:

    ode = (t, x) -> [-x[2]; x[1]]
    t, x = rk4(ode, [0 10], [1; 0], 0.1);
    plot(t, x);

See also: odehybrid, ode45, odeset, rkfixed, rkadapt.

Online doc: http://www.anuncommonlab.com/doc/odehybrid/rk4.html

Copyright 2014 An Uncommon Lab
"""
function rk4(ode, ts, x0, options::ODE_SET)

    dt = options.MaxStep
    if isempty(dt)
        @error "Rk4: Specify the time step with the MaxStep option."
    end

    # Time history, making sure the end time is explicity represented.
    t  = (ts[1]:dt:ts[end]);
    if t[end] != ts[end]
        t[end+1, 1] = ts[end];
    end

    ns = maximum(size(t));            # Number of samples
    nx = length(x0);                  # Number of states
    x  = [x0[:]'; zeros(ns-1, nx)];   # State history
    xk = x0[:];                       # Current state
    hdt = 0.5 * dt;                   # Half of the time step

    # If the user provided an OutputFcn, call it.
    if (options.OutputFcn != [])
        options.OutputFcn(ts, x0, "init");
    end

    # Propagate from each k to k+1.
    for k = 1:ns-1

        # The last sample may be cut short to have the correct end time.
        if k == ns-1
            dt = t[k+1] - t[k];
            hdt = 0.5 * dt;
        end

        # From k to k+1
        tk = t[k];                                        # Current time
        k1 = ode(tk,       xk);                           # RK derivatives
        k2 = ode(tk + hdt, xk + hdt * k1);                # ...
        k3 = ode(tk + hdt, xk + hdt * k2);
        k4 = ode(tk +  dt, xk +  dt * k3);
        xk = xk + (k1 + 2 * k2 + 2 * k3 + k4) * (dt / 6); # Updated state
        x[k+1, :] = xk[:];                                # Store.

        # If the user provided an OutputFcn, call it.
        if (options.OutputFcn != [])
            if options.OutputFcn(t[k+1], xk, "");
                t = t[1:k+1];
                x = x[1:k+1, :];
                break;
            end
        end

    end

        # If the user provided an OutputFcn, call it.
    if (options.OutputFcn != [])
        options.OutputFcn([], [], "done");
    end

    return t, x
end

function rk4(ode, ts, x0, dt)
    """
        Wrapper function for RK4 when only dt is specified, rather than a full ODE_SET
            t, x = rk4(ode, ts, x0, dt);
    """

    options = ODE_SET()
    options.MaxStep = dt

    return rk4(ode, ts, x0, options)
end


text:
include("./src/MyPlots.jl")
import .MyPlots as plo

plo.plot()

max_stars_repo_name: applied-math-coding/article-snippets

max_issues_repo_name: applied-math-coding/article-snippets

max_forks_repo_name: applied-math-coding/article-snippets


text:
N =

if (N % 3 == 0) & (N % 5 == 0)
    println("FizzBuzz")
elseif N % 3 == 0
    println("Fizz")
elseif N % 5 == 0
    println("Buzz")
else
    println(N)
end

x =
y =

if x > y
    x
else
    y
end

(x > y) ? x : y

(x > y) && println(x)

(x < y) && println(y)

max_stars_repo_name: miguelraz/JuliaTutorials

max_issues_repo_name: miguelraz/JuliaTutorials

max_forks_repo_name: miguelraz/JuliaTutorials


text:
function constrained_output_elimination(f_map::Dict{A, Set{B}}) where {A, B}
	copied_f_map = copy(f_map)
	constrained_output_elimination!(copied_f_map)
	copied_f_map
end

function constrained_output_elimination!(f_map::Dict{A, Set{B}}) where {A, B}
	f_pairs = collect(f_map)
	mapped_length = x->length(x.second)
	sort!(f_pairs; by=mapped_length)

	i = 1
	while i <= length(f_pairs)
		f_pair = f_pairs[i]
		potential_fully_connected = f_pairs[searchsorted(f_pairs, f_pair; by=mapped_length)]
		fully_connected = filter(potential_fully_connected) do p
			isequal(p.second, f_pair.second)
		end

		if length(fully_connected) == mapped_length(f_pair)
			f_pair_remove_from = setdiff(f_pairs, fully_connected)
			for p ∈ f_pair_remove_from
				p_beforeremove = length(p.second)
				setdiff!(p.second, f_pair.second)
				if length(p.second) < p_beforeremove
					i = 1  # can possibly be changed to the lowest index with lowest length modified, but this is nontrivial to find (requires a search)
					sort!(f_pairs; by=mapped_length)
				end
			end
        end

		i += 1
	end

	nothing
end


text:
title = "LyceumMuJoCo"
weight = 29


text:
datapath = joinpath(@__DIR__, "..", "data")
figpath = joinpath(@__DIR__, "..", "figures")

experiment_order = [:Raby07_breakfastchoice, :Raby07_planningforbreakfast,
                    :Cheke11_planning, :Correia07_exp2, :deKort07_exp2,
                    :deKort07_exp3, :deKort07_exp4, :Clayton05_exp1,
                    :Clayton05_exp2, :Clayton05_exp3, :Clayton05_exp4,
                    :Clayton99A_exp1, :Clayton99A_exp2, :Clayton99B_exp1,
                    :Clayton99B_exp2, :Clayton0103, :Clayton01_exp1,
                    :Clayton01_exp2, :Clayton01_exp3, :Clayton01_exp4,
                    :Clayton03_exp1, :Clayton03_exp2, :deKort05,
                    :Cheke11_specsat, :Correia07_exp1, :Clayton99C_exp1,
                    :Clayton99C_exp2, :Clayton99C_exp3, :Watanabe05,
                    :deKort07_exp1]
model_order = [:ReplayAndPlan, :PlasticCaching,
               :EpisodicLikeMemory, :MotivationalControl, :Baseline]

function loadmodel(model, experiment, id, rev; datapath = datapath)
    load(joinpath(datapath, join([model, experiment, id, rev], "_")))
end

function loadresult(filename)
    open(joinpath(datapath, filename)) do f
        s = ZstdDecompressorStream(f)
        res = BSON.load(s)[:results]
        close(s)
        res
    end
end

function prepare_plot(res; baseline = :self, baselinemodel = :ReplayAndPlan)
    sort!(res, :logp_hat)
    sort!(res, :experiment, by = x -> findfirst(==(x), experiment_order))
    sort!(res, :model, by = x -> findfirst(==(x), model_order))
    best = combine(groupby(res, [:model, :experiment]),
                   names(res) .=> x -> [last(x)], renamecols = false)
    if baseline != :none
        baseline = baseline == :self ? best : baseline
        nmodels = length(union(best.model))
        idxs = baseline.model .== baselinemodel
        best.logp_hat_rel = best.logp_hat .- repeat(baseline.logp_hat[idxs], nmodels)
        best.logp_hat_std_rel = sqrt.(best.logp_hat_std.^2 .+ repeat(baseline.logp_hat_std[idxs], nmodels).^2)
        best.logp_hat_std_rel[idxs] .= 0
        groups = groupby(best[(baseline == best ? best.model .!= baselinemodel : :), :], :model)
        y = [g.logp_hat_rel for g in groups]
        err = [g.logp_hat_std_rel ./ sqrt.(g.rep) for g in groups]
    else
        groups = groupby(best, :model)
        y = [g.logp_hat for g in groups]
        err = [g.logp_hat_std ./ sqrt.(g.rep) for g in groups]
    end
    (best = best, groups = groups, y = y, err = err)
end

function toname(e)
    e == :Raby07_planningforbreakfast && return "Raby07 planning"
    e == :Raby07_breakfastchoice && return "Raby07 choice"
    e == :Clayton0103 && return "Clayton01\\&Clayton03"
    replace(string(e), "_" => " ")
end

function labelaccessor(e, tests)
    vcat([FoodCachingExperiments.EXPERIMENTS[e].tests[test.id].labels .== "A"
          for test in tests.tests]...)
end
samebinarysignificance(x::Number, y::Number) = (x < 4 && y < 4) || (x > 3 && y > 3)
function samebinarysignificance(e, data)
    tests = FoodCachingExperiments.statistical_tests(e, data)
    l = labelaccessor(e, tests)
    tt = FoodCachingExperiments.asdataframe(e, target(e))[2]
    t = FoodCachingExperiments.significancecode.(tests.pvalues)
    s = samebinarysignificance.(t, tt)
    (all(s[l]), mean(s[l]), mean(s), s, l, tests.pvalues)
end

function runbestparams(e, model, bestseed; reseed = true)
    Random.seed!(bestseed)
    ms = rand(model, nbirds(e))
    reseed && Random.seed!(time_ns())
    run!(e, ms)
end

function concat_experiments(e)
    if hasproperty(e[1], :id)
        for i in eachindex(e)
            e[i].id .+= 100 * i
        end
    end
    vcat(e...)
end
function run_sigtests(data; loadmodel = loadmodel, Ninner = 1, Nouter = 10^3)
    sigresults = DataFrame(model = [], experiment = [],
                           samebinarysignificance = [],
                           meansamebinarysigA = [],
                           meansamebinarysig = [],
                           s = [], l = [], pvalues = [])
    for row in eachrow(data)
        model, e = row.model, row.experiment
        id = row.id
        rev = hasproperty(row, :rev) ? row.rev : "indi"
        @show model, e
        m = loadmodel(model, e, id, rev)
        for k in 1:Nouter
    #         data = runbestparams(e, m, s, reseed = k != 1)
            d = [runbestparams(e, m, rand(UInt)) for _ in 1:Ninner]
            if e == :Clayton0103
                data = [concat_experiments([d[i][j] for i in 1:Ninner]) for j in 1:6]
                for (i, ei) in enumerate(FoodCachingExperiments.CLAYTON0103_EXPERIMENTS)
                    push!(sigresults, [model, ei, samebinarysignificance(ei, data[i])...])
                end
            else
                data = concat_experiments(d)
                s = samebinarysignificance(e, data)
                push!(sigresults, [model, e, s...])
            end
        end
    end
    sigresults.ratio_same_rejects = ratio_same_rejects.(eachrow(sigresults))
    sigresults.atleasttwo = [mean(s[l]) > .5 for (s, l) in zip(sigresults.s, sigresults.l)]
    sigresults
end

function rm_test_locals(e)
    for (k, c) in e.tests
        e.tests[k] = FoodCachingExperiments.TestSummary(c.pvalues,
                                 c.keys,
                                 c.df,
                                 c.labels,
                                 c.statistic,
                                 c.value,
                                 Base.RefValue{Any}(),
                                 Base.RefValue{Any}())
    end
    e
end
function BY(α, p; α₁ = (p, m, α) -> .05, arbitrary_dependence = false)
    p = sort(p)
    m = length(p)
    c = arbitrary_dependence ? 1 : sum(1/i for i in 1:m)
    for k in 1:m
        if p[k] > k/(m * c) * α
            if k == 1
                return α₁(p[1], m, α)
            else
                return p[k-1]
            end
        end
    end
    p[end]
end
function rejects(e; α = .05)
    α != .05 && error("Not implemented for $α ≂̸ 0.05.")
    FoodCachingExperiments.asdataframe(e, target(e))[2] .< 4
end
same_rejects(e, pvalues; α = .05) = pvalues[rejects(e; α)[1:length(pvalues)]] .<= α
function ratio_same_rejects(row; corrected = false)
    e = EXPERIMENTS[row.experiment]
    sr = same_rejects(e, row.pvalues,
                      α = corrected ? BY(.2, pvalues(e)) : .05)
    length(sr) > 0 ? mean(sr) : 1.
end


text:
"""
    commjoin(c1::CommunityProfile, comms::CommunityProfile...)

Join multiple `CommunityProfile`s, creating a new `CommunityProfile`.
For now, sample names cannot overlap in any of the input profiles.
"""
function commjoin(c1::CommunityProfile, comms::CommunityProfile...)
    length(intersect(samplenames(c1), samplenames.(comms)...)) == 0 || error("Duplicate sample names detected: $(intersect(samplenames(c1), samplenames.(comms)...))")

    all_samples = vcat(samples(c1), samples.(comms)...)
    sample_dict = dictionary(zip(all_samples, eachindex(all_samples)))
    all_features = unique(vcat(features(c1), features.(comms)...))
    feature_dict = dictionary(zip(all_features, eachindex(all_features)))

    mat = spzeros(length(all_features), length(all_samples))
    for comm in (c1, comms...)
        for sample in samples(comm)
            for feature in features(comm)
                mat[feature_dict[feature], sample_dict[sample]] = comm[feature, sample]
            end
        end
    end

    return CommunityProfile(mat, all_features, all_samples)
end


text:
push!(LOAD_PATH, "../src/")

using Documenter, Deductive

makedocs(
    sitename="Deductive Documentation",
    modules = [Deductive],
    pages = [
        "index.md",
        "Propositional Logic" => "propositional_logic.md",
        "Internals" => [
            "Expressions" => "internals/expressions.md",
            "Proof Utilities" => "internals/proof_utilities.md",
            "Assertion Proofs" => "internals/assertion_proofs.md",
            "Indexes" => "internals/indexes.md"
        ]
    ]
)

deploydocs(
    repo = "github.com/ctrekker/Deductive.jl.git",
)


text:
using PlotlyJS

function bar1()
    data = bar(;x=["giraffes", "orangutans", "monkeys"],
               	y=[20, 14, 23])
    plot(data)
end

function bar2()
    trace1 = bar(;x=["giraffes", "orangutans", "monkeys"],
                  y=[20, 14, 23],
                  name="SF Zoo")
    trace2 = bar(;x=["giraffes", "orangutans", "monkeys"],
                  y=[12, 18, 29],
                  name="LA Zoo")
    data = [trace1, trace2]
    layout = Layout(;barmode="group")
    plot(data, layout)
end

function bar3()
    trace1 = bar(;x=["giraffes", "orangutans", "monkeys"],
                  y=[20, 14, 23],
                  name="SF Zoo")
    trace2 = bar(x=["giraffes", "orangutans", "monkeys"],
                 y=[12, 18, 29],
                 name="LA Zoo")
    data = [trace1, trace2]
    layout = Layout(;barmode="stack")
    plot(data, layout)
end

function bar4()
    data = bar(;x=["Product A", "Product B", "Product C"],
                y=[20, 14, 23],
                text=["$(i)% market share" for i in rand(15:30, 3)],
                marker=attr(color="rgb(158, 202, 225)", opacity=0.6),
                line=attr(color="rgb(8, 48, 107)", width=1.5))

    layout = Layout(;title="January 2013 Sales Report")

    plot(data, layout)
end

function bar5()
    x_value = ["Product A", "Product B", "Product C"]
    y_value = [20, 14, 23]

    data = bar(;x=x_value,
                y=y_value,
                text=["$(i)% market share" for i in rand(15:30, 3)],
		        marker=attr(color="rgb(158, 202, 225)", opacity=0.6,
                            line=attr(color="rgb(8, 48, 107)", width=1.5)))

    annotations = []

    for i in 1:length(x_value)
        result = attr(x=x_value[i],
                      y=y_value[i],
                      text=y_value[i],
                      xanchor="center",
                      yanchor="bottom",
                      showarrow=false)
        push!(annotations, result)
    end

    layout = Layout(;title="January 2013 Sales Report",
                    annotations=annotations)
    plot(data, layout)
end

function bar6()
    trace1 = bar(;x=["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
                     "Sep", "Oct", "Nov", "Dec"],
                  y=[20, 14, 25, 16, 18, 22, 19, 15, 12, 16, 14, 17],
                  name="Primary Product",
                  marker_color="rgb(49, 130, 189)",
                  opacity=0.7)
    trace2 = bar(;x=["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
                     "Sep", "Oct", "Nov", "Dec"],
                  y=[19, 14, 22, 14, 16, 19, 15, 14, 10, 12, 12, 16],
                  name="Secondary Product",
		  marker=attr(color="rgb(204, 204, 204)", opacity=0.5))
    data = [trace1, trace2]
    layout = Layout(;title="2013 Sales Report",
                     xaxis_tickangle=-45,
                     barmode="group")
    plot(data, layout)
end

function bar7()
    data = bar(;x=["Feature $(s)" for s in 'A':'E'],
                y=[20, 14, 23, 25, 22],
    		    marker_color=["rgba(204, 204, 204, 1)",
                              "rgba(222, 45, 38, 0.8)",
                              "rgba(204, 204, 204, 1)",
                              "rgba(204, 204, 204, 1)",
                              "rgba(204, 204, 204, 1)"])
    layout = Layout(;title="Least Used Feature")
    plot(data, layout)
end

function bar8()
    data = bar(;x=["Liam", "Sophie", "Jacob", "Mia", "William", "Olivia"],
                y=[8.0, 8.0, 12.0, 12.0, 13.0, 20.0],
                text=["4.17 below the mean", "4.17 below the mean",
                      "0.17 below the mean", "0.17 below the mean",
                      "0.83 above the mean", "7.83 above the mean"],
                marker_color="rgb(142, 124, 195)")
    layout = Layout(;title="Number of Graphs Made this Week",
                     font_family="Raleway, sans-serif",
                     showlegend=false,
                     xaxis_tickangle=-45,
                     yaxis=attr(zeroline=false, gridwidth=2),
                     bargap=0.05)
    plot(data, layout)
end

function bar9()
    trace1 = bar(;x=1995:2012,
                  y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263, 350,
                     430, 474, 526, 488, 537, 500, 439],
                  name="Rest of world",
                  marker_color="rgb(55, 83, 109)")
    trace2 = bar(;x=1995:2012,
                  y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
                     299, 340, 403, 549, 499],
                  name="China",
                  marker_color="rgb(26, 118, 255)")

    data = [trace1, trace2]

    layout = Layout(;title="US Export of Plastic Scrap",
		             xaxis=attr(tickfont_size= 14,
                                tickfont_color="rgb(107, 107, 107)"),
		             yaxis=attr(title="USD (millions)",
                                titlefont=attr(size=16,
                                               color="rgb(107, 107, 107)"),
                                tickfont=attr(size=14,
                                              color="rgb(107, 107, 107)")),
                     legend=attr(x=0, y=1.0, bgcolor="rgba(255, 255, 255, 0)",
                                 bordercolor="rgba(255, 255, 255, 0)"),
                     barmode="group",
                     bargap=0.15,
                     bargroupgap=0.1)
    plot(data, layout)
end

function bar10()
    x_data = ["Product Revenue", "Services Revenue", "Total Revenue",
              "Fixed Costs", "Variable Costs", "Total Costs", "Total"]
    y_data = [400, 660, 660, 590, 400, 400, 340]
    textList = ["\$430K", "\$260K", "\$690K", "\$-120K", "\$-200K", "\$-320K",
                "\$370K"]

    #Base
    trace1 = bar(;x=x_data,
                  y=[0, 430, 0, 570, 370, 370, 0],
                  marker_color="rgba(1, 1, 1, 0.0)")

    #Revenue
    trace2 = bar(;x=x_data,
                  y=[430, 260, 690, 0, 0, 0, 0],
                  marker_color="rgba(55, 128, 191, 0.7)",
		  line=attr(color="rgba(55, 128, 191, 1.0)", width=2))

    #Cost
    trace3 = bar(;x=x_data,
                  y=[0, 0, 0, 120, 200, 320, 0],
		  marker=attr(color="rgba(219, 64, 82, 0.7)",
		  line=attr(color="rgba(219, 64, 82, 1.0)", width=2)))

    #Profit
    trace4 = bar(;x=x_data,
                  y=[0, 0, 0, 0, 0, 0, 370],
		  marker=attr(color="rgba(50, 171, 96, 0.7)",
		  line=attr(color="rgba(50, 171, 96, 1.0)", width=2)))

    data = [trace1, trace2, trace3, trace4]

    annotations = []
    for i in 1:7
        result = attr(x=x_data[i],
                      y=y_data[i],
		              text=textList[i],
		              font=attr(;family="Arial", font_size=14,
                                 font_color="rgba(245, 246, 249, 1)"),
                                 showarrow=false)
        push!(annotations, result)
    end

    layout = Layout(;title="Annual Profit 2015",
                     barmode="stack",
                     paper_bgcolor="rgba(245, 246, 249, 1)",
                     plot_bgcolor="rgba(245, 246, 249, 1)",
                     width=600,
                     height=600,
                     showlegend=false,
                     xaxis_showtickabels=true,
                     annotations=annotations)

    plot(data, layout)
end

function bar11()
    trace1 = bar(;x=[1, 2, 3, 4],
                  y=[1, 4, 9, 16],
                  name="Trace1")
    trace2 = bar(;x=[1, 2, 3, 4],
                  y=[6, -8, -4.5, 8],
                  name="Trace2")
    trace3 = bar(;x=[1, 2, 3, 4],
                  y=[-15, -3, 4.5, -8],
                  name="Trace3")
    trace4 = bar(;x=[1, 2, 3, 4],
                  y=[-1, 3, -3, -4],
                  name="Trace4")
    data = [trace1, trace2, trace3, trace4]
    layout = Layout(;xaxis_title="X axis",
                     yaxis_title="Y axis",
                     barmode="relative",
                     title="Relative Barmode")
    plot(data, layout)
end


text:
module RealTime

export
    get_message,
    get_data_source,
    line2dict,
    csv2dict,
    keys_ok

include("conversions.jl")
include("accessors.jl")

end

max_stars_repo_name: andrewadare/realtime

max_issues_repo_name: andrewadare/realtime

max_forks_repo_name: andrewadare/realtime


text:
_compare(a, b)::Bool = a == b

_compare(a::Number, b::Number)::Bool = isapprox(a, b)
_compare(a::Missing, b::Number)::Bool = false
_compare(a::Number, b::Missing)::Bool = false
_compare(a::Missing, b::Missing)::Bool = false

function _get_property(m, field)
    try
        result = getproperty(m, field)
        return result
    catch ex
        # showerror(stderr, ex)
        # Base.show_backtrace(stderr, catch_backtrace())
        @debug("Ignoring exception", exception=(ex, catch_backtrace()))
    end
    return nothing
end

function _fix_vector_type(original_vector::AbstractVector)
    new_vector = vec([original_vector...])
    return new_vector
end

function _is_iterable(::Type{<:Number})::Bool
    return false
end

function _is_iterable(::Type{<:Char})::Bool
    return false
end

function _is_iterable(::Type{T})::Bool where T
    return hasmethod(iterate, (T,))
end

function _has_isassigned(::Type{T})::Bool where T
    return hasmethod(isassigned, (T, Int,))
end

function _is_indexable(::Type{<:Number})::Bool
    return false
end

function _is_indexable(::Type{<:Char})::Bool
    return false
end

function _is_indexable(::Type{T})::Bool where T
    return hasmethod(length, (T,))
end


text:
using Adapt

import Oceananigans.BoundaryConditions: fill_halo_regions!

#####
##### AbstractReducedField stuff
#####

abstract type AbstractReducedField{X, Y, Z, A, G, T, N} <: AbstractDataField{X, Y, Z, A, G, T, 3} end

const ARF = AbstractReducedField

fill_halo_regions!(field::AbstractReducedField, arch, args...) =
    fill_halo_regions!(field.data, field.boundary_conditions, arch, field.grid, args...; reduced_dimensions=field.dims)

const DimsType = NTuple{N, Int} where N

function validate_reduced_dims(dims)
    dims = Tuple(dims)

    # Check type
    dims isa DimsType || error("Reduced dims must be an integer or tuple of integers.")

    # Check length
    length(dims) > 3  && error("Models are 3-dimensional. Cannot reduce over 4+ dimensions.")

    # Check values
    all(1 <= d <= 3 for d in dims) || error("Dimensions must be one of 1, 2, 3.")

    return dims
end

function validate_reduced_locations(X, Y, Z, dims)
    loc = (X, Y, Z)

    # Check reduced locations
    for i in dims
        isnothing(loc[i]) || ArgumentError("The location of reduced dimensions must be Nothing")
    end

    return nothing
end

#####
##### Concrete ReducedField
#####

struct ReducedField{X, Y, Z, A, D, G, T, N, B} <: AbstractReducedField{X, Y, Z, A, G, T, N}
                   data :: D
           architecture :: A
                   grid :: G
                   dims :: NTuple{N, Int}
    boundary_conditions :: B

    """
        ReducedField{X, Y, Z}(data, grid, dims)

    Returns a `ReducedField` at location `(X, Y, Z)` with `data` on `grid`
    that is reduced over the dimensions in `dims`.
    """
    function ReducedField{X, Y, Z}(data::D, arch::A, grid::G, dims, bcs::B) where {X, Y, Z, A, D, G, B}

        dims = validate_reduced_dims(dims)
        validate_reduced_locations(X, Y, Z, dims)
        validate_field_data(X, Y, Z, data, grid)

        N = length(dims)
        T = eltype(grid)

        return new{X, Y, Z, A, D, G, T, N, B}(data, arch, grid, dims, bcs)
    end
end

"""
    ReducedField(X, Y, Z, arch, grid; dims, data=nothing, boundary_conditions=nothing)

Returns a `ReducedField` reduced over `dims` on `grid` and `arch`itecture with `boundary_conditions`.

The location `(X, Y, Z)` may be the parent, three-dimension location or the reduced location.

If `data` is specified, it should be an `OffsetArray` with singleton reduced dimensions;
otherwise `data` is allocated.

If `boundary_conditions` are not provided, default boundary conditions are constructed
using the reduced location.
"""
function ReducedField(Xr, Yr, Zr, arch, grid; dims, data=nothing,
                      boundary_conditions=nothing)

    dims = validate_reduced_dims(dims)

    # Reduce non-reduced dimensions
    X, Y, Z = reduced_location((Xr, Yr, Zr); dims=dims)

    if isnothing(data)
        data = new_data(arch, grid, (X, Y, Z))
    end

    if isnothing(boundary_conditions)
        boundary_conditions = FieldBoundaryConditions(grid, (X, Y, Z))
    end

    return ReducedField{X, Y, Z}(data, arch, grid, dims, boundary_conditions)
end

ReducedField(Lr, arch, grid; dims, kwargs...) = ReducedField(Lr..., arch, grid; dims=dims, kwargs...)

# Canonical `similar` for AbstractReducedField
Base.similar(r::AbstractReducedField{X, Y, Z, Arch}) where {X, Y, Z, Arch} =
    ReducedField(X, Y, Z, Arch(), r.grid; dims=r.dims, boundary_conditions=r.boundary_conditions)

#####
##### ReducedField utils
#####

reduced_location(loc; dims) = Tuple(i ∈ dims ? Nothing : loc[i] for i in 1:3)

Adapt.adapt_structure(to, reduced_field::ReducedField{X, Y, Z}) where {X, Y, Z} =
    ReducedField{X, Y, Z}(adapt(to, reduced_field.data), nothing, adapt(to, reduced_field.grid), reduced_field.dims, nothing)


text:
test01_eigvals = [
    -0.15883100381194412 - 13.264252860693972im
    -0.15883100381194412 + 13.264252860693972im
]

test01_eigvals_psat = [
    -0.15883 - 13.2643im
    -0.15883 + 13.2643im
]

test02_eigvals = [
    -999.9999922288165 + 0.0im
    -999.9999824863403 + 0.0im
    -5.667091663572526 + 0.0im
    -4.939539293199701 - 7.861631594468724im
    -4.939539293199701 + 7.861631594468724im
    -4.653008913694498 + 0.0im
    -4.144476689106577 - 7.59715123723563im
    -4.144476689106577 + 7.59715123723563im
    -1.3798285681206308 - 16.03637615711796im
    -1.3798285681206308 + 16.03637615711796im
    -0.9405263132225796 - 13.949752815351614im
    -0.9405263132225796 + 13.949752815351614im
    -0.4805073944481576 - 0.5379518986751338im
    -0.4805073944481576 + 0.5379518986751338im
    -0.27347592115017016 - 0.4565372544427486im
    -0.27347592115017016 + 0.4565372544427486im
]

test02_eigvals_psat = [
    -999.999990000000 + 0.00000000000000im
    -999.999980000000 + 0.00000000000000im
    -5.67379000000000 + 0.00000000000000im
    -4.93393000000000 - 7.85614000000000im
    -4.93393000000000 + 7.85614000000000im
    -4.64127000000000 + 0.00000000000000im
    -4.14297000000000 - 7.59557000000000im
    -4.14297000000000 + 7.59557000000000im
    -1.36440000000000 - 14.5583400000000im
    -1.36440000000000 + 14.5583400000000im
    -0.967800000000000 - 12.6558000000000im
    -0.967800000000000 + 12.6558000000000im
    -0.471260000000000 - 0.579790000000000im
    -0.471260000000000 + 0.579790000000000im
    -0.283050000000000 - 0.460360000000000im
    -0.283050000000000 + 0.460360000000000im
]

test03_eigvals = [
    -1000.0000000405296 + 0.0im
    -1000.0000000190737 + 0.0im
    -56.28815836405992 + 0.0im
    -53.12282741877609 + 0.0im
    -5.957028010206276 + 0.0im
    -5.02141964268554 - 7.898682332162664im
    -5.02141964268554 + 7.898682332162664im
    -4.8668814493140715 + 0.0im
    -4.218933906222015 - 7.617379500840438im
    -4.218933906222015 + 7.617379500840438im
    -2.117324030780644 + 0.0im
    -2.029229441492322 + 0.0im
    -1.7025637072900843 - 16.974819568195652im
    -1.7025637072900843 + 16.974819568195652im
    -1.1756081480320293 - 14.621543797706247im
    -1.1756081480320293 + 14.621543797706247im
    -0.3806667287614366 - 0.5866699857779792im
    -0.3806667287614366 + 0.5866699857779792im
    -0.223947575088223 - 0.4971805954842863im
    -0.223947575088223 + 0.4971805954842863im
]

test03_eigvals_psat = [
    -1000.00000000000 + 0.00000000000000im
    -1000.00000000000 + 0.00000000000000im
    -56.4167900000000 + 0.00000000000000im
    -53.3586000000000 + 0.00000000000000im
    -5.95708000000000 + 0.00000000000000im
    -5.02325000000000 - 7.89808000000000im
    -5.02325000000000 + 7.89808000000000im
    -4.82461000000000 + 0.00000000000000im
    -4.21943000000000 - 7.61745000000000im
    -4.21943000000000 + 7.61745000000000im
    -2.12296000000000 + 0.00000000000000im
    -2.03127000000000 + 0.00000000000000im
    -1.63531000000000 - 15.4006100000000im
    -1.63531000000000 + 15.4006100000000im
    -1.19151000000000 - 13.1976800000000im
    -1.19151000000000 + 13.1976800000000im
    -0.351320000000000 - 0.628360000000000im
    -0.351320000000000 + 0.628360000000000im
    -0.237770000000000 - 0.501610000000000im
    -0.237770000000000 + 0.501610000000000im
]

test04_eigvals = [
    -1000.0000000605402 + 0.0im
    -1000.0000000226693 + 0.0im
    -57.04885681143213 - 637.6983559917089im
    -57.04885681143213 + 637.6983559917089im
    -56.354194606909395 + 0.0im
    -53.18155023445317 + 0.0im
    -13.892570544392896 - 469.6478362124919im
    -13.892570544392896 + 469.6478362124919im
    -5.958255480381696 + 0.0im
    -5.021533592090426 - 7.898654827966522im
    -5.021533592090426 + 7.898654827966522im
    -4.868492180624505 + 0.0im
    -4.21902585809358 - 7.617326092491787im
    -4.21902585809358 + 7.617326092491787im
    -2.117386441281952 + 0.0im
    -2.029241696160179 + 0.0im
    -1.6959817848903833 - 16.973813636829668im
    -1.6959817848903833 + 16.973813636829668im
    -1.1682442396700994 - 14.616878713067868im
    -1.1682442396700994 + 14.616878713067868im
    -0.3805794727021142 - 0.586700331651268im
    -0.3805794727021142 + 0.586700331651268im
    -0.22394101115665865 - 0.49721792171561435im
    -0.22394101115665865 + 0.49721792171561435im
]

test05_eigvals = [
    -1000.0000037690621 + 0.0im
    -1000.000001656506 + 0.0im
    -126.28458815493319 + 0.0im
    -120.85493880185086 + 0.0im
    -46.48594631374737 + 0.0im
    -44.373301051079025 + 0.0im
    -10.0 + 0.0im
    -10.0 + 0.0im
    -3.9151560397679868 - 7.4523722134285535im
    -3.9151560397679868 + 7.4523722134285535im
    -3.836126674242972 - 7.360635983756108im
    -3.836126674242972 + 7.360635983756108im
    -0.3447395139088272 - 12.01547546088681im
    -0.3447395139088272 + 12.01547546088681im
    -0.20916948755870068 - 10.607466623279306im
    -0.20916948755870068 + 10.607466623279306im
    -0.16185759374101516 - 1.055686298197309im
    -0.16185759374101516 + 1.055686298197309im
    -0.0354521777174975 - 0.684452609868359im
    -0.0354521777174975 + 0.684452609868359im
]

test06_eigvals = [
    -1000.0000053062265 + 0.0im
    -1000.0000018990927 + 0.0im
    -126.75458543123136 + 0.0im
    -121.57403285596196 + 0.0im
    -46.52052665063294 + 0.0im
    -44.44016429276653 + 0.0im
    -34.69415568906223 - 514.6812519452002im
    -34.69415568906223 + 514.6812519452002im
    -10.0 + 0.0im
    -10.0 + 0.0im
    -9.032779692453872 - 426.00371528493883im
    -9.032779692453872 + 426.00371528493883im
    -3.915125641859613 - 7.452252518206685im
    -3.915125641859613 + 7.452252518206685im
    -3.8358938132681137 - 7.35974852776193im
    -3.8358938132681137 + 7.35974852776193im
    -0.3416921618276846 - 12.015383170233138im
    -0.3416921618276846 + 12.015383170233138im
    -0.2070154019752105 - 10.607728100520474im
    -0.2070154019752105 + 10.607728100520474im
    -0.16160669596957933 - 1.0557462673615183im
    -0.16160669596957933 + 1.0557462673615183im
    -0.03542058981778998 - 0.6844527516049774im
    -0.03542058981778998 + 0.6844527516049774im
]

test07_eigvals = [
    -999.9999914519308 + 0.0im
    -999.9999811538516 + 0.0im
    -5.027556786573994 + 0.0im
    -4.718819407730444 - 7.793357014228797im
    -4.718819407730444 + 7.793357014228797im
    -4.102660287139421 + 0.0im
    -4.05293289550599 - 7.552138242134652im
    -4.05293289550599 + 7.552138242134652im
    -1.7861497514412061 - 14.79309762975019im
    -1.7861497514412061 + 14.79309762975019im
    -1.2196573043212584 - 10.886078375537343im
    -1.2196573043212584 + 10.886078375537343im
    -0.8706541640276413 - 305.13445202971826im
    -0.8706541640276413 + 305.13445202971826im
    -0.502803286708684 - 0.5630005275892728im
    -0.502803286708684 + 0.5630005275892728im
    -0.2987601265912182 - 0.45339471510837304im
    -0.2987601265912182 + 0.45339471510837304im
    -0.2633057653103954 - 177.90377304106366im
    -0.2633057653103954 + 177.90377304106366im
    -0.1738090829024186 - 62.75964264819572im
    -0.1738090829024186 + 62.75964264819572im
    -0.15473139662959312 - 136.78740680694625im
    -0.15473139662959312 + 136.78740680694625im
]

test08_eigvals = [
    -2285.9589650663725 - 6825.3997886670795im
    -2285.9589650663725 + 6825.3997886670795im
    -2095.818014258244 - 6533.140681149019im
    -2095.818014258244 + 6533.140681149019im
    -1595.4562252195476 - 233.8877642497275im
    -1595.4562252195476 + 233.8877642497275im
    -986.7365548177571 + 0.0im
    -500.00000000000034 + 0.0im
    -471.8999089077167 + 0.0im
    -220.89110522830256 + 0.0im
    -50.32121120551376 + 0.0im
    -50.245608339393456 + 0.0im
    -34.244848184557476 - 260.53071319880445im
    -34.244848184557476 + 260.53071319880445im
    -11.303322380605753 + 0.0im
    -11.19036178279552 + 0.0im
    -7.691789694217273 - 28.802262759948757im
    -7.691789694217273 + 28.802262759948757im
    -4.513698394541721 + 0.0im
]

test09_eigvals = [
    -2266.7564379695177 - 6842.581227177472im
    -2266.7564379695177 + 6842.581227177472im
    -2102.099837452439 - 6516.907948869909im
    -2102.099837452439 + 6516.907948869909im
    -1603.381787508888 - 299.73078962017183im
    -1603.381787508888 + 299.73078962017183im
    -999.9999853200115 + 0.0im
    -981.5890587115106 + 0.0im
    -499.9999999999994 + 0.0im
    -472.94184918788915 + 0.0im
    -222.99856689050083 + 0.0im
    -58.916888833164734 - 297.3751281905745im
    -58.916888833164734 + 297.3751281905745im
    -50.391739774950395 + 0.0im
    -50.28496360614687 + 0.0im
    -11.312374832548043 + 0.0im
    -11.192736801973947 + 0.0im
    -6.304389558457761 - 25.99314750112348im
    -6.304389558457761 + 25.99314750112348im
    -4.936441589468676 - 7.860345016545073im
    -4.936441589468676 + 7.860345016545073im
    -4.874648728527242 + 0.0im
    -4.628324873366437 + 0.0im
    -1.3057837545018711 - 14.985883650327404im
    -1.3057837545018711 + 14.985883650327404im
    -0.522548405283088 - 0.5356190189108261im
    -0.522548405283088 + 0.5356190189108261im
]

test10_eigvals = [
    -2272.361404997375 - 6851.776097141858im
    -2272.361404997375 + 6851.776097141858im
    -2124.253409194327 - 6511.932840117861im
    -2124.253409194327 + 6511.932840117861im
    -1575.608572024949 - 348.98204359994224im
    -1575.608572024949 + 348.98204359994224im
    -999.9999861307571 + 0.0im
    -960.833224373504 + 0.0im
    -499.9999999999994 + 0.0im
    -477.8792003131099 + 0.0im
    -218.19978493862791 + 0.0im
    -75.81020867999811 - 305.1528459259956im
    -75.81020867999811 + 305.1528459259956im
    -50.41186110201852 + 0.0im
    -50.352254884885475 + 0.0im
    -11.32379102275944 + 0.0im
    -11.22342662568646 + 0.0im
    -7.581779910900623 - 27.826786728047516im
    -7.581779910900623 + 27.826786728047516im
    -5.484429063458374 + 0.0im
    -3.9465322340765994 - 7.508941380094648im
    -3.9465322340765994 + 7.508941380094648im
    -2.907232875387719 + 0.0im
    -0.7994143393863338 - 14.039239192684892im
    -0.7994143393863338 + 14.039239192684892im
    -0.13279249045498032 - 0.4749686774987308im
    -0.13279249045498032 + 0.4749686774987308im
]

test11_eigvals = [
    -2280.2076336006976 - 6841.438512443791im
    -2280.2076336006976 + 6841.438512443791im
    -1997.9475701466922 - 6457.618671478096im
    -1997.9475701466922 + 6457.618671478096im
    -1538.5966307902208 - 345.7913984886787im
    -1538.5966307902208 + 345.7913984886787im
    -1250.2936918022115 - 9608.999772834375im
    -1250.2936918022115 + 9608.999772834375im
    -999.9999874645969 + 0.0im
    -956.6099185810597 + 0.0im
    -863.7185028125181 - 6165.596120417795im
    -863.7185028125181 + 6165.596120417795im
    -500.0 + 0.0im
    -478.235862371146 + 0.0im
    -218.28706882452354 + 0.0im
    -58.31526552633564 - 299.61738004327435im
    -58.31526552633564 + 299.61738004327435im
    -50.414620572174904 + 0.0im
    -50.35016783858917 + 0.0im
    -42.437976630212574 - 467.0560264577345im
    -42.437976630212574 + 467.0560264577345im
    -11.323801014242834 + 0.0im
    -11.223449045909994 + 0.0im
    -7.577419561243316 - 27.833300571016597im
    -7.577419561243316 + 27.833300571016597im
    -5.483728552089268 + 0.0im
    -3.9464440004154135 - 7.508758038269464im
    -3.9464440004154135 + 7.508758038269464im
    -2.9069124155527093 + 0.0im
    -0.8024184353347599 - 14.03943882277388im
    -0.8024184353347599 + 14.03943882277388im
    -0.13278021568161175 - 0.4749742367984249im
    -0.13278021568161175 + 0.4749742367984249im
]

test12_eigvals = [
    -3.2920243068593518 + 0.0im
    -1.6618154866354078 - 13.047603342124061im
    -1.6618154866354078 + 13.047603342124061im
    -1.3729088875953759 + 0.0im
    0.0 + 0.0im
]

test13_eigvals = [
    -10459.358851638417 + 0.0im
    -9518.115670592897 + 0.0im
    -187.23828129745846 + 0.0im
    -10.106065550045505 + 0.0im
    -5.224596052187488 + 0.0im
    -4.501324080186281 + 0.0im
    -2.2096561921652595 + 0.0im
    -1.364291810141959 - 16.041013087225934im
    -1.364291810141959 + 16.041013087225934im
    -0.9689898367154597 - 0.5406190107493515im
    -0.9689898367154597 + 0.5406190107493515im
    -0.912054975024942 - 14.019102730055716im
    -0.912054975024942 + 14.019102730055716im
    -0.7804036299927885 - 3.4337139420134424im
    -0.7804036299927885 + 3.4337139420134424im
    -0.01997649074097721 + 0.0im
]

test14_eigvals = [
    -2211.559225208874 - 6867.397086978464im
    -2211.559225208874 + 6867.397086978464im
    -2153.3432865951086 - 6491.971642495368im
    -2153.3432865951086 + 6491.971642495368im
    -1462.896574367588 - 695.7281671214944im
    -1462.896574367588 + 695.7281671214944im
    -970.080948471084 + 0.0im
    -499.99999999999983 + 0.0im
    -482.5402184273114 + 0.0im
    -201.56587149903578 - 418.3793747723215im
    -201.56587149903578 + 418.3793747723215im
    -50.64444300708785 + 0.0im
    -50.44192340001585 + 0.0im
    -27.188203422464376 + 0.0im
    -11.33561590913596 + 0.0im
    -11.102536791187191 + 0.0im
    -6.344697298038714 - 2.6649640855535446im
    -6.344697298038714 + 2.6649640855535446im
    -1.1700311436684936 + 0.0im
    -1.1609343910392274 - 59.839297865644994im
    -1.1609343910392274 + 59.839297865644994im
    0.0 + 0.0im
]

test15_eigvals = [
    -39.22517135042766 - 0.23707459763811717im
    -39.22517135042766 + 0.23707459763811717im
    -8.162038320970622 + 0.0im
    -0.6894464243695174 - 8.596453673443301im
    -0.6894464243695174 + 8.596453673443301im
    -0.20829262548711688 + 0.0im
]

test15_eigvals_no_sat = [
    -41.28060502979358 + 0.0im
    -38.77124317334057 + 0.0im
    -6.080781526447125 + 0.0im
    -0.6716198831075708 - 8.713016722797422im
    -0.6716198831075708 + 8.713016722797422im
    -0.12135746559823009 + 0.0im
]

test15_eigvals_high_sat = [
    -38.90919918657826 - 0.5990898792027552im
    -38.90919918657826 + 0.5990898792027552im
    -8.716992122605445 + 0.0im
    -0.7802024926568868 - 8.344701265008863im
    -0.7802024926568868 + 8.344701265008863im
    -0.34108463674219724 + 0.0im
]

test16_eigvals = [
    -40.86489059167709 + 0.0im
    -38.79830220459659 + 0.0im
    -6.574706325237592 + 0.0im
    -0.6823066948004968 - 8.675007074248816im
    -0.6823066948004968 + 8.675007074248816im
    -0.14426163605639922 + 0.0im
]

test16_eigvals_high_sat = [
    -40.47662880667318 + 0.0im
    -38.83801972051611 + 0.0im
    -7.051748409012963 + 0.0im
    -0.6774433789575647 - 8.665077736802273im
    -0.6774433789575647 + 8.665077736802273im
    -0.15891752736421258 + 0.0im
]

test17_eigvals = [
    -999.9999972744532 + 0.0im
    -41.280911913534204 + 0.0im
    -38.76959077602596 + 0.0im
    -6.091730762853858 + 0.0im
    -4.397466628592957 - 7.701876240796052im
    -4.397466628592957 + 7.701876240796052im
    -0.667225952071195 - 8.709471358908846im
    -0.667225952071195 + 8.709471358908846im
    -0.19039981492553382 - 0.3625458335906406im
    -0.19039981492553382 + 0.3625458335906406im
]

test18_eigvals = [
    -28.693211883382347 + 0.0im
    -9.84238486492247 + 0.0im
    -1.7031770286738355 - 8.629174022229318im
    -1.7031770286738355 + 8.629174022229318im
    -0.7640548534736492 + 0.0im
]

test19_eigvals = [
    -28.76860514709647 + 0.0im
    -11.070928271739678 + 0.0im
    -1.665595867723869 - 8.572069631777202im
    -1.665595867723869 + 8.572069631777202im
    -0.4928568982924553 + 0.0im
]

test20_eigvals = [
    -100.00021842764602 + 0.0im
    -39.23707786473363 - 0.30958964511252446im
    -39.23707786473363 + 0.30958964511252446im
    -22.649793103911897 + 0.0im
    -8.543255967128875 + 0.0im
    -4.268315666497509 - 5.271829751631857im
    -4.268315666497509 + 5.271829751631857im
    -0.6848864366573055 - 8.58151201322762im
    -0.6848864366573055 + 8.58151201322762im
    -0.3129633046035981 - 0.5454321262659743im
    -0.3129633046035981 + 0.5454321262659743im
]

test20_eigvals_sat = [
    -100.00021924248418 + 0.0im
    -39.237422024473375 - 0.3111853351055383im
    -39.237422024473375 + 0.3111853351055383im
    -23.53165375119335 + 0.0im
    -8.410026826125367 + 0.0im
    -4.083256130308611 - 6.7290588685710615im
    -4.083256130308611 + 6.7290588685710615im
    -0.6809735490286486 - 8.581164994672914im
    -0.6809735490286486 + 8.581164994672914im
    -0.2887994589525554 - 0.4637034811911777im
    -0.2887994589525554 + 0.4637034811911777im
]

test21_eigvals = [
    -100.00020053211583 + 0.0im
    -41.279813572520446 + 0.0im
    -38.78824931300675 + 0.0im
    -22.694847726099287 + 0.0im
    -7.165287493953102 + 0.0im
    -6.288515161288432 + 0.0im
    -4.32006481720849 - 5.205289410871649im
    -4.32006481720849 + 5.205289410871649im
    -1.6673562530234518 - 1.8236637052912814im
    -1.6673562530234518 + 1.8236637052912814im
    -0.668108638820426 - 8.693770608657735im
    -0.668108638820426 + 8.693770608657735im
    -0.2847206455963953 - 0.6074406070433199im
    -0.2847206455963953 + 0.6074406070433199im
]

test22_eigvals = [
    -100.00020053191396 + 0.0im
    -41.280045081130595 + 0.0im
    -38.788825337778704 + 0.0im
    -22.69485578546965 + 0.0im
    -6.303130049514896 + 0.0im
    -4.931544822778555 + 0.0im
    -4.317954358167336 - 5.200375674306719im
    -4.317954358167336 + 5.200375674306719im
    -1.2329467571249157 + 0.0im
    -0.7041317791080781 - 8.858313170722147im
    -0.7041317791080781 + 8.858313170722147im
    -0.2858469340096019 - 0.602763234760579im
    -0.2858469340096019 + 0.602763234760579im
]

test23_eigvals = [
    -2285.984972487398 - 6825.409970531314im
    -2285.984972487398 + 6825.409970531314im
    -2095.8292998741636 - 6533.139948007082im
    -2095.8292998741636 + 6533.139948007082im
    -1596.153193009681 - 233.6009166562695im
    -1596.153193009681 + 233.6009166562695im
    -986.970453826522 + 0.0im
    -50.61566828819503 + 0.0im
    -50.24422063249634 + 0.0im
    -33.8405217631388 - 255.4659930547156im
    -33.8405217631388 + 255.4659930547156im
    -16.35252074296158 - 35.19268456048173im
    -16.35252074296158 + 35.19268456048173im
    -11.402718862758675 + 0.0im
    -11.30330507373553 + 0.0im
]

test24_eigvals = [
    -16167.452158969038 - 8.043416230174378im
    -16167.452158969038 + 8.043416230174378im
    -306.82201246319016 - 5867.887293375189im
    -306.82201246319016 + 5867.887293375189im
    -258.57819319671626 - 4795.264357598769im
    -258.57819319671626 + 4795.264357598769im
    -202.1765718936583 - 520.5322326644553im
    -202.1765718936583 + 520.5322326644553im
    -26.89823743203937 - 74.8261131632425im
    -26.89823743203937 + 74.8261131632425im
    -14.146571409094378 - 5.185851435997753im
    -14.146571409094378 + 5.185851435997753im
    -10.131535087046432 + 0.0im
    -1.8914933786938954 - 0.005547477442692941im
    -1.8914933786938954 + 0.005547477442692941im
]


text:
using Pkg
pkg"activate .."

import Pkg;
Pkg.add("Documenter")
using Documenter
using Colors_2021

makedocs(
    sitename = "Colors_2021",
    format = Documenter.HTML(),
    modules = [Colors_2021]
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
#=deploydocs(
    repo = "<repository url>"
)=#


text:
module AerialGroundPathFinding

# Write your package code here.
using Random

using StaticArrays
using Distances
using Parameters
using TOML
using Infiltrator
using SparseArrays
using JSON
using DataStructures
using StatsFuns
using IterTools

using Graphs
using LightGraphs
using LightGraphs.ShortestPaths

using MultiAgentPathFinding

export
    AgentTask,
    Location2D,
    LatLonCoords,
    EuclideanLatLongMetric,
    GroundTransitState,
    AerialMAPFState,
    GroundTransitAction,
    GroundTransitConflict,
    GroundTransitConstraint,
    GroundTransitVertexConstraint,
    GroundMAPFState,
    GroundMAPFAction,
    GroundMAPFConflict,
    GroundMAPFConstraint

export
    get_location2D_list,
    compute_bidir_astar_euclidean,
    get_increasing_dist_ordering,
    CoordinatedMAPFEnv,
    update_aerial_ground_paths_cbs_solution!,
    set_ground_transit_graph!,
    compute_independent_paths,
    get_tasks_with_valid_path,
    augment_road_graph_with_aerial_paths!,
    update_ground_paths_with_ground_mapf_result!,
    compute_total_cost,
    count_cars_not_coordinating,
    plan_prioritized_paths!

include("types.jl")
include("utils.jl")
include("stage1_paths.jl")
include("stage2_paths.jl")
include("prioritized_planning.jl")

end