text:
struct EconomicDispatchProblem <: PowerSimulationsOperationsProblem end
struct UnitCommitmentProblem <: PowerSimulationsOperationsProblem end
struct AGCReserveDeployment <: PowerSimulationsOperationsProblem end

function _generic_template(; kwargs...)
    network = get(kwargs, :network, CopperPlatePowerModel)

    devices = get(
        kwargs,
        :devices,
        Dict(
            :Generators => DeviceModel(PSY.ThermalStandard, ThermalBasicUnitCommitment),
            :RE => DeviceModel(PSY.RenewableDispatch, RenewableFullDispatch),
            :DistRE => DeviceModel(PSY.RenewableFix, FixedOutput),
            :Hydro => DeviceModel(PSY.HydroEnergyReservoir, HydroDispatchRunOfRiver),
            :HydroROR => DeviceModel(PSY.HydroDispatch, HydroDispatchRunOfRiver),
            :Loads => DeviceModel(PSY.PowerLoad, StaticPowerLoad),
            :ILoads => DeviceModel(PSY.InterruptibleLoad, InterruptiblePowerLoad),
        ),
    )

    branches = get(
        kwargs,
        :branches,
        Dict(
            :L => DeviceModel(PSY.Line, StaticLine),
            :T => DeviceModel(PSY.Transformer2W, StaticTransformer),
            :TT => DeviceModel(PSY.TapTransformer, StaticTransformer),
            :DC => DeviceModel(PSY.HVDCLine, HVDCDispatch),
        ),
    )

    services = get(
        kwargs,
        :services,
        Dict(
            :ReserveUp =>
                ServiceModel(PSY.VariableReserve{PSY.ReserveUp}, RangeReserve),
            :ReserveDown =>
                ServiceModel(PSY.VariableReserve{PSY.ReserveDown}, RangeReserve),
        ),
    )

    template = OperationsProblemTemplate(network, devices, branches, services)

    return template
end
"""
    template_unit_commitment(; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for a Unit Commitment
problem.

# Example
```julia
template = template_unit_commitment()
```

# Accepted Key Words
- `network::Type{<:PM.AbstractPowerModel}` : override default network model settings
- `devices::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `branches::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `services::Dict{Symbol, ServiceModel}` : override default `ServiceModel` settings
"""
function template_unit_commitment(; kwargs...)
    template = _generic_template(; kwargs...)
    return template
end

"""
    template_economic_dispatch(; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for an Economic Dispatch
problem.

# Example
```julia
template = template_economic_dispatch()
```

# Accepted Key Words
- `network::Type{<:PM.AbstractPowerModel}` : override default network model settings
- `devices::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `branches::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `services::Dict{Symbol, ServiceModel}` : override default `ServiceModel` settings
"""
function template_economic_dispatch(; kwargs...)
    devices = get(
        kwargs,
        :devices,
        Dict(
            :Generators => DeviceModel(PSY.ThermalStandard, ThermalRampLimited),
            :RE => DeviceModel(PSY.RenewableDispatch, RenewableFullDispatch),
            :DistRE => DeviceModel(PSY.RenewableFix, FixedOutput),
            :Hydro =>
                DeviceModel(PSY.HydroEnergyReservoir, HydroDispatchReservoirBudget),
            :HydroROR => DeviceModel(PSY.HydroDispatch, FixedOutput),
            :Loads => DeviceModel(PSY.PowerLoad, StaticPowerLoad),
            :ILoads => DeviceModel(PSY.InterruptibleLoad, InterruptiblePowerLoad),
        ),
    )

    services = get(kwargs, :services, Dict())

    template = _generic_template(devices = devices, services = services; kwargs...)

    return template
end

"""
    template_agc_reserve_deployment(; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for an AGC Reserve Deplyment Problem. This model doesn't support customization

# Example
```julia
template = agc_reserve_deployment()
```
"""
function template_agc_reserve_deployment(; kwargs...)
    if !isempty(kwargs)
        throw(ArgumentError("AGC Template doesn't currently support customization"))
    end
    devices = Dict(
        :Generators => DeviceModel(PSY.ThermalStandard, FixedOutput),
        :Ren => DeviceModel(PSY.RenewableDispatch, FixedOutput),
        :Loads => DeviceModel(PSY.PowerLoad, StaticPowerLoad),
        :Hydro => DeviceModel(PSY.HydroEnergyReservoir, FixedOutput),
        :HydroROR => DeviceModel(PSY.HydroDispatch, FixedOutput),
        :RenFx => DeviceModel(PSY.RenewableFix, FixedOutput),
        :Regulation_thermal => DeviceModel(
            PSY.RegulationDevice{PSY.ThermalStandard},
            DeviceLimitedRegulation,
        ),
        :Regulation_hydro_dispatch => DeviceModel(
            PSY.RegulationDevice{PSY.HydroDispatch},
            ReserveLimitedRegulation,
        ),
        :Regulation_hydro_reservoir => DeviceModel(
            PSY.RegulationDevice{PSY.HydroEnergyReservoir},
            ReserveLimitedRegulation,
        ),
    )
    services = Dict(:AGC => ServiceModel(PSY.AGC, PIDSmoothACE))
    template = _generic_template(
        network = PSI.AreaBalancePowerModel,
        devices = devices,
        branches = Dict(),
        services = services;
        kwargs...,
    )
end

"""
    EconomicDispatchProblem(system::PSY.System; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for an EconomicDispatch
problem. Uses the template to create an `OperationsProblem`.

# Example
```julia
ed_problem = EconomicDispatchProblem(system)
```

# Accepted Key Words
- `network::Type{<:PM.AbstractPowerModel}` : override default network model settings
- `devices::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `branches::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `services::Dict{Symbol, ServiceModel}` : override default `ServiceModel` settings
- Key word arguments supported by `OperationsProblem`
"""

function EconomicDispatchProblem(system::PSY.System; kwargs...)
    kwargs = Dict(kwargs)
    template_kwargs = Dict()
    for kw in setdiff(keys(kwargs), OPERATIONS_ACCEPTED_KWARGS)
        template_kwargs[kw] = pop!(kwargs, kw)
    end

    template = template_economic_dispatch(; template_kwargs...)
    op_problem = OperationsProblem(EconomicDispatchProblem, template, system; kwargs...)
    return op_problem
end

"""
    UnitCommitmentProblem(system::PSY.System; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for a Unit Commitment
problem. Uses the template to create an `OperationsProblem`.

# Example
```julia
uc_problem = UnitCommitmentProblem(system)
```

# Accepted Key Words
- `network::Type{<:PM.AbstractPowerModel}` : override default network model settings
- `devices::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `branches::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `services::Dict{Symbol, ServiceModel}` : override default `ServiceModel` settings
- Key word arguments supported by `OperationsProblem`
"""
function UnitCommitmentProblem(system::PSY.System; kwargs...)
    kwargs = Dict(kwargs)
    template_kwargs = Dict()
    for kw in setdiff(keys(kwargs), OPERATIONS_ACCEPTED_KWARGS)
        template_kwargs[kw] = pop!(kwargs, kw)
    end

    template = template_unit_commitment(; template_kwargs...)
    op_problem = OperationsProblem(UnitCommitmentProblem, template, system; kwargs...)
    return op_problem
end

"""
    AGCReserveDeployment(system::PSY.System; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for an AGC Reserve Deplyoment Problem.
Uses the template to create an `OperationsProblem`.

# Example
```julia
agc_problem = AGCReserveDeployment(system)
```

# Accepted Key Words
- Key word arguments supported by `OperationsProblem`
"""
function AGCReserveDeployment(system::PSY.System; kwargs...)
    kwargs = Dict(kwargs)
    template_kwargs = Dict()
    for kw in setdiff(keys(kwargs), OPERATIONS_ACCEPTED_KWARGS)
        template_kwargs[kw] = pop!(kwargs, kw)
    end

    template = template_agc_reserve_deployment(; template_kwargs...)
    op_problem = OperationsProblem(AGCReserveDeployment, template, system; kwargs...)
    return op_problem
end

"""
    run_unit_commitment(system::PSY.System; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for a Unit Commitment
problem. Uses the template to create an `OperationsProblem`. Solves the created operations problem.

# Example
```julia
results = run_unit_commitment(system; optimizer = optimizer)
```

# Accepted Key Words
- `network::Type{<:PM.AbstractPowerModel}` : override default network model settings
- `devices::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `branches::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `services::Dict{Symbol, ServiceModel}` : override default `ServiceModel` settings
- `optimizer::JuMP Optimizer` : An optimizer is a required key word
- `savepath::AbstractString`  : Path to save results
- Key word arguments supported by `OperationsProblem`
"""

function run_unit_commitment(sys::PSY.System; kwargs...)
    solve_kwargs = Dict()
    for kw in OPERATIONS_SOLVE_KWARGS
        haskey(kwargs, kw) && (solve_kwargs[kw] = kwargs[kw])
    end
    op_problem = UnitCommitmentProblem(sys; kwargs...)
    results = solve!(op_problem; solve_kwargs...)
    return results
end

"""
    run_economic_dispatch(system::PSY.System; kwargs...)

Creates an `OperationsProblemTemplate` with default DeviceModels for an EconomicDispatch
problem. Uses the template to create an `OperationsProblem`.

# Example
```julia
results = run_economic_dispatch(system; optimizer = optimizer)
```

# Accepted Key Words
- `network::Type{<:PM.AbstractPowerModel}` : override default network model settings
- `devices::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `branches::Dict{Symbol, DeviceModel}` : override default `DeviceModel` settings
- `services::Dict{Symbol, ServiceModel}` : override default `ServiceModel` settings
- `optimizer::JuMP optimizer` : a JuMP optimizer is a required key word
- `savepath::AbstractString`  : Path to save results
- Key word arguments supported by `OperationsProblem`
"""

function run_economic_dispatch(sys::PSY.System; kwargs...)
    solve_kwargs = Dict()
    for kw in OPERATIONS_SOLVE_KWARGS
        haskey(kwargs, kw) && (solve_kwargs[kw] = kwargs[kw])
    end
    op_problem = EconomicDispatchProblem(sys; kwargs...)
    results = solve!(op_problem; solve_kwargs...)
    return results
end


text:
using AlgebraicDynamics.CPortGraphDynam
using AlgebraicDynamics.DWDDynam
using AlgebraicDynamics.CPortGraphDynam: draw, barbell, gridpath, grid, meshpath
using AlgebraicDynamics.DWDDynam: trajectory
using Catlab.WiringDiagrams
using Catlab.WiringDiagrams.CPortGraphs
using Catlab.CategoricalAlgebra
using Catlab.Theories
using Catlab.Graphs
using Catlab

using DelayDiffEq
using LabelledArrays

using Test
using PrettyTables
using Base.Iterators: repeated

function simulate(f::ContinuousMachine{T}, nsteps::Int, h::Real, u0::Vector, xs=T[]) where T
    trajectory(euler_approx(f, h), u0, xs, nothing, nsteps)
end

function printsim(traj, stepfun, indxfun, shape)
    for u in stepfun(traj)
        pretty_table(reshape(indxfun(u), shape), equal_columns_width=true, noheader=true)
    end
end

d = barbell(2)
xs = [
    ContinuousMachine{Float64}(2,2,(u, x, p, t)->[x[1]*u[1], x[2]*u[2]], (u,p,t)->u),
    ContinuousMachine{Float64}(2,2,(u, x, p, t)->[1/x[1]*u[1], -x[2]*u[2]], (u,p,t)->u)
]
h = 0.1
u₀ = ones(Float64, 4)
composite = oapply(d, xs)
fcomp(u,p,t) = eval_dynamics(composite, u, Float64[], p, t)
rcomp(u) = readout(composite,u)
@test rcomp(u₀) == []
u₁ = fcomp(u₀, Float64[], h)
@test u₁ == [1,1, 1, -1]
@test rcomp(u₁) == []
@test fcomp(u₁, [], h) == [1,-1, 1, 1]
# @show simulate(composite, 10, h, u₀)

d₀ = OpenCPortGraph()
add_parts!(d₀, :Box, 1)
d₁ = barbell(2)
F = ACSetTransformation((Box=[2],), d₀, d₁)
G = ACSetTransformation((Box=[1],), d₀, d₁)
# |1| <-> |3| <-> |5|
# |2| <-> |4| <-> |6|
d₂ = apex(pushout(F,G))
Catlab.Theories.id(OpenCPortGraph, n) = begin
    g = OpenCPortGraph()
    add_parts!(g, :Box, 1)
    add_parts!(g, :Port, n, box=1)
    add_parts!(g, :OuterPort, n, con=1:n)
    return g
end
lob(n) = let
    b = barbell(n)
    p = add_parts!(b, :Port, n, box=1)
    add_parts!(b, :OuterPort, n, con=p)
    b
end
d₂′ = ocompose(barbell(2), [id(OpenCPortGraph, 2), lob(2)])
# @show d₂′
β = 0.4
μ = 0.4
α₁ = 0.01
α₂ = 0.01

sirfuncb = (u,x,p,t)->[-β*u[1]*u[2] - α₁*(u[1]-x[1]), # Ṡ
                        β*u[1]*u[2] - μ*u[2] - α₂*(u[2]-x[2]), #İ
                        μ*u[2] # Ṙ
                        ]
sirfuncm = (u,x,p,t)->[-β*u[1]*u[2] - α₁*(u[1]-(x[1]+x[3])/2),
                        β*u[1]*u[2] - μ*u[2] - α₂*(u[2]-(x[2]+x[4])/2),
                        μ*u[2]
                        ]

boundary  = ContinuousMachine{Float64}(2,3,sirfuncb, (u,p,t)->u[1:2])
middle    = ContinuousMachine{Float64}(4,3, sirfuncm, (u,p,t)->u[[1,2,1,2]])
threecity = oapply(d₂, [boundary,middle,boundary])

# println("Simulating 3 city")
traj = simulate(threecity, 100, 0.01, [100,1,0,100,0,0,100,0,0.0] )
# map(traj) do u
#     return (i1=u[2], i2=u[5], i3=u[8])
# end |> pretty_table

gl = @acset OpenCPortGraph begin
    Box = 3
    Port = 7
    Wire = 4
    OuterPort = 3
    box = [1,1,2,2,2,3,3]
    src = [2, 3, 5, 6]
    tgt = [3, 2, 6, 5]
    con = [1,4,7]
end

gm = @acset OpenCPortGraph begin
    Box = 3
    Port = 10
    Wire = 4
    OuterPort = 6
    box = [1,1,1,2,2,2,2,3,3,3]
    src = [2, 4, 6, 8]
    tgt = [4, 2, 8, 6]
    con = [3, 7, 10, 1, 5, 9]
end

gr = @acset OpenCPortGraph begin
    Box = 3
    Port = 7
    Wire = 4
    OuterPort = 3
    box = [1,1,2,2,2,3,3]
    src = [1, 3, 4, 6]
    tgt = [3, 1, 6, 4]
    con = [2,5,7]
end

symedges(g) = g.tables.W[g.tables.W.src .<= g.tables.W.tgt]
sympairs(z) = Iterators.filter(x->x[1] <= x[2], z)
pg2 = ocompose(barbell(3), [gl, gm])
g2 = migrate!(Graph(), pg2)
@test g2[ 9:11, :src] == [1,2,3]
@test g2[ 9:11, :tgt] == [4,5,6]
@test g2[12:14, :src] == [4,5,6]
@test g2[12:14, :tgt] == [1,2,3]

d3 = ocompose(barbell(3), [id(OpenCPortGraph, 3), lob(3)])
pg3 = ocompose(d3, [gl,gm,gr])
g3 = migrate!(Graph(), pg3)
@test g3[19:24, :src] == 1:6
@test g3[19:24, :tgt] == [4,5,6,1,2,3]
@test incident(pg3, 5, :box) == 11:14

α₁ = 1
fm = ContinuousMachine{Float64}(4, 1, (u,x,p,t) -> α₁ * (sum(x) .- u .* length(x)), (u,p,t)->collect(repeated(u[1], 4)))
fl = ContinuousMachine{Float64}(3, 1, (u,x,p,t) -> α₁ * (sum(x) .- u .* length(x)), (u,p,t)->collect(repeated(u[1], 3)))
fr = ContinuousMachine{Float64}(3, 1, (u,x,p,t) -> α₁ * (sum(x) .- u .* length(x)), (u,p,t)->collect(repeated(u[1], 3)))
ft = ContinuousMachine{Float64}(3, 1, (u,x,p,t) -> α₁ * (sum(x) .- u .* length(x)), (u,p,t)->collect(repeated(u[1], 3)))
fb = ContinuousMachine{Float64}(3, 1, (u,x,p,t) -> α₁ * (sum(x) .- u .* length(x)), (u,p,t)->collect(repeated(u[1], 3)))
fc = ContinuousMachine{Float64}(2, 1, (u,x,p,t) -> α₁ * (sum(x) .- u .* length(x)), (u,p,t)->collect(repeated(u[1], 2)))

@test eval_dynamics(ft, ones(1), ones(3), nothing, 0.0) == zeros(1)
f₁ = oapply(gl, [fc, fl, fc])
f₂ = oapply(gm, [ft, fm, fb])
f₃ = oapply(gr, [fc, fr, fc])

F = oapply(pg3, [fc, fl, fc, ft, fm, fb, fc, fr, fc])
@test eval_dynamics(F, ones(Float64, 9), [], nothing, 1.0) == zeros(Float64, 9)
u₀ = zeros(Float64, 9)
u₀[5] = 1.0
@test eval_dynamics(F, u₀, [], nothing, 1.0)[5] < 0
@test eval_dynamics(F, u₀, [], nothing, 1.0)[4] == α₁
@test eval_dynamics(F, u₀, [], nothing, 1.0)[2] == α₁
@test eval_dynamics(F, u₀, [], nothing, 1.0)[3] == 0

d4 = @acset OpenCPortGraph begin
    Box = 3
    Port = 18
    Wire = 12
    OuterPort = 6
    box = Iterators.flatten([repeated(i, 6) for i in 1:3])
    con = [1,2,3,16,17,18]
    src = [4,5,6,7,8,9,10,11,12,13,14,15]
    tgt = [7,8,9,4,5,6,13,14,15,10,11,12]
end
pg3 = ocompose(d4, [gm,gm,gm])
draw(pg3)
# pg3[:, :con]

@testset "Laplacians" begin
@test eval_dynamics(oapply(gm, [ft, fm, fb]), ones(3), ones(6), nothing, 1.0) == zeros(3)
@test eval_dynamics(oapply(gm, [ft, fm, fb]), [1,2,1], ones(6), nothing, 1.0) == [1,-4,1]
@test eval_dynamics(oapply(gm, [ft, fm, fb]), [1,2,0], ones(6), nothing, 1.0) == [1,-5, 4]

F = oapply(d4, oapply(gm, [ft,fm,fb]))
@test nstates(F) == 9
@test ninputs(F) == 6
@test eval_dynamics(F, ones(9), ones(6), nothing, 1.0) == zeros(9)
@test eval_dynamics(F, 2*ones(9), 2*ones(6), nothing, 1.0) == zeros(9)

pg4 = ocompose(d4, [pg3, pg3, pg3])
pg5 = ocompose(d4, [pg4, pg4, pg4])
# draw(pg5)

@test (nstates(F),ninputs(F)) == (9,6)
F2 = oapply(d4, [F, F, F])
@test (nstates(F2),ninputs(F2)) == (27,6)
@test eval_dynamics(F2, ones(Float64, 27), ones(Float64, 6), nothing, 0.0) == zeros(27)
F3 = oapply(d4, [F2,F2,F2])
@test (nstates(F3), ninputs(F3)) == (81, 6)
@test eval_dynamics(F3, ones(Float64, 81), ones(Float64, 6), nothing, 0.0) == zeros(81)
end

@testset "Advection-Diffusion" begin

advecdiffuse(α₁, α₂) = begin
    diffop(u,p,t) = α₁ .* (sum(p) .- u .* length(p))
    advop(u,p,t)  = α₂ .* (p[end] .- u)
    ft = ContinuousMachine{Float64}(3, 1, (u,p,q,t) -> diffop(u,p,u) .+ advop(u,p,t), (u,p,t)->collect(repeated(u[1], 3)))
    fm = ContinuousMachine{Float64}(4, 1, (u,p,q,t) -> diffop(u,p,u) .+ advop(u,p,t), (u,p,t)->collect(repeated(u[1], 4)))
    fb = ContinuousMachine{Float64}(3, 1, (u,p,q,t) -> diffop(u,p,u) .+ advop(u,p,t), (u,p,t)->collect(repeated(u[1], 3)))
    return ft, fm, fb
end

ft, fm, fb = advecdiffuse(1.0,2.0)
eval_dynamics(ft, [1.0], [1,1,1.0], 0.0)
F = oapply(d4, oapply(gm, [ft,fm,fb]))
eval_dynamics(F, zeros(9), ones(6), 0.0)
traj = simulate(F, 48, 0.1, zeros(9), vcat(ones(3), zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,3))

F2 = oapply(d4, [F,F,F])
traj = simulate(F2, 16, 0.1, zeros(27), vcat(ones(3), zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))

ft, fm, fb = advecdiffuse(1.0,4.0)
F = oapply(d4, oapply(gm, [ft,fm,fb]))
F2 = oapply(d4, [F,F,F])
traj = simulate(F2, 16, 0.1, zeros(27), vcat(ones(3), zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))
i=16
@test traj[i][end-2:end][1] == traj[i][end-2:end][2]
@test traj[i][end-2:end][2] == traj[i][end-2:end][3]

traj = simulate(F2, 16, 0.1, zeros(27), vcat([0,1,0], zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))
@test traj[i][end-2:end][1] <= traj[i][end-2:end][2]
@test traj[i][end-2:end][2] >= traj[i][end-2:end][3]
end

@testset "Reaction-Diffusion-Advection" begin

RDA(α₀, α₁, α₂) = begin
    diffop(u,p,t) = α₁ .* (sum(p) .- u .* length(p))
    advop(u,p,t)  = α₂ .* (p[end] .- u)
    ft = ContinuousMachine{Float64}(3, 1, (u,p,q,t) -> α₀ .* u .+ diffop(u,p,u) .+ advop(u,p,t), (u,p,t)->collect(repeated(u[1], 3)))
    fm = ContinuousMachine{Float64}(4, 1, (u,p,q,t) -> α₀ .* u .+ diffop(u,p,u) .+ advop(u,p,t), (u,p,t)->collect(repeated(u[1], 4)))
    fb = ContinuousMachine{Float64}(3, 1, (u,p,q,t) -> α₀ .* u .+ diffop(u,p,u) .+ advop(u,p,t), (u,p,t)->collect(repeated(u[1], 3)))
    return ft, fm, fb
end

ft, fm, fb = RDA(0.1, 1.0,2.0)
eval_dynamics(ft, [1.0], [1,1,1.0], nothing, 0.0)
F = oapply(d4, oapply(gm, [ft,fm,fb]))
eval_dynamics(F, zeros(9), ones(6), nothing, 0.0)
traj = simulate(F, 48, 0.1, zeros(9), vcat(ones(3), zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,3))

F2 = oapply(d4, [F,F,F])
traj = simulate(F2, 16, 0.1, zeros(27), vcat(ones(3), zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))

ft, fm, fb = RDA(0.1, 1.0,4.0)
F = oapply(d4, oapply(gm, [ft,fm,fb]))
F2 = oapply(d4, [F,F,F])
traj = simulate(F2, 16, 0.1, zeros(27), vcat(ones(3), zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))
i=16
@test traj[i][end-2:end][1] ≈ traj[i][end-2:end][2] atol=1e-3
@test traj[i][end-2:end][2] ≈ traj[i][end-2:end][3] atol=1e-3

traj = simulate(F2, 64, 0.1, zeros(27), vcat([0,1,0], zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))
@test traj[i][end-2:end][1] <= traj[i][end-2:end][2]
@test traj[i][end-2:end][2] >= traj[i][end-2:end][3]

ft, fm, fb = RDA(-0.4, 1.0,4.0)
F = oapply(d4, oapply(gm, [ft,fm,fb]))
F2 = oapply(d4, [F,F,F])
traj = simulate(F2, 128, 0.1, zeros(27), vcat([0,1,0], zeros(3)))
# printsim(traj, t->t[end-2:end], identity, (3,9))
end

@testset "Grids" begin
function testgridsize(n,m)
    g = grid(n,m)
    @test nparts(g, :Box) == n*m
    @test nparts(g, :Port) == 6n + 4n*(m-2)
    @test nparts(g, :Wire) == 2*((n-1)*m + (m-1)*n)
end
for (i,j) in Iterators.product(1:6, 2:4)
    testgridsize(i,j)
end
end

@testset "DelayMachine" begin
# ross macdonald
c = @acset OpenCPortGraph begin
    Box = 2
    Port = 2
    Wire = 2
    OuterPort = 0
    box = [1,2]
    src = [1,2]
    tgt = [2,1]
    con = []
end

dzdt_delay = function(u,x,h,p,t)
    Y, Z = u
    Y_delay, Z_delay = h(p, t - p.n)
    X, X_delay = x[1]

    [p.a*p.c*X*(1 - Y - Z) -
        p.a*p.c*X_delay*(1 - Y_delay - Z_delay)*exp(-p.g*p.n) -
        p.g*Y,
    p.a*p.c*X_delay*(1 - Y_delay - Z_delay)*exp(-p.g*p.n) -
        p.g*Z]
end

dxdt_delay = function(u,x,h,p,t)
    X, = u
    Z, _ = x[1]
    [p.m*p.a*p.b*Z*(1 - X) - p.r*X]
end

#-

1 + 1
mosquito_delay_model = DelayMachine{Float64, 2}(
    1, 2, 1, dzdt_delay, (u,h,p,t) -> [[u[2], h(p,t - p.n)[2]]])

human_delay_model = DelayMachine{Float64, 2}(
    1, 1, 1, dxdt_delay, (u,h,p,t) -> [[u[1], h(p, t - p.n)[1]]])
rm_model = oapply(c, [mosquito_delay_model, human_delay_model])

params = LVector(a = 0.3, b = 0.55, c = 0.15,
    g = 0.1, n = 10, r = 1.0/200, m = 0.5)

u0_delay = [0.09, .01, 0.3]
tspan = (0.0, 365.0*5)
hist(p,t) = u0_delay;

prob = DDEProblem(rm_model, u0_delay, [], hist, tspan, params)
alg = MethodOfSteps(Tsit5())
sol = solve(prob, alg)
a, b, c, g, n, r, m = params
R0 = (m*a^2*b*c*exp(-g*n))/(r*g)
@test isapprox(last(sol)[3], (R0 - 1)/(R0 + (a*c)/g), atol = 1e-3)
end

ext: jl


text:
# Clayton, N. S., Yu, K. S. and Dickinson, A. (2001), Scrub jays (Aphelocoma
# coerulescens) form integrated memories of the multiple features of caching
# episodes. Journal of Experimental Psychology: Animal Behavior Processes, 1:17-29
#
# Experiments 1 & 2 are merged because they are very similar and involve the
# same birds. Interleaved training trials have set index 0, test phase of
# experiment 1 set index 5 and test phase of experiment 2 has set index 6.

const CLAYTON0103_EXPERIMENTS = (:Clayton01_exp1, :Clayton01_exp2, :Clayton01_exp3,
                                 :Clayton01_exp4, :Clayton03_exp1, :Clayton03_exp2)
function summarize(::Experiment{:Clayton0103}, results)
    [summarize(EXPERIMENTS[key], res)
     for (key, res) in zip(CLAYTON0103_EXPERIMENTS, results)]
end
function _score(exp::Experiment{:Clayton0103}, result, metric)
    vcat([_score(EXPERIMENTS[key], res, metric)
          for (key, res) in zip(CLAYTON0103_EXPERIMENTS, result)]...)
end
function statistical_tests(exp::Experiment{:Clayton0103}, data)
    [statistical_tests(EXPERIMENTS[key], d)
     for (key, d) in zip(CLAYTON0103_EXPERIMENTS, data)]
end
function run!(::Experiment{:Clayton0103}, models)
    results = [run!(EXPERIMENTS[key], models) for key in CLAYTON0103_EXPERIMENTS]
    results[5] = vcat(results[1], results[5]) # data from Clayton01_exp1 was reused in Clayton03_exp2
    results
end

function cricketproportions(results)
    pcinspect = @where(results, :action .== "inspect", (|).(:set .== 4, :set .== 7),
                      :trial .== "pc", :RI .> 4)
    sort!(pcinspect, [:id, :foodtype, :RI, :set])
    pcinspectcricket = @where(pcinspect, :foodtype .== "other")
    pcinspectcricket.counts = float.(pcinspectcricket.counts)
    pcinspectcricket.counts ./= (pcinspectcricket.counts .+
                                @where(pcinspect, :foodtype .== "peanut").counts .+
                                1e-16) # I prefer 0 if no site is ever inspected over NaN because NaN propagates and may lead to NaN scores.
    pcinspectcricket
end
function _summarize(::Experiment{:Clayton01_exp1}, results)
    res = combine(groupby(@where(results, :action .== "inspect", :set .!= 0,
                                 (|).(:foodtype .!= "other", :set .== 5)),
                          [:group, :foodtype, :action, :RI, :trial, :set]),
             df -> DataFrame(μ = mean(df.counts), sem = sem(df.counts),
                             firstinspection = mean(df.firstinspection),
                             n = length(df.counts)))
    for g in groupby(@where(results, :action .== "cache", :set .== 5), :group)
        push!(res, [g.group[1], missing, "cache", missing, missing, 5,
                    mean(g.counts), sem(g.counts), 0, length(g.counts)])
    end
    res
end
firstinspect_dist(df::DataFrame; n = 4) = firstinspect_dist(df.firstinspection; n = n)
function firstinspect_dist(firstinspections; n = 4)
    patterncounts = zeros(Int, 2^n)
    for i in 1:n:length(firstinspections)
        p = 1
        for j in 0:n - 1
            firstinspections[i + j] == 0 || (p += 2^j)
        end
        patterncounts[p] += 1
    end
    patterncounts
end
function statistical_tests(exp::Experiment{:Clayton01_exp1}, data)
    tests = Test[]
    for g in groupby(@where(data, :action .== "inspect", :set .== 4), :RI)
        push!(tests, Test("first inspect set 4 RI $(g.RI[1])",
                          chisqtest(firstinspect_dist(@where(g, :group .== "degrade")),
                                    firstinspect_dist(@where(g, :group .== "replenish"))),
                          exp.tests))
    end
    datatest = @where(data, :action .== "inspect", :set .== 5)
    for g in groupby(datatest, :RI)
        RI = g.RI[1]
        id = "inspect test RI $RI"
        push!(tests, Test(id,
                          splitplotanova(g,
                                         exp.tests[id].locals,
                                         betweenfactors = :group,
                                         withinfactors = :foodtype,
                                         id = :id, data = :counts),
                          exp.tests))
        push!(tests, Test("first inspect test RI $RI",
                          chisqtest(firstinspect_dist(@where(g, :group .== "degrade")),
                                    firstinspect_dist(@where(g, :group .== "replenish"))),
                          exp.tests))
        if RI == 100
            for subg in groupby(g, :group)
                id = "inspect test RI 100 $(subg.group[1])"
                push!(tests, Test(id,
                                  anova(subg, @formula(counts ~ foodtype),
                                        exp.tests[id].locals),
                                  exp.tests))
            end
        end
    end
    datatestcache = @where(data, :action .== "cache", :set .== 5)
    for g in groupby(datatest, :RI)
        RI = g.RI[1]
        id = "cache test RI $RI"
        push!(tests, Test(id,
                          splitplotanova(g,
                                         exp.tests[id].locals,
                                         betweenfactors = :group,
                                         withinfactors = :foodtype,
                                         id = :id, data = :counts),
                          exp.tests))
    end
    TestCollection(tests)
end
value(x::Quantity) = x.val
value(x) = div(x, 60)
@inline function Clayton01_trial!(m, results, group, id, set,
                        foodtype1, foodtype2, wait;
                        degrade, pilfer, bothtrayspresent = false,
                        waitbetween = 0u"hr", specificothername = false)
    foodtype1string = lowercase(string(foodtype1))
    foodtype2string = foodtype1 == Peanut && !specificothername ? "other" : lowercase(string(foodtype2))
    trial = "$(foodtype1string[1])$(lowercase(string(foodtype2)[1]))"
    remove!(m, Any)
    wait!(m, 4u"hr")
    tray1 = Tray(hack(1)) # one half of tray
    tray2 = Tray(hack(2)) # second half of tray
    if bothtrayspresent
        cover!(tray2)
        add!(m, tray2)
    end
    add!(m, tray1)
    add!(m, foodtype1, 50)
    wait!(m, 15u"minute")
    remove!(m, Any)
    push!(results, [group, foodtype1string, id, set, trial, value(wait), "cache", countcache(tray1), missing])
    if waitbetween > 0u"hr"
        add!(m, MaintenanceDiet)
        wait!(m, waitbetween - 4u"hr")
        remove!(m, Any)
        wait!(m, 4u"hr")
    end
    if bothtrayspresent
        uncover!(tray2)
        cover!(tray1)
        add!(m, tray1)
    end
    add!(m, tray2)
    add!(m, foodtype2, 50)
    wait!(m, 15u"minute")
    remove!(m, Any)
    uncover!(tray1)
    push!(results, [group, foodtype2string, id, set, trial, value(wait), "cache",
                    countcache(tray2), missing])
    if wait > 4u"hr"
        add!(m, MaintenanceDiet)
        wait!(m, wait - 4u"hr")
        remove!(m, MaintenanceDiet)
    end
    if degrade
        degrade!(tray2)
    end
    if pilfer
        pilfer!(tray2)
        pilfer!(tray1)
    end
    wait!(m, 4u"hr")
    add!(m, tray1)
    add!(m, tray2)
    inspectionobserver = InspectionObserver()
    add!(m, inspectionobserver)
    wait!(m, 5u"minute")
    remove!(m, Any)
    push!(results, [group, foodtype1string, id, set, trial, value(wait), "inspect",
                    countinspections(inspectionobserver, tray1),
                    firstinspection(inspectionobserver, tray1)])
    push!(results, [group, foodtype2string, id, set, trial, value(wait), "inspect",
                    countinspections(inspectionobserver, tray2),
                    firstinspection(inspectionobserver, tray2)])
    add!(m, MaintenanceDiet)
    wait!(m, 25u"minute" + 15u"hr")
end
function isdegrade(group, foodtype, wait)
    if group === "degrade"
        foodtype == Mealworm && wait > 4u"hr" ||
        foodtype == Cricket && wait > 28u"hr" ||
        foodtype == Waxworm && wait > 4u"hr"
    elseif group == "ripen"
        foodtype == Waxworm && wait <= 4u"hr"
    else
        false
    end
end
function run!(::Experiment{:Clayton01_exp1}, models)
    results = DataFrame(group = Union{String,Missing}[],
                        foodtype = Union{String,Missing}[],
                        id = Int[], set = Int[], trial = Union{String,Missing}[],
                        RI = Union{Int,Missing}[],
                        action = String[], counts = Int[],
                        firstinspection = Union{Missing,Int}[])
    id = 0
    for group in ["degrade", "replenish"]
        for i in 1:8
            id += 1
            m = models[id]
            # training (May 1999)
            for set in 1:4
                ris = [4u"hr", 28u"hr", 100u"hr"]
                isodd(set) && reverse!(ris)
                for wait in ris
                    for foodtype in [Mealworm, Cricket]
                        Clayton01_trial!(m, results, group, id, set, Peanut, foodtype, wait,
                               degrade = isdegrade(group, foodtype, wait), pilfer = false)
                    end
                end
            end
            # test
            ris = [28u"hr", 100u"hr", 4u"hr"]
            for wait in ris
                for foodtype in [Mealworm, Cricket]
                    Clayton01_trial!(m, results, group, id, 5, Peanut, foodtype, wait,
                           degrade = false, pilfer = true)
                    Clayton01_trial!(m, results, group, id, 0, Peanut, foodtype, 4u"hr",
                           degrade = false, pilfer = false)
                end
            end
        end
    end
    results
end

function _summarize(::Experiment{:Clayton01_exp2}, results)
    res = combine(groupby(@where(results, :action .== "inspect", :set .!= 0),
                          [:group, :foodtype, :action, :RI, :trial, :set]),
             df -> DataFrame(μ = mean(df.counts), sem = sem(df.counts),
                             firstinspection = mean(df.firstinspection),
                             n = length(df.counts)))
    for g in groupby(@where(results, :action .== "cache", :set .>= 6),
                     [:foodtype, :set])
        push!(res, [missing, g.foodtype[1], "cache", missing, missing, g.set[1],
                    mean(g.counts), sem(g.counts), 0, length(g.counts)])
    end
    res
end
function statistical_tests(exp::Experiment{:Clayton01_exp2}, data)
    tests = Test[]
    for g in groupby(@where(data, :set .== 6), :action)
        action  = g.action[1]
        id = "$action test exp 2"
        push!(tests, Test(id,
                          splitplotanova(g,
                                         exp.tests[id].locals,
                                         betweenfactors = :group,
                                         withinfactors = [:foodtype, :RI],
                                         id = :id, data = :counts),
                          exp.tests))
        if action == "inspect"
            for subg in groupby(g, :RI)
                id = "$action test exp 2 RI $(subg.RI[1])"
                push!(tests, Test(id,
                                  splitplotanova(subg,
                                                 exp.tests[id].locals,
                                                 betweenfactors = :group,
                                                 withinfactors = :foodtype,
                                                 id = :id, data = :counts),
                                  exp.tests))
            end
            for subg in groupby(g, :group)
                group = subg.group[1]
                id = "$action test exp 2 group $group"
                push!(tests, Test(id,
                                  anova(subg, @formula(counts ~ foodtype * RI), exp.tests[id].locals),
                                  exp.tests))
                if group == "degrade"
                    for subsubg in groupby(subg, :RI)
                        id = "$action test exp 2 group $group RI $(subsubg.RI[1])"
                        push!(tests, Test(id,
                                          anova(subsubg, @formula(counts ~ foodtype), exp.tests[id].locals),
                                          exp.tests))
                    end
                end
            end
        end
    end
    datatest2inspect = sort(@where(data, :set .== 6, :action .== "inspect"), [:id, :RI, :foodtype])
    push!(tests, Test("first inspect test exp 2",
                      chisqtest(firstinspect_dist(@where(datatest2inspect, :group .== "degrade")),
                                firstinspect_dist(@where(datatest2inspect, :group .== "replenish"))),
                      exp.tests))
    TestCollection(tests)
end
function run!(::Experiment{:Clayton01_exp2}, models)
    results = DataFrame(group = Union{String,Missing}[],
                        foodtype = Union{String,Missing}[],
                        id = Int[], set = Int[], trial = Union{String,Missing}[],
                        RI = Union{Int,Missing}[],
                        action = String[], counts = Int[],
                        firstinspection = Union{Missing,Int}[])
    id = 0
    for group in ["degrade", "replenish"]
        for i in 1:8
            id += 1
            m = models[id]
            # training before experiment 2 (July 1999)
            for foodtype in [Mealworm, Cricket]
                Clayton01_trial!(m, results, group, id, 0, Peanut, foodtype, 28u"hr",
                       degrade = isdegrade(group, foodtype, 28u"hr"), pilfer = false)
            end
            # test experiment 2
            ris = [4u"hr", 28u"hr"]
            isodd(id) && reverse!(ris)
            for wait in ris
                Clayton01_trial!(m, results, group, id, 6, Mealworm, Cricket, wait,
                       degrade = false, pilfer = true)
                # interleaved training trials
                for foodtype in [Mealworm, Cricket]
                    Clayton01_trial!(m, results, group, id, 0, Peanut, foodtype, 28u"hr",
                           degrade = isdegrade(group, foodtype, 28u"hr"), pilfer = false)
                end
            end
        end
    end
    results
end

function _summarize(::Experiment{:Clayton01_exp3}, results)
    res = [combine(groupby(@where(results, :action .== "inspect", :set .== 1),
                           [:group, :foodtype, :action, :trial]),
             df -> DataFrame(μ = mean(df.counts), sem = sem(df.counts),
                             firstinspection = mean(df.firstinspection),
                             n = length(df.counts)));
           combine(groupby(@where(results, :action .== "cache", :set .== 1), :foodtype),
              df -> DataFrame(μ = mean(df.counts), sem = sem(df.counts),
                              group = missing, action = "cache", trial = missing,
                              firstinspection = missing, n = length(df.counts)))]
end
function statistical_tests(exp::Experiment{:Clayton01_exp3}, data)
    tests = Test[]
    testdata = @where(data, :set .== 1)
    sort!(testdata, [:id, :foodtype, :trial])
    for g in groupby(testdata, :action)
        action = g.action[1]
        push!(tests, Test(action,
                          splitplotanova(g,
                                         exp.tests[action].locals,
                                         betweenfactors = :group,
                                         withinfactors = [:foodtype, :trial],
                                         id = :id, data = :counts),
                          exp.tests))
        if action == "inspect"
            for subg in groupby(g, :trial)
                id = "$action $(subg.trial[1])"
                push!(tests, Test(id,
                                  splitplotanova(subg,
                                                 exp.tests[id].locals,
                                                 betweenfactors = :group,
                                                 withinfactors = :foodtype,
                                                 id = :id, data = :counts),
                                  exp.tests))
            end
            for subg in groupby(g, :group)
                group = subg.group[1]
                id = "$action $group"
                push!(tests, Test(id,
                                  splitplotanova(subg,
                                                 exp.tests[id].locals,
                                                 betweenfactors = [],
                                                 withinfactors = [:foodtype, :trial],
                                                 id = :id, data = :counts),
                                  exp.tests))
                if group == "degrade"
                    for subsubg in groupby(subg, :trial)
                        trial = subsubg.trial[1]
                        condbigger = trial == "mc" ? "cricket" : "mealworm"
                        condsmaller = trial == "mc" ? "mealworm" : "cricket"
                        push!(tests, Test("$action $group $trial",
                                          is_significantly_bigger(subsubg,
                                                                  conditionkey = :foodtype,
                                                                  condbigger = condbigger,
                                                                  condsmaller = condsmaller,
                                                                  valuekey = :counts),
                                          exp.tests))
                    end
                end
            end
            push!(tests, Test("first inspectes",
                              chisqtest(firstinspect_dist(@where(g, :group .== "degrade")),
                                        firstinspect_dist(@where(g, :group .== "replenish"))),
                              exp.tests))
        end
    end
    TestCollection(tests)
end
function run!(::Experiment{:Clayton01_exp3}, models)
    results = DataFrame(group = Union{String,Missing}[],
                        foodtype = Union{String,Missing}[],
                        id = Int[], set = Int[], trial = Union{String,Missing}[],
                        RI = Union{Int,Missing}[],
                        action = String[], counts = Int[],
                        firstinspection = Union{Missing,Int}[])
    id = 0
    for group in ["degrade", "replenish"]
        for i in 1:8
            id += 1
            m = models[id]
            # training (November 1999)
            for wait in [28u"hr", 4u"hr"]
                for foodtype in [Mealworm, Cricket]
                    Clayton01_trial!(m, results, group, id, 0, Peanut, foodtype, wait,
                           degrade = isdegrade(group, foodtype, wait), pilfer = false)
                end
            end
            # testing
            trials = [(Cricket, Mealworm), (Mealworm, Cricket)]
            i >= 4 && reverse!(trials)
            for (food1, food2) in trials
                Clayton01_trial!(m, results, group, id, 1, food1, food2, 4u"hr",
                       degrade = false, pilfer = true, bothtrayspresent = true,
                       waitbetween = 24u"hr")
                # interleaved training
                for foodtype in [Mealworm, Cricket]
                    Clayton01_trial!(m, results, group, id, 0, Peanut, foodtype, 4u"hr",
                           degrade = false, pilfer = false)
                end
            end
        end
    end
    results
end

function _summarize(::Experiment{:Clayton01_exp4}, results)
    res = [combine(groupby(@where(results, :action .== "inspect", :set .== 1),
                           [:group, :foodtype, :action, :RI]),
             df -> DataFrame(μ = mean(df.counts), sem = sem(df.counts),
                             firstinspection = mean(df.firstinspection),
                             n = length(df.counts)));
           combine(groupby(@where(results, :action .== "cache", :set .== 1),
                           [:group, :foodtype]),
              df -> DataFrame(μ = mean(df.counts), sem = sem(df.counts),
                              action = "cache", RI = missing,
                              firstinspection = missing, n = length(df.counts)))]
end
function statistical_tests(exp::Experiment{:Clayton01_exp4}, data)
    tests = Test[]
    datacache = @where(data, :action .== "cache", :set .== 1)
    id = "cache"
    push!(tests, Test(id,
                      splitplotanova(datacache,
                                     exp.tests[id].locals,
                                     betweenfactors = :group,
                                     withinfactors = [:foodtype, :RI],
                                     id = :id, data = :counts),
                      exp.tests))
    for g in groupby(datacache, :foodtype)
        id = "cache $(g.foodtype[1])"
        push!(tests, Test(id,
                          splitplotanova(g,
                                         exp.tests[id].locals,
                                         betweenfactors = :group,
                                         withinfactors = :RI,
                                         id = :id, data = :counts),
                          exp.tests))
    end
    datacache = @where(data, :action .== "inspect", :set .== 1)
    sort!(datacache, [:id, :foodtype, :RI])
    id = "inspect"
    push!(tests, Test(id,
                      splitplotanova(datacache,
                                     exp.tests[id].locals,
                                     betweenfactors = :group,
                                     withinfactors = [:foodtype, :RI],
                                     id = :id, data = :counts),
                      exp.tests))
    for g in groupby(datacache, :RI)
        id = "inspect $(g.RI[1])"
        push!(tests, Test(id,
                          splitplotanova(g,
                                         exp.tests[id].locals,
                                         betweenfactors = :group,
                                         withinfactors = :foodtype,
                                         id = :id, data = :counts),
                          exp.tests))
    end
    datacacheegrade = @where(datacache, :group .== "degrade")
    id = "inspect degrade"
    push!(tests, Test(id,
                      splitplotanova(datacacheegrade,
                                     exp.tests[id].locals,
                                     betweenfactors = [],
                                     withinfactors = [:foodtype, :RI],
                                     id = :id, data = :counts),
                      exp.tests))
    for g in groupby(datacacheegrade, :RI)
        id = "inspect degrade $(g.RI[1])"
        push!(tests, Test(id,
                          splitplotanova(g,
                                         exp.tests[id].locals,
                                         betweenfactors = [],
                                         withinfactors = :foodtype,
                                         id = :id, data = :counts),
                          exp.tests))
    end
    push!(tests, Test("first inspectes",
                      chisqtest(firstinspect_dist(@where(datacache, :group .== "degrade")),
                                firstinspect_dist(@where(datacache, :group .== "replenish"))),
                      exp.tests))
    TestCollection(tests)
end
function run!(::Experiment{:Clayton01_exp4}, models)
    results = DataFrame(group = Union{String,Missing}[],
                        foodtype = Union{String,Missing}[],
                        id = Int[], set = Int[], trial = Union{String,Missing}[],
                        RI = Union{Int,Missing}[],
                        action = String[], counts = Int[],
                        firstinspection = Union{Missing,Int}[])
    id = 0
    for group in ["degrade", "replenish"]
        for i in 1:8
            id += 1
            m = models[id]
            # training (December 1999)
            Clayton01_trial!(m, results, group, id, 0, Peanut, Mealworm, 4u"hr",
                   degrade = false, pilfer = false)
            remove!(m, Any)
            wait!(m, 4u"hr")
            tray1 = Tray(hack(1)) # one half of tray
            tray2 = Tray(hack(2)) # second half of tray
            add!(m, tray1)
            add!(m, Peanut, 50)
            wait!(m, 15u"minute")
            remove!(m, Any)
            push!(results, [group, "peanut", id, 1, "pm", 28, "cache", countcache(tray1), missing])
            add!(m, tray2)
            add!(m, Mealworm, 50)
            wait!(m, 15u"minute")
            remove!(m, Any)
            push!(results, [group, "mealworm", id, 1, "pm", 28, "cache", countcache(tray2), missing])
            add!(m, MaintenanceDiet)
            wait!(m, 30u"minute" + 23u"hr")
            tray3 = Tray(hack(1)) # one half of tray
            tray4 = Tray(hack(2)) # second half of tray
            add!(m, tray3)
            add!(m, Peanut, 50)
            wait!(m, 15u"minute")
            remove!(m, Any)
            push!(results, [group, "peanut", id, 1, "pm", 4, "cache", countcache(tray3), missing])
            add!(m, tray4)
            add!(m, Mealworm, 50)
            wait!(m, 15u"minute")
            remove!(m, Any)
            push!(results, [group, "mealworm", id, 1, "pm", 4, "cache", countcache(tray4), missing])
            wait!(m, 4u"hr")
            pilfer!(tray1); pilfer!(tray2); pilfer!(tray3); pilfer!(tray4)
            add!(m, tray1); add!(m, tray2)
            inspectionobserver = InspectionObserver()
            add!(m, inspectionobserver)
            wait!(m, 5u"minute")
            remove!(m, Any)
            push!(results, [group, "peanut", id, 1, "pm", 28, "inspect",
                            countinspections(inspectionobserver, tray1),
                            firstinspection(inspectionobserver, tray1)])
            push!(results, [group, "mealworm", id, 1, "pm", 28, "inspect",
                            countinspections(inspectionobserver, tray2),
                            firstinspection(inspectionobserver, tray2)])
            add!(m, tray3); add!(m, tray4)
            inspectionobserver = InspectionObserver()
            add!(m, inspectionobserver)
            wait!(m, 5u"minute")
            remove!(m, Any)
            push!(results, [group, "peanut", id, 1, "pm", 4, "inspect",
                            countinspections(inspectionobserver, tray3),
                            firstinspection(inspectionobserver, tray3)])
            push!(results, [group, "mealworm", id, 1, "pm", 4, "inspect",
                            countinspections(inspectionobserver, tray4),
                            firstinspection(inspectionobserver, tray4)])
        end
    end
    results
end


text:
#= Tests
using PGFPlotsX
x1 = linspace(1,10,10)
y1 = linspace(1,10,10)
y2 = linspace(10,0,10)

#@test scatterplot(x1,y1) isa(Plots.Plot)
#@test scatterplot(x1,y2) isa(Plots.Plot)
=#


text:
#!/usr/bin/env julia

using Images, ImageView, Gtk.ShortNames
using FileIO

function main(args)
    img = load(args[1])

    guidict = imshow(img);

    #If we are not in a REPL
    if (!isinteractive())

        # Create a condition object
        c = Condition()

        # Get the window
        win = guidict["gui"]["window"]

        # Notify the condition object when the window closes
        signal_connect(win, :destroy) do widget
            notify(c)
        end

        # Wait for the notification before proceeding ...
        wait(c)
    end
end

main(ARGS)


text:
# syntax: proto3
using ProtoBuf
import ProtoBuf.meta

mutable struct Client <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Client(; kwargs...)
        obj = new(meta(Client), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Client
const __meta_Client = Ref{ProtoMeta}()
function meta(::Type{Client})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Client)
            __meta_Client[] = target = ProtoMeta(Client)
            allflds = Pair{Symbol,Union{Type,String}}[:id => AbstractString, :secret => AbstractString, :redirect_uris => Base.Vector{AbstractString}, :trusted_peers => Base.Vector{AbstractString}, :public => Bool, :name => AbstractString, :logo_url => AbstractString]
            meta(target, Client, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Client[]
    end
end
function Base.getproperty(obj::Client, name::Symbol)
    if name === :id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :secret
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :redirect_uris
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    elseif name === :trusted_peers
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    elseif name === :public
        return (obj.__protobuf_jl_internal_values[name])::Bool
    elseif name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :logo_url
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct CreateClientReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function CreateClientReq(; kwargs...)
        obj = new(meta(CreateClientReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct CreateClientReq
const __meta_CreateClientReq = Ref{ProtoMeta}()
function meta(::Type{CreateClientReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_CreateClientReq)
            __meta_CreateClientReq[] = target = ProtoMeta(CreateClientReq)
            allflds = Pair{Symbol,Union{Type,String}}[:client => Client]
            meta(target, CreateClientReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_CreateClientReq[]
    end
end
function Base.getproperty(obj::CreateClientReq, name::Symbol)
    if name === :client
        return (obj.__protobuf_jl_internal_values[name])::Client
    else
        getfield(obj, name)
    end
end

mutable struct CreateClientResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function CreateClientResp(; kwargs...)
        obj = new(meta(CreateClientResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct CreateClientResp
const __meta_CreateClientResp = Ref{ProtoMeta}()
function meta(::Type{CreateClientResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_CreateClientResp)
            __meta_CreateClientResp[] = target = ProtoMeta(CreateClientResp)
            allflds = Pair{Symbol,Union{Type,String}}[:already_exists => Bool, :client => Client]
            meta(target, CreateClientResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_CreateClientResp[]
    end
end
function Base.getproperty(obj::CreateClientResp, name::Symbol)
    if name === :already_exists
        return (obj.__protobuf_jl_internal_values[name])::Bool
    elseif name === :client
        return (obj.__protobuf_jl_internal_values[name])::Client
    else
        getfield(obj, name)
    end
end

mutable struct DeleteClientReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function DeleteClientReq(; kwargs...)
        obj = new(meta(DeleteClientReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct DeleteClientReq
const __meta_DeleteClientReq = Ref{ProtoMeta}()
function meta(::Type{DeleteClientReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_DeleteClientReq)
            __meta_DeleteClientReq[] = target = ProtoMeta(DeleteClientReq)
            allflds = Pair{Symbol,Union{Type,String}}[:id => AbstractString]
            meta(target, DeleteClientReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_DeleteClientReq[]
    end
end
function Base.getproperty(obj::DeleteClientReq, name::Symbol)
    if name === :id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct DeleteClientResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function DeleteClientResp(; kwargs...)
        obj = new(meta(DeleteClientResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct DeleteClientResp
const __meta_DeleteClientResp = Ref{ProtoMeta}()
function meta(::Type{DeleteClientResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_DeleteClientResp)
            __meta_DeleteClientResp[] = target = ProtoMeta(DeleteClientResp)
            allflds = Pair{Symbol,Union{Type,String}}[:not_found => Bool]
            meta(target, DeleteClientResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_DeleteClientResp[]
    end
end
function Base.getproperty(obj::DeleteClientResp, name::Symbol)
    if name === :not_found
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct UpdateClientReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function UpdateClientReq(; kwargs...)
        obj = new(meta(UpdateClientReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct UpdateClientReq
const __meta_UpdateClientReq = Ref{ProtoMeta}()
function meta(::Type{UpdateClientReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_UpdateClientReq)
            __meta_UpdateClientReq[] = target = ProtoMeta(UpdateClientReq)
            allflds = Pair{Symbol,Union{Type,String}}[:id => AbstractString, :redirect_uris => Base.Vector{AbstractString}, :trusted_peers => Base.Vector{AbstractString}, :name => AbstractString, :logo_url => AbstractString]
            meta(target, UpdateClientReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_UpdateClientReq[]
    end
end
function Base.getproperty(obj::UpdateClientReq, name::Symbol)
    if name === :id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :redirect_uris
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    elseif name === :trusted_peers
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{AbstractString}
    elseif name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :logo_url
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct UpdateClientResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function UpdateClientResp(; kwargs...)
        obj = new(meta(UpdateClientResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct UpdateClientResp
const __meta_UpdateClientResp = Ref{ProtoMeta}()
function meta(::Type{UpdateClientResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_UpdateClientResp)
            __meta_UpdateClientResp[] = target = ProtoMeta(UpdateClientResp)
            allflds = Pair{Symbol,Union{Type,String}}[:not_found => Bool]
            meta(target, UpdateClientResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_UpdateClientResp[]
    end
end
function Base.getproperty(obj::UpdateClientResp, name::Symbol)
    if name === :not_found
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct Password <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Password(; kwargs...)
        obj = new(meta(Password), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Password
const __meta_Password = Ref{ProtoMeta}()
function meta(::Type{Password})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Password)
            __meta_Password[] = target = ProtoMeta(Password)
            allflds = Pair{Symbol,Union{Type,String}}[:email => AbstractString, :hash => Array{UInt8,1}, :username => AbstractString, :user_id => AbstractString]
            meta(target, Password, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Password[]
    end
end
function Base.getproperty(obj::Password, name::Symbol)
    if name === :email
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :hash
        return (obj.__protobuf_jl_internal_values[name])::Array{UInt8,1}
    elseif name === :username
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :user_id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct CreatePasswordReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function CreatePasswordReq(; kwargs...)
        obj = new(meta(CreatePasswordReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct CreatePasswordReq
const __meta_CreatePasswordReq = Ref{ProtoMeta}()
function meta(::Type{CreatePasswordReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_CreatePasswordReq)
            __meta_CreatePasswordReq[] = target = ProtoMeta(CreatePasswordReq)
            allflds = Pair{Symbol,Union{Type,String}}[:password => Password]
            meta(target, CreatePasswordReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_CreatePasswordReq[]
    end
end
function Base.getproperty(obj::CreatePasswordReq, name::Symbol)
    if name === :password
        return (obj.__protobuf_jl_internal_values[name])::Password
    else
        getfield(obj, name)
    end
end

mutable struct CreatePasswordResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function CreatePasswordResp(; kwargs...)
        obj = new(meta(CreatePasswordResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct CreatePasswordResp
const __meta_CreatePasswordResp = Ref{ProtoMeta}()
function meta(::Type{CreatePasswordResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_CreatePasswordResp)
            __meta_CreatePasswordResp[] = target = ProtoMeta(CreatePasswordResp)
            allflds = Pair{Symbol,Union{Type,String}}[:already_exists => Bool]
            meta(target, CreatePasswordResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_CreatePasswordResp[]
    end
end
function Base.getproperty(obj::CreatePasswordResp, name::Symbol)
    if name === :already_exists
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct UpdatePasswordReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function UpdatePasswordReq(; kwargs...)
        obj = new(meta(UpdatePasswordReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct UpdatePasswordReq
const __meta_UpdatePasswordReq = Ref{ProtoMeta}()
function meta(::Type{UpdatePasswordReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_UpdatePasswordReq)
            __meta_UpdatePasswordReq[] = target = ProtoMeta(UpdatePasswordReq)
            allflds = Pair{Symbol,Union{Type,String}}[:email => AbstractString, :new_hash => Array{UInt8,1}, :new_username => AbstractString]
            meta(target, UpdatePasswordReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_UpdatePasswordReq[]
    end
end
function Base.getproperty(obj::UpdatePasswordReq, name::Symbol)
    if name === :email
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :new_hash
        return (obj.__protobuf_jl_internal_values[name])::Array{UInt8,1}
    elseif name === :new_username
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct UpdatePasswordResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function UpdatePasswordResp(; kwargs...)
        obj = new(meta(UpdatePasswordResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct UpdatePasswordResp
const __meta_UpdatePasswordResp = Ref{ProtoMeta}()
function meta(::Type{UpdatePasswordResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_UpdatePasswordResp)
            __meta_UpdatePasswordResp[] = target = ProtoMeta(UpdatePasswordResp)
            allflds = Pair{Symbol,Union{Type,String}}[:not_found => Bool]
            meta(target, UpdatePasswordResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_UpdatePasswordResp[]
    end
end
function Base.getproperty(obj::UpdatePasswordResp, name::Symbol)
    if name === :not_found
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct DeletePasswordReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function DeletePasswordReq(; kwargs...)
        obj = new(meta(DeletePasswordReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct DeletePasswordReq
const __meta_DeletePasswordReq = Ref{ProtoMeta}()
function meta(::Type{DeletePasswordReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_DeletePasswordReq)
            __meta_DeletePasswordReq[] = target = ProtoMeta(DeletePasswordReq)
            allflds = Pair{Symbol,Union{Type,String}}[:email => AbstractString]
            meta(target, DeletePasswordReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_DeletePasswordReq[]
    end
end
function Base.getproperty(obj::DeletePasswordReq, name::Symbol)
    if name === :email
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct DeletePasswordResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function DeletePasswordResp(; kwargs...)
        obj = new(meta(DeletePasswordResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct DeletePasswordResp
const __meta_DeletePasswordResp = Ref{ProtoMeta}()
function meta(::Type{DeletePasswordResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_DeletePasswordResp)
            __meta_DeletePasswordResp[] = target = ProtoMeta(DeletePasswordResp)
            allflds = Pair{Symbol,Union{Type,String}}[:not_found => Bool]
            meta(target, DeletePasswordResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_DeletePasswordResp[]
    end
end
function Base.getproperty(obj::DeletePasswordResp, name::Symbol)
    if name === :not_found
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct ListPasswordReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListPasswordReq(; kwargs...)
        obj = new(meta(ListPasswordReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListPasswordReq
const __meta_ListPasswordReq = Ref{ProtoMeta}()
function meta(::Type{ListPasswordReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListPasswordReq)
            __meta_ListPasswordReq[] = target = ProtoMeta(ListPasswordReq)
            allflds = Pair{Symbol,Union{Type,String}}[]
            meta(target, ListPasswordReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListPasswordReq[]
    end
end

mutable struct ListPasswordResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListPasswordResp(; kwargs...)
        obj = new(meta(ListPasswordResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListPasswordResp
const __meta_ListPasswordResp = Ref{ProtoMeta}()
function meta(::Type{ListPasswordResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListPasswordResp)
            __meta_ListPasswordResp[] = target = ProtoMeta(ListPasswordResp)
            allflds = Pair{Symbol,Union{Type,String}}[:passwords => Base.Vector{Password}]
            meta(target, ListPasswordResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListPasswordResp[]
    end
end
function Base.getproperty(obj::ListPasswordResp, name::Symbol)
    if name === :passwords
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Password}
    else
        getfield(obj, name)
    end
end

mutable struct VersionReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function VersionReq(; kwargs...)
        obj = new(meta(VersionReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct VersionReq
const __meta_VersionReq = Ref{ProtoMeta}()
function meta(::Type{VersionReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_VersionReq)
            __meta_VersionReq[] = target = ProtoMeta(VersionReq)
            allflds = Pair{Symbol,Union{Type,String}}[]
            meta(target, VersionReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_VersionReq[]
    end
end

mutable struct VersionResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function VersionResp(; kwargs...)
        obj = new(meta(VersionResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct VersionResp
const __meta_VersionResp = Ref{ProtoMeta}()
function meta(::Type{VersionResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_VersionResp)
            __meta_VersionResp[] = target = ProtoMeta(VersionResp)
            allflds = Pair{Symbol,Union{Type,String}}[:server => AbstractString, :api => Int32]
            meta(target, VersionResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_VersionResp[]
    end
end
function Base.getproperty(obj::VersionResp, name::Symbol)
    if name === :server
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :api
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct RefreshTokenRef <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function RefreshTokenRef(; kwargs...)
        obj = new(meta(RefreshTokenRef), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct RefreshTokenRef
const __meta_RefreshTokenRef = Ref{ProtoMeta}()
function meta(::Type{RefreshTokenRef})
    ProtoBuf.metalock() do
        if !isassigned(__meta_RefreshTokenRef)
            __meta_RefreshTokenRef[] = target = ProtoMeta(RefreshTokenRef)
            fnum = Int[1,2,5,6]
            allflds = Pair{Symbol,Union{Type,String}}[:id => AbstractString, :client_id => AbstractString, :created_at => Int64, :last_used => Int64]
            meta(target, RefreshTokenRef, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_RefreshTokenRef[]
    end
end
function Base.getproperty(obj::RefreshTokenRef, name::Symbol)
    if name === :id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :client_id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :created_at
        return (obj.__protobuf_jl_internal_values[name])::Int64
    elseif name === :last_used
        return (obj.__protobuf_jl_internal_values[name])::Int64
    else
        getfield(obj, name)
    end
end

mutable struct ListRefreshReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListRefreshReq(; kwargs...)
        obj = new(meta(ListRefreshReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListRefreshReq
const __meta_ListRefreshReq = Ref{ProtoMeta}()
function meta(::Type{ListRefreshReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListRefreshReq)
            __meta_ListRefreshReq[] = target = ProtoMeta(ListRefreshReq)
            allflds = Pair{Symbol,Union{Type,String}}[:user_id => AbstractString]
            meta(target, ListRefreshReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListRefreshReq[]
    end
end
function Base.getproperty(obj::ListRefreshReq, name::Symbol)
    if name === :user_id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct ListRefreshResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListRefreshResp(; kwargs...)
        obj = new(meta(ListRefreshResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListRefreshResp
const __meta_ListRefreshResp = Ref{ProtoMeta}()
function meta(::Type{ListRefreshResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListRefreshResp)
            __meta_ListRefreshResp[] = target = ProtoMeta(ListRefreshResp)
            allflds = Pair{Symbol,Union{Type,String}}[:refresh_tokens => Base.Vector{RefreshTokenRef}]
            meta(target, ListRefreshResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListRefreshResp[]
    end
end
function Base.getproperty(obj::ListRefreshResp, name::Symbol)
    if name === :refresh_tokens
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{RefreshTokenRef}
    else
        getfield(obj, name)
    end
end

mutable struct RevokeRefreshReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function RevokeRefreshReq(; kwargs...)
        obj = new(meta(RevokeRefreshReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct RevokeRefreshReq
const __meta_RevokeRefreshReq = Ref{ProtoMeta}()
function meta(::Type{RevokeRefreshReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_RevokeRefreshReq)
            __meta_RevokeRefreshReq[] = target = ProtoMeta(RevokeRefreshReq)
            allflds = Pair{Symbol,Union{Type,String}}[:user_id => AbstractString, :client_id => AbstractString]
            meta(target, RevokeRefreshReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_RevokeRefreshReq[]
    end
end
function Base.getproperty(obj::RevokeRefreshReq, name::Symbol)
    if name === :user_id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :client_id
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct RevokeRefreshResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function RevokeRefreshResp(; kwargs...)
        obj = new(meta(RevokeRefreshResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct RevokeRefreshResp
const __meta_RevokeRefreshResp = Ref{ProtoMeta}()
function meta(::Type{RevokeRefreshResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_RevokeRefreshResp)
            __meta_RevokeRefreshResp[] = target = ProtoMeta(RevokeRefreshResp)
            allflds = Pair{Symbol,Union{Type,String}}[:not_found => Bool]
            meta(target, RevokeRefreshResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_RevokeRefreshResp[]
    end
end
function Base.getproperty(obj::RevokeRefreshResp, name::Symbol)
    if name === :not_found
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct VerifyPasswordReq <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function VerifyPasswordReq(; kwargs...)
        obj = new(meta(VerifyPasswordReq), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct VerifyPasswordReq
const __meta_VerifyPasswordReq = Ref{ProtoMeta}()
function meta(::Type{VerifyPasswordReq})
    ProtoBuf.metalock() do
        if !isassigned(__meta_VerifyPasswordReq)
            __meta_VerifyPasswordReq[] = target = ProtoMeta(VerifyPasswordReq)
            allflds = Pair{Symbol,Union{Type,String}}[:email => AbstractString, :password => AbstractString]
            meta(target, VerifyPasswordReq, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_VerifyPasswordReq[]
    end
end
function Base.getproperty(obj::VerifyPasswordReq, name::Symbol)
    if name === :email
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :password
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct VerifyPasswordResp <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function VerifyPasswordResp(; kwargs...)
        obj = new(meta(VerifyPasswordResp), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct VerifyPasswordResp
const __meta_VerifyPasswordResp = Ref{ProtoMeta}()
function meta(::Type{VerifyPasswordResp})
    ProtoBuf.metalock() do
        if !isassigned(__meta_VerifyPasswordResp)
            __meta_VerifyPasswordResp[] = target = ProtoMeta(VerifyPasswordResp)
            allflds = Pair{Symbol,Union{Type,String}}[:verified => Bool, :not_found => Bool]
            meta(target, VerifyPasswordResp, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_VerifyPasswordResp[]
    end
end
function Base.getproperty(obj::VerifyPasswordResp, name::Symbol)
    if name === :verified
        return (obj.__protobuf_jl_internal_values[name])::Bool
    elseif name === :not_found
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

# service methods for Dex
const _Dex_methods = MethodDescriptor[
        MethodDescriptor("CreateClient", 1, CreateClientReq, CreateClientResp),
        MethodDescriptor("UpdateClient", 2, UpdateClientReq, UpdateClientResp),
        MethodDescriptor("DeleteClient", 3, DeleteClientReq, DeleteClientResp),
        MethodDescriptor("CreatePassword", 4, CreatePasswordReq, CreatePasswordResp),
        MethodDescriptor("UpdatePassword", 5, UpdatePasswordReq, UpdatePasswordResp),
        MethodDescriptor("DeletePassword", 6, DeletePasswordReq, DeletePasswordResp),
        MethodDescriptor("ListPasswords", 7, ListPasswordReq, ListPasswordResp),
        MethodDescriptor("GetVersion", 8, VersionReq, VersionResp),
        MethodDescriptor("ListRefresh", 9, ListRefreshReq, ListRefreshResp),
        MethodDescriptor("RevokeRefresh", 10, RevokeRefreshReq, RevokeRefreshResp),
        MethodDescriptor("VerifyPassword", 11, VerifyPasswordReq, VerifyPasswordResp)
    ] # const _Dex_methods
const _Dex_desc = ServiceDescriptor("api.Dex", 1, _Dex_methods)

Dex(impl::Module) = ProtoService(_Dex_desc, impl)

mutable struct DexStub <: AbstractProtoServiceStub{false}
    impl::ProtoServiceStub
    DexStub(channel::ProtoRpcChannel) = new(ProtoServiceStub(_Dex_desc, channel))
end # mutable struct DexStub

mutable struct DexBlockingStub <: AbstractProtoServiceStub{true}
    impl::ProtoServiceBlockingStub
    DexBlockingStub(channel::ProtoRpcChannel) = new(ProtoServiceBlockingStub(_Dex_desc, channel))
end # mutable struct DexBlockingStub

CreateClient(stub::DexStub, controller::ProtoRpcController, inp::CreateClientReq, done::Function) = call_method(stub.impl, _Dex_methods[1], controller, inp, done)
CreateClient(stub::DexBlockingStub, controller::ProtoRpcController, inp::CreateClientReq) = call_method(stub.impl, _Dex_methods[1], controller, inp)

UpdateClient(stub::DexStub, controller::ProtoRpcController, inp::UpdateClientReq, done::Function) = call_method(stub.impl, _Dex_methods[2], controller, inp, done)
UpdateClient(stub::DexBlockingStub, controller::ProtoRpcController, inp::UpdateClientReq) = call_method(stub.impl, _Dex_methods[2], controller, inp)

DeleteClient(stub::DexStub, controller::ProtoRpcController, inp::DeleteClientReq, done::Function) = call_method(stub.impl, _Dex_methods[3], controller, inp, done)
DeleteClient(stub::DexBlockingStub, controller::ProtoRpcController, inp::DeleteClientReq) = call_method(stub.impl, _Dex_methods[3], controller, inp)

CreatePassword(stub::DexStub, controller::ProtoRpcController, inp::CreatePasswordReq, done::Function) = call_method(stub.impl, _Dex_methods[4], controller, inp, done)
CreatePassword(stub::DexBlockingStub, controller::ProtoRpcController, inp::CreatePasswordReq) = call_method(stub.impl, _Dex_methods[4], controller, inp)

UpdatePassword(stub::DexStub, controller::ProtoRpcController, inp::UpdatePasswordReq, done::Function) = call_method(stub.impl, _Dex_methods[5], controller, inp, done)
UpdatePassword(stub::DexBlockingStub, controller::ProtoRpcController, inp::UpdatePasswordReq) = call_method(stub.impl, _Dex_methods[5], controller, inp)

DeletePassword(stub::DexStub, controller::ProtoRpcController, inp::DeletePasswordReq, done::Function) = call_method(stub.impl, _Dex_methods[6], controller, inp, done)
DeletePassword(stub::DexBlockingStub, controller::ProtoRpcController, inp::DeletePasswordReq) = call_method(stub.impl, _Dex_methods[6], controller, inp)

ListPasswords(stub::DexStub, controller::ProtoRpcController, inp::ListPasswordReq, done::Function) = call_method(stub.impl, _Dex_methods[7], controller, inp, done)
ListPasswords(stub::DexBlockingStub, controller::ProtoRpcController, inp::ListPasswordReq) = call_method(stub.impl, _Dex_methods[7], controller, inp)

GetVersion(stub::DexStub, controller::ProtoRpcController, inp::VersionReq, done::Function) = call_method(stub.impl, _Dex_methods[8], controller, inp, done)
GetVersion(stub::DexBlockingStub, controller::ProtoRpcController, inp::VersionReq) = call_method(stub.impl, _Dex_methods[8], controller, inp)

ListRefresh(stub::DexStub, controller::ProtoRpcController, inp::ListRefreshReq, done::Function) = call_method(stub.impl, _Dex_methods[9], controller, inp, done)
ListRefresh(stub::DexBlockingStub, controller::ProtoRpcController, inp::ListRefreshReq) = call_method(stub.impl, _Dex_methods[9], controller, inp)

RevokeRefresh(stub::DexStub, controller::ProtoRpcController, inp::RevokeRefreshReq, done::Function) = call_method(stub.impl, _Dex_methods[10], controller, inp, done)
RevokeRefresh(stub::DexBlockingStub, controller::ProtoRpcController, inp::RevokeRefreshReq) = call_method(stub.impl, _Dex_methods[10], controller, inp)

VerifyPassword(stub::DexStub, controller::ProtoRpcController, inp::VerifyPasswordReq, done::Function) = call_method(stub.impl, _Dex_methods[11], controller, inp, done)
VerifyPassword(stub::DexBlockingStub, controller::ProtoRpcController, inp::VerifyPasswordReq) = call_method(stub.impl, _Dex_methods[11], controller, inp)

export Client, CreateClientReq, CreateClientResp, DeleteClientReq, DeleteClientResp, UpdateClientReq, UpdateClientResp, Password, CreatePasswordReq, CreatePasswordResp, UpdatePasswordReq, UpdatePasswordResp, DeletePasswordReq, DeletePasswordResp, ListPasswordReq, ListPasswordResp, VersionReq, VersionResp, RefreshTokenRef, ListRefreshReq, ListRefreshResp, RevokeRefreshReq, RevokeRefreshResp, VerifyPasswordReq, VerifyPasswordResp, Dex, DexStub, DexBlockingStub, CreateClient, UpdateClient, DeleteClient, CreatePassword, UpdatePassword, DeletePassword, ListPasswords, GetVersion, ListRefresh, RevokeRefresh, VerifyPassword


text:
using Sigma
Sigma.restart_counter!()
# import Base:size,getindex
typealias Point AbstractVector
typealias Vec AbstractVector
typealias Mat AbstractMatrix

dist(A,B) = sum(abs(A - B)) < 0.01

typealias Map Mat
typealias Path Mat

# Length of path
path_length(p::Path) = size(p,2)

# Comptue the cost of a path
function path_cost(p::Path, m::Map, terrain_costs::Vec)
  total_cost = 0.0
  for i = 1:path_length(p)
    @show p[1,i]
    @show p[2,i]
    terrain = m[p[1,i],p[2,i]]
    total_cost += terrain_costs[terrain]
  end
  total_cost
end

function inverse_planning(m::Map, terrain_costs::Vec, observed_path::Path)
  # Assume costs between 0 and 1
  max_cost = length(terrain_costs)
  min_cost = 0.0

  # Path Length
  plen = path_length(observed_path)
  path = mvuniform(1,max(size(m)...),2,plen)
  # path starts and ends in right place

  # Find the minimal cost
  path, dist(path,observed_path) & (path_cost(observed_path, m, terrain_costs) < 0.1)
end

# function inverse_planning_two()
#   # Choose some subset of omega
#   # This will define a subset of the terrain space
#   # Then for this subset find a subse
# end

# exp_λ = 0.1

## Maps
## =====
## Eaxmple
amap =
  [1 1 1 1 1 1 1
   1 1 1 1 1 1 1
   1 1 2 2 2 2 2
   0 0 0 0 2 2 2
   0 0 0 0 2 2 2
   0 0 0 0 0 0 0]

amap =
  [2 2 2 2 2 2 2
   2 2 2 2 2 2 2
   2 2 0 0 0 0 0
   1 1 1 1 0 0 0
   1 1 1 1 0 0 0
   1 1 1 1 1 1 1]

amap =
  [0 1 0
   1 1 0
   0 0 0
   2 0 2
   0 1 0
   0 0 0]

## Terrain Costs
terrain_costs = mvuniform(0,1,3)

## Observed Paths
## ==============
observed_path = [6 5 4 3 2 1 1 1
                 1 1 1 2 3 4 5 6]

observed_path = [6 6 6 6 6 6 6 5 4 3 2
                 1 2 3 4 5 6 7 7 7 7 7]

observed_path = [4 4 4 3 2 2 2 2 2 2
                 4 3 2 2 2 3 4 5 6 7]

observed_path = [6 5 4
                 2 2 2]

path, condition = inverse_planning(amap+1, terrain_costs, observed_path)
println("Converting into randvar")
# a = convert(Sigma.DRealRandVar{Bool}, condition)

@show dims(condition)
println("Drawing samples")
# samples = Sigma.pre_tlmh(condition, 1)

# terrains = rand(terrain_costs, condition,2,pre_tlmh, DRealSolverBinary)
# pre = Sigma.pre_tlmh_parallel(condition,10,Sigma.DRealSolverBinary;ncores=4)


text:
import HTTP

res = HTTP.get("https://example.com")
@show res

max_stars_repo_name: mkanenobu/trashbox

max_issues_repo_name: mkanenobu/trashbox

max_forks_repo_name: mkanenobu/trashbox