text:
split_state(o::Tuple, u::AbstractArray) = split_state(o, u, 0)
split_state(o::Tuple{O,Vararg}, u::AbstractArray, offset) where O = begin
    v = view(u, offset+1:offset+STATELEN)
    lv = LArray{STATE}(v)
    (lv, split_state(tail(o), u, offset + STATELEN)...)
end
split_state(o::Tuple{}, u::AbstractArray, offset) = ()

" Sum flux matrix "
sum_flux!(du, organs::Tuple) = begin
    offset_apply(sum_flux!, du, organs, 0)
    du
end
sum_flux!(du, organ::Organ, offset::Int) = begin
    J = flux(organ)
    z = zero(J[1,1])
    for i in 1:size(J, 1)
        s = z
        for j in 1:size(J, 2)
            s += J[i,j]
        end
        du[i+offset] = s
    end
    offset + size(J, 1)
end

update_tstep!(o::Organ, t) = set_tstep!(o, calc_tstep(o, t))

calc_tstep(o::Organ, t) = calc_tstep(vars(o), t)
calc_tstep(vars, t) = length(vars.rate) != 1 ? floor(Int, ustrip(t)) : 1

calc_envtime(o, t) = t + o.environment_start[]

zero_flux!(o) = begin
    o.J .= zero(eltype(o.J))
    o.J1 .= zero(eltype(o.J1))
end

(o::Plant)(du::AbstractVector{<:Real}, u::AbstractVector{<:Real}, p::AbstractVector{<:Real}, t::Number) = begin
    println("Real/Real")
    u1 = u .* mol
    o1 = reconstruct(o, p)
    du1 = du .* (mol/hr)
    rec = Records(o.params)
    o1(du1, u1, t * (hr), define_organs(o1.params, rec, o1, t))
    du2 = ustrip.(du1)
    if eltype(du2) == eltype(du)
        du .= du2
    end
    du2
end
(o::Plant)(du::AbstractVector{<:Real}, u::AbstractVector{<:Real}, p::Nothing, t::Number) = begin
    t = t * (hr)
    du1 = du .* (mol/hr)
    u1 = u .* (mol)
    o(du1, u1, t, define_organs(o, t))
    du .= ustrip.(du1)
end
(o::Plant)(du, u, p::Nothing, t::Number) = o(du, u, t, define_organs(o, t))

" update vars and flux to record for time t "
define_organs(o, t) = define_organs(o.params, o.shared, o.records, t)
define_organs(params::Tuple{P,Vararg}, shared, records::Tuple{R,Vararg}, t) where {P,R} = begin
    rec = records[1]
    t = calc_tstep(rec.vars, t)
    rec.vars.t[1] = t
    vJ = view(rec.J, :, :, t)
    vJ1 = view(rec.J1, :, :, t)
    J = LArray{Tuple{STATE,TRANS}}(vJ)
    J1 = LArray{Tuple{STATE1,TRANS1}}(vJ1)

    organ = Organ(params[1], shared, rec.vars, J, J1)
    (organ, define_organs(tail(params), shared, tail(records), t)...)
end
define_organs(params::Tuple{}, shared, records::Tuple{}, t) = ()

check_params(o::Tuple) = apply(check_params, o)
check_params(o::Organ) = begin
    y_V_E(o) <= n_N_V(o)/n_N_E(o) || error("y_V_E too high for these valuse of n_N_V and n_N_E ", (y_V_E(o), n_N_V(o), n_N_E(o) ))
    2 <= y_E_EC(o) + y_E_EN(o) || error("y_ECH_NO or y_E_EN too high for C conservation ", (y_E_EC(o), y_E_EN(o)))
    # 2n_N_E(o) <= n_N_EC(o) * y_E_EC(o) + n_N_EN(o) * y_E_EN(o) || error("y_ECH_NO or y_E_EN too high for N conservation ",
    # (2n_N_E(o), n_N_EC(o), y_E_EC(o), n_N_EN(o), y_E_EN(o)))
    2n_N_E(o) <= y_E_EC(o) + y_E_EN(o) || error("y_ECH_NO or y_E_EN too high for N conservation ",
                                                   (n_N_E(o), y_E_EC(o), y_E_EN(o)))
    check_params(production_pars(o), o)

    # These will be required if structures can have different reserve N ratios
    # y_E_ET(o) < n_N_E/n_N_E
    # y_EC_ECT(o) <n_N_C/n_N_C
    # y_EN_ENT(o) < n_N_N/n_N_N
end
check_params(p::Nothing, o::Organ) = nothing
check_params(p::Production, o::Organ) = begin
    p.y_P_V <= p.n_N_P/n_N_V(o) || error("y_P_V too high for N conservation ", (p.y_P_V, p.n_N_P, n_N_V(o)))
    p.j_P_mai <= j_E_mai(o) || error("j_P_mai must be lower than j_E_mai ", (p.j_P_mai, j_E_mai(o)))
end


text:
import JSON

"""
    DeployConfig

Abstract type which new deployment configs should be subtypes of.
"""
abstract type DeployConfig end

"""
    DeployDecision(; kwargs...)

Struct containing information about the decision to deploy or not deploy.

# Arguments

- `all_ok::Bool` - Should documentation be deployed?
- `branch::String` - The branch to which documentation should be pushed
- `is_preview::Bool` - Is this documentation build a pull request?
- `repo::String` - The repo to which documentation should be pushed
- `subfolder::String` - The subfolder to which documentation should be pushed
"""
Base.@kwdef struct DeployDecision
    all_ok::Bool
    branch::String = ""
    is_preview::Bool = false
    repo::String = ""
    subfolder::String = ""
end

"""
    Documenter.documenter_key(cfg::DeployConfig)

Return the Base64-encoded SSH private key for the repository.
Defaults to reading the `DOCUMENTER_KEY` environment variable.

This method must be supported by configs that push with SSH, see
[`Documenter.authentication_method`](@ref).
"""
function documenter_key(::DeployConfig)
    return ENV["DOCUMENTER_KEY"]
end

"""
    Documenter.documenter_key_previews(cfg::DeployConfig)

Return the Base64-encoded SSH private key for the repository.
Uses the `DOCUMENTER_KEY_PREVIEWS` environment variable if it is defined,
otherwise uses the `DOCUMENTER_KEY` environment variable.

This method must be supported by configs that push with SSH, see
[`Documenter.authentication_method`](@ref).
"""
function documenter_key_previews(cfg::DeployConfig)
    return get(ENV, "DOCUMENTER_KEY_PREVIEWS", documenter_key(cfg))
end

"""
    Documenter.deploy_folder(cfg::DeployConfig; repo, devbranch, push_preview, devurl, kwargs...)

Return a `DeployDecision`.
This function is called with the `repo`, `devbranch`, `push_preview` and `devurl`
arguments from [`deploydocs`](@ref).

!!! note
    Implementations of this functions should accept trailing `kwargs...` for
    compatibility with future Documenter releases which may pass additional
    keyword arguments.
"""
function deploy_folder(cfg::DeployConfig; kwargs...)
    @warn "Documenter.deploy_folder(::$(typeof(cfg)); kwargs...) not implemented. Skipping deployment."
    return DeployDecision(; all_ok = false)
end
function deploy_folder(::Nothing; kwargs...)
    @warn "Documenter could not auto-detect the building environment Skipping deployment."
    return DeployDecision(; all_ok = false)
end

@enum AuthenticationMethod SSH HTTPS

"""
    Documenter.authentication_method(::DeployConfig)

Return enum instance `SSH` or `HTTPS` depending on push method to be used.

Configs returning `SSH` should support [`Documenter.documenter_key`](@ref).
Configs returning `HTTPS` should support [`Documenter.authenticated_repo_url`](@ref).
"""
authentication_method(::DeployConfig) = SSH

"""
    Documenter.authenticated_repo_url(cfg::DeployConfig)

Return an authenticated URL to the upstream repository.

This method must be supported by configs that push with HTTPS, see
[`Documenter.authentication_method`](@ref).
"""
function authenticated_repo_url end

post_status(cfg::Union{DeployConfig,Nothing}; kwargs...) = nothing
post_status(; kwargs...) = post_status(auto_detect_deploy_system(); kwargs...)

marker(x) = x ? "✔" : "✘"

#############
# Travis CI #
#############

"""
    Travis <: DeployConfig

Default implementation of `DeployConfig`.

The following environment variables influences the build
when using the `Travis` configuration:

 - `DOCUMENTER_KEY`: must contain the Base64-encoded SSH private key for the repository.
   This variable should be set in the Travis settings for the repository. Make sure this
   variable is marked **NOT** to be displayed in the build log.

 - `TRAVIS_PULL_REQUEST`: must be set to `false`.
   This avoids deployment on pull request builds.

 - `TRAVIS_REPO_SLUG`: must match the value of the `repo` keyword to [`deploydocs`](@ref).

 - `TRAVIS_EVENT_TYPE`: may not be set to `cron`. This avoids re-deployment of existing
   docs on builds that were triggered by a Travis cron job.

 - `TRAVIS_BRANCH`: unless `TRAVIS_TAG` is non-empty, this must have the same value as
   the `devbranch` keyword to [`deploydocs`](@ref). This makes sure that only the
   development branch (commonly, the `master` branch) will deploy the "dev" documentation
   (deployed into a directory specified by the `devurl` keyword to [`deploydocs`](@ref)).

 - `TRAVIS_TAG`: if set, a tagged version deployment is performed instead; the value
   must be a valid version number (i.e. match `Base.VERSION_REGEX`). The documentation for
   a package version tag gets deployed to a directory named after the version number in
   `TRAVIS_TAG` instead.

The `TRAVIS_*` variables are set automatically on Travis. More information on how Travis
sets the `TRAVIS_*` variables can be found in the
[Travis documentation](https://docs.travis-ci.com/user/environment-variables/#default-environment-variables).
"""
struct Travis <: DeployConfig
    travis_branch::String
    travis_pull_request::String
    travis_repo_slug::String
    travis_tag::String
    travis_event_type::String
end
function Travis()
    travis_branch       = get(ENV, "TRAVIS_BRANCH",        "")
    travis_pull_request = get(ENV, "TRAVIS_PULL_REQUEST",  "")
    travis_repo_slug    = get(ENV, "TRAVIS_REPO_SLUG",     "")
    travis_tag          = get(ENV, "TRAVIS_TAG",           "")
    travis_event_type   = get(ENV, "TRAVIS_EVENT_TYPE",    "")
    return Travis(travis_branch, travis_pull_request,
        travis_repo_slug, travis_tag, travis_event_type)
end

# Check criteria for deployment
function deploy_folder(cfg::Travis;
                       repo,
                       repo_previews = repo,
                       branch = "gh-pages",
                       branch_previews = branch,
                       devbranch,
                       push_preview,
                       devurl,
                       kwargs...)
    io = IOBuffer()
    all_ok = true
    ## Determine build type; release, devbranch or preview
    if cfg.travis_pull_request != "false"
        build_type = :preview
    elseif !isempty(cfg.travis_tag)
        build_type = :release
    else
        build_type = :devbranch
    end
    println(io, "Deployment criteria for deploying $(build_type) build from Travis:")
    ## The deploydocs' repo should match TRAVIS_REPO_SLUG
    repo_ok = occursin(cfg.travis_repo_slug, repo)
    all_ok &= repo_ok
    println(io, "- $(marker(repo_ok)) ENV[\"TRAVIS_REPO_SLUG\"]=\"$(cfg.travis_repo_slug)\" occurs in repo=\"$(repo)\"")
    if build_type === :release
        ## Do not deploy for PRs
        pr_ok = cfg.travis_pull_request == "false"
        println(io, "- $(marker(pr_ok)) ENV[\"TRAVIS_PULL_REQUEST\"]=\"$(cfg.travis_pull_request)\" is \"false\"")
        all_ok &= pr_ok
        tag_nobuild = version_tag_strip_build(cfg.travis_tag)
        ## If a tag exist it should be a valid VersionNumber
        tag_ok = tag_nobuild !== nothing
        all_ok &= tag_ok
        println(io, "- $(marker(tag_ok)) ENV[\"TRAVIS_TAG\"] contains a valid VersionNumber")
        deploy_branch = branch
        deploy_repo = repo
        is_preview = false
        ## Deploy to folder according to the tag
        subfolder = tag_nobuild
    elseif build_type === :devbranch
        ## Do not deploy for PRs
        pr_ok = cfg.travis_pull_request == "false"
        println(io, "- $(marker(pr_ok)) ENV[\"TRAVIS_PULL_REQUEST\"]=\"$(cfg.travis_pull_request)\" is \"false\"")
        all_ok &= pr_ok
        ## deploydocs' devbranch should match TRAVIS_BRANCH
        branch_ok = !isempty(cfg.travis_tag) || cfg.travis_branch == devbranch
        all_ok &= branch_ok
        println(io, "- $(marker(branch_ok)) ENV[\"TRAVIS_BRANCH\"] matches devbranch=\"$(devbranch)\"")
        deploy_branch = branch
        deploy_repo = repo
        is_preview = false
        ## Deploy to deploydocs devurl kwarg
        subfolder = devurl
    else # build_type === :preview
        pr_number = tryparse(Int, cfg.travis_pull_request)
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(io, "- $(marker(pr_ok)) ENV[\"TRAVIS_PULL_REQUEST\"]=\"$(cfg.travis_pull_request)\" is a number")
        btype_ok = push_preview
        all_ok &= btype_ok
        println(io, "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`")
        deploy_branch = branch_previews
        deploy_repo = repo_previews
        is_preview = true
        ## deploy to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
    end
    ## DOCUMENTER_KEY should exist (just check here and extract the value later)
    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    all_ok &= key_ok
    println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists")
    ## Cron jobs should not deploy
    type_ok = cfg.travis_event_type != "cron"
    all_ok &= type_ok
    println(io, "- $(marker(type_ok)) ENV[\"TRAVIS_EVENT_TYPE\"]=\"$(cfg.travis_event_type)\" is not \"cron\"")
    print(io, "Deploying: $(marker(all_ok))")
    @info String(take!(io))
    if build_type === :devbranch && !branch_ok && devbranch == "master" && cfg.travis_branch == "main"
        @warn """
        Possible deploydocs() misconfiguration: main vs master
        Documenter's configured primary development branch (`devbranch`) is "master", but the
        current branch (\$TRAVIS_BRANCH) is "main". This can happen because Documenter uses
        GitHub's old default primary branch name as the default value for `devbranch`.

        If your primary development branch is 'main', you must explicitly pass `devbranch = "main"`
        to deploydocs.

        See #1443 for more discussion: https://github.com/JuliaDocs/Documenter.jl/issues/1443
        """
    end
    if all_ok
        return DeployDecision(; all_ok = true,
                                branch = deploy_branch,
                                is_preview = is_preview,
                                repo = deploy_repo,
                                subfolder = subfolder)
    else
        return DeployDecision(; all_ok = false)
    end
end

##################
# GitHub Actions #
##################

"""
    GitHubActions <: DeployConfig

Implementation of `DeployConfig` for deploying from GitHub Actions.

The following environment variables influences the build
when using the `GitHubActions` configuration:

 - `GITHUB_EVENT_NAME`: must be set to `push`.
   This avoids deployment on pull request builds.

 - `GITHUB_REPOSITORY`: must match the value of the `repo` keyword to [`deploydocs`](@ref).

 - `GITHUB_REF`: must match the `devbranch` keyword to [`deploydocs`](@ref), alternatively
   correspond to a git tag.

 - `GITHUB_TOKEN` or `DOCUMENTER_KEY`: used for authentication with GitHub,
   see the manual section for [GitHub Actions](@ref) for more information.

The `GITHUB_*` variables are set automatically on GitHub Actions, see the
[documentation](https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables#default-environment-variables).
"""
struct GitHubActions <: DeployConfig
    github_repository::String
    github_event_name::String
    github_ref::String
end
function GitHubActions()
    github_repository = get(ENV, "GITHUB_REPOSITORY", "") # "JuliaDocs/Documenter.jl"
    github_event_name = get(ENV, "GITHUB_EVENT_NAME", "") # "push", "pull_request" or "cron" (?)
    github_ref        = get(ENV, "GITHUB_REF",        "") # "refs/heads/$(branchname)" for branch, "refs/tags/$(tagname)" for tags
    return GitHubActions(github_repository, github_event_name, github_ref)
end

# Check criteria for deployment
function deploy_folder(cfg::GitHubActions;
                       repo,
                       repo_previews = repo,
                       branch = "gh-pages",
                       branch_previews = branch,
                       devbranch,
                       push_preview,
                       devurl,
                       kwargs...)
    io = IOBuffer()
    all_ok = true
    ## Determine build type
    if cfg.github_event_name == "pull_request"
        build_type = :preview
    elseif occursin(r"^refs\/tags\/(.*)$", cfg.github_ref)
        build_type = :release
    else
        build_type = :devbranch
    end
    println(io, "Deployment criteria for deploying $(build_type) build from GitHub Actions:")
    ## The deploydocs' repo should match GITHUB_REPOSITORY
    repo_ok = occursin(cfg.github_repository, repo)
    all_ok &= repo_ok
    println(io, "- $(marker(repo_ok)) ENV[\"GITHUB_REPOSITORY\"]=\"$(cfg.github_repository)\" occurs in repo=\"$(repo)\"")
    if build_type === :release
        ## Do not deploy for PRs
        event_ok = cfg.github_event_name == "push"
        all_ok &= event_ok
        println(io, "- $(marker(event_ok)) ENV[\"GITHUB_EVENT_NAME\"]=\"$(cfg.github_event_name)\" is \"push\"")
        ## If a tag exist it should be a valid VersionNumber
        m = match(r"^refs\/tags\/(.*)$", cfg.github_ref)
        tag_nobuild = version_tag_strip_build(m.captures[1])
        tag_ok = tag_nobuild !== nothing
        all_ok &= tag_ok
        println(io, "- $(marker(tag_ok)) ENV[\"GITHUB_REF\"]=\"$(cfg.github_ref)\" contains a valid VersionNumber")
        deploy_branch = branch
        deploy_repo = repo
        is_preview = false
        ## Deploy to folder according to the tag
        subfolder = m === nothing ? nothing : tag_nobuild
    elseif build_type === :devbranch
        ## Do not deploy for PRs
        event_ok = cfg.github_event_name == "push"
        all_ok &= event_ok
        println(io, "- $(marker(event_ok)) ENV[\"GITHUB_EVENT_NAME\"]=\"$(cfg.github_event_name)\" is \"push\"")
        ## deploydocs' devbranch should match the current branch
        m = match(r"^refs\/heads\/(.*)$", cfg.github_ref)
        branch_ok = m === nothing ? false : String(m.captures[1]) == devbranch
        all_ok &= branch_ok
        println(io, "- $(marker(branch_ok)) ENV[\"GITHUB_REF\"] matches devbranch=\"$(devbranch)\"")
        deploy_branch = branch
        deploy_repo = repo
        is_preview = false
        ## Deploy to deploydocs devurl kwarg
        subfolder = devurl
    else # build_type === :preview
        m = match(r"refs\/pull\/(\d+)\/merge", cfg.github_ref)
        pr_number = tryparse(Int, m === nothing ? "" : m.captures[1])
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(io, "- $(marker(pr_ok)) ENV[\"GITHUB_REF\"] corresponds to a PR number")
        btype_ok = push_preview
        all_ok &= btype_ok
        println(io, "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`")
        deploy_branch = branch_previews
        deploy_repo = repo_previews
        is_preview = true
        ## deploydocs to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
    end
    ## GITHUB_ACTOR should exist (just check here and extract the value later)
    actor_ok = haskey(ENV, "GITHUB_ACTOR")
    all_ok &= actor_ok
    println(io, "- $(marker(actor_ok)) ENV[\"GITHUB_ACTOR\"] exists")
    ## GITHUB_TOKEN or DOCUMENTER_KEY should exist (just check here and extract the value later)
    token_ok = haskey(ENV, "GITHUB_TOKEN")
    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    auth_ok = token_ok | key_ok
    all_ok &= auth_ok
    if key_ok
        println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists")
    elseif token_ok
        println(io, "- $(marker(token_ok)) ENV[\"GITHUB_TOKEN\"] exists")
    else
        println(io, "- $(marker(auth_ok)) ENV[\"DOCUMENTER_KEY\"] or ENV[\"GITHUB_TOKEN\"] exists")
    end
    print(io, "Deploying: $(marker(all_ok))")
    @info String(take!(io))
    if build_type === :devbranch && !branch_ok && devbranch == "master" && cfg.github_ref == "refs/heads/main"
        @warn """
        Possible deploydocs() misconfiguration: main vs master
        Documenter's configured primary development branch (`devbranch`) is "master", but the
        current branch (from \$GITHUB_REF) is "main". This can happen because Documenter uses
        GitHub's old default primary branch name as the default value for `devbranch`.

        If your primary development branch is 'main', you must explicitly pass `devbranch = "main"`
        to deploydocs.

        See #1443 for more discussion: https://github.com/JuliaDocs/Documenter.jl/issues/1443
        """
    end
    if all_ok
        return DeployDecision(; all_ok = true,
                                branch = deploy_branch,
                                is_preview = is_preview,
                                repo = deploy_repo,
                                subfolder = subfolder)
    else
        return DeployDecision(; all_ok = false)
    end
end

function authentication_method(::GitHubActions)
    if haskey(ENV, "DOCUMENTER_KEY")
        return SSH
    else
        @warn "Currently the GitHub Pages build is not triggered when " *
              "using `GITHUB_TOKEN` for authentication. See issue #1177 " *
              "(https://github.com/JuliaDocs/Documenter.jl/issues/1177) " *
              "for more information."
        return HTTPS
    end
end
function authenticated_repo_url(cfg::GitHubActions)
    return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@github.com/$(cfg.github_repository).git"
end

function version_tag_strip_build(tag)
    m = match(Base.VERSION_REGEX, tag)
    m === nothing && return nothing
    s0 = startswith(tag, 'v') ? "v" : ""
    s1 = m[1] # major
    s2 = m[2] === nothing ? "" : ".$(m[2])" # minor
    s3 = m[3] === nothing ? "" : ".$(m[3])" # patch
    s4 = m[5] === nothing ? "" : m[5] # pre-release (starting with -)
    # m[7] is the build, which we want to discard
    "$s0$s1$s2$s3$s4"
end

function post_status(::GitHubActions; type, repo::String, subfolder=nothing, kwargs...)
    try # make this non-fatal and silent
        # If we got this far it usually means everything is in
        # order so no need to check everything again.
        # In particular this is only called after we have
        # determined to deploy.
        sha = nothing
        if get(ENV, "GITHUB_EVENT_NAME", nothing) == "pull_request"
            event_path = get(ENV, "GITHUB_EVENT_PATH", nothing)
            event_path === nothing && return
            event = JSON.parsefile(event_path)
            if haskey(event, "pull_request") &&
               haskey(event["pull_request"], "head") &&
               haskey(event["pull_request"]["head"], "sha")
               sha = event["pull_request"]["head"]["sha"]
            end
        elseif get(ENV, "GITHUB_EVENT_NAME", nothing) == "push"
            sha = get(ENV, "GITHUB_SHA", nothing)
        end
        sha === nothing && return
        return post_github_status(type, repo, sha, subfolder)
    catch
        @debug "Failed to post status"
    end
end

function post_github_status(type::S, deploydocs_repo::S, sha::S, subfolder=nothing) where S <: String
    try
        Sys.which("curl") === nothing && return
        ## Extract owner and repository name
        m = match(r"^github.com\/(.+?)\/(.+?)(.git)?$", deploydocs_repo)
        m === nothing && return
        owner = String(m.captures[1])
        repo = String(m.captures[2])

        ## Need an access token for this
        auth = get(ENV, "GITHUB_TOKEN", nothing)
        auth === nothing && return
        # construct the curl call
        cmd = `curl -sX POST`
        push!(cmd.exec, "-H", "Authorization: token $(auth)")
        push!(cmd.exec, "-H", "User-Agent: Documenter.jl")
        push!(cmd.exec, "-H", "Content-Type: application/json")
        json = Dict{String,Any}("context" => "documenter/deploy", "state"=>type)
        if type == "pending"
            json["description"] = "Documentation build in progress"
        elseif type == "success"
            json["description"] = "Documentation build succeeded"
            if subfolder !== nothing
                json["target_url"] = "https://$(owner).github.io/$(repo)/$(subfolder)/"
            end
        elseif type == "error"
            json["description"] = "Documentation build errored"
        elseif type == "failure"
            json["description"] = "Documentation build failed"
        else
            error("unsupported type: $type")
        end
        push!(cmd.exec, "-d", sprint(JSON.print, json))
        push!(cmd.exec, "https://api.github.com/repos/$(owner)/$(repo)/statuses/$(sha)")
        # Run the command (silently)
        io = IOBuffer()
        res = run(pipeline(cmd; stdout=io, stderr=devnull))
        @debug "Response of curl POST request" response=String(take!(io))
    catch
        @debug "Failed to post status"
    end
    return nothing
end

##########
# GitLab #
##########

"""
    GitLab <: DeployConfig

GitLab implementation of `DeployConfig`.

The following environment variables influence the build when using the
`GitLab` configuration:

 - `DOCUMENTER_KEY`: must contain the Base64-encoded SSH private key for the
   repository. This variable should be set in the GitLab settings. Make sure this
   variable is marked **NOT** to be displayed in the build log.

 - `CI_COMMIT_BRANCH`: the name of the commit branch.

 - `CI_EXTERNAL_PULL_REQUEST_IID`: Pull Request ID from GitHub if the pipelines
   are for external pull requests.

 - `CI_PROJECT_PATH_SLUG`: The namespace with project name. All letters
   lowercased and non-alphanumeric characters replaced with `-`.

 - `CI_COMMIT_TAG`: The commit tag name. Present only when building tags.

 - `CI_PIPELINE_SOURCE`: Indicates how the pipeline was triggered.

The `CI_*` variables are set automatically on GitLab. More information on how GitLab
sets the `CI_*` variables can be found in the
[GitLab documentation](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html).
"""
struct GitLab <: DeployConfig
    commit_branch::String
    pull_request_iid::String
    repo_slug::String
    commit_tag::String
    pipeline_source::String
end

function GitLab()
    commit_branch = get(ENV, "CI_COMMIT_BRANCH", "")
    pull_request_iid = get(ENV, "CI_EXTERNAL_PULL_REQUEST_IID", "")
    repo_slug = get(ENV, "CI_PROJECT_PATH_SLUG", "")
    commit_tag = get(ENV, "CI_COMMIT_TAG", "")
    pipeline_source = get(ENV, "CI_PIPELINE_SOURCE", "")
    GitLab(commit_branch, pull_request_iid, repo_slug, commit_tag, pipeline_source)
end

function deploy_folder(
    cfg::GitLab;
    repo,
    repo_previews = repo,
    devbranch,
    push_preview,
    devurl,
    branch = "gh-pages",
    branch_previews = branch,
    kwargs...,
)

    marker(x) = x ? "✔" : "✘"

    io = IOBuffer()
    all_ok = true

    println(io, "\nGitLab config:")
    println(io, "  Commit branch: \"", cfg.commit_branch, "\"")
    println(io, "  Pull request IID: \"", cfg.pull_request_iid, "\"")
    println(io, "  Repo slug: \"", cfg.repo_slug, "\"")
    println(io, "  Commit tag: \"", cfg.commit_tag, "\"")
    println(io, "  Pipeline source: \"", cfg.pipeline_source, "\"")

    build_type = if cfg.pull_request_iid != ""
        :preview
    elseif cfg.commit_tag != ""
        :release
    else
        :devbranch
    end

    println(io, "Detected build type: ", build_type)

    if build_type == :release
        tag_nobuild = version_tag_strip_build(cfg.commit_tag)
        ## If a tag exist it should be a valid VersionNumber
        tag_ok = tag_nobuild !== nothing

        println(
            io,
            "- $(marker(tag_ok)) ENV[\"CI_COMMIT_TAG\"] contains a valid VersionNumber",
        )
        all_ok &= tag_ok

        is_preview = false
        subfolder = tag_nobuild
        deploy_branch = branch
        deploy_repo = repo
    elseif build_type == :preview
        pr_number = tryparse(Int, cfg.pull_request_iid)
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(
            io,
            "- $(marker(pr_ok)) ENV[\"CI_EXTERNAL_PULL_REQUEST_IID\"]=\"$(cfg.pull_request_iid)\" is a number",
        )
        btype_ok = push_preview
        all_ok &= btype_ok
        is_preview = true
        println(
            io,
            "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`",
        )
        ## deploy to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
        deploy_branch = branch_previews
        deploy_repo = repo_previews
    else
        branch_ok = !isempty(cfg.commit_tag) || cfg.commit_branch == devbranch
        all_ok &= branch_ok
        println(
            io,
            "- $(marker(branch_ok)) ENV[\"CI_COMMIT_BRANCH\"] matches devbranch=\"$(devbranch)\"",
        )
        is_preview = false
        subfolder = devurl
        deploy_branch = branch
        deploy_repo = repo
    end

    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists")
    all_ok &= key_ok

    print(io, "Deploying to folder $(repr(subfolder)): $(marker(all_ok))")
    @info String(take!(io))

    if all_ok
        return DeployDecision(;
            all_ok = true,
            branch = deploy_branch,
            repo = deploy_repo,
            subfolder = subfolder,
            is_preview = is_preview,
        )
    else
        return DeployDecision(; all_ok = false)
    end
end

authentication_method(::GitLab) = Documenter.SSH

documenter_key(::GitLab) = ENV["DOCUMENTER_KEY"]

#############
# Buildkite #
#############

"""
    Buildkite <: DeployConfig

Buildkite implementation of `DeployConfig`.

The following environment variables influence the build when using the
`Buildkite` configuration:

 - `DOCUMENTER_KEY`: must contain the Base64-encoded SSH private key for the
   repository. This variable should be somehow set in the CI environment, e.g.,
   provisioned by an agent environment plugin.

 - `BUILDKITE_BRANCH`: the name of the commit branch.

 - `BUILDKITE_PULL_REQUEST`: Pull Request ID from GitHub if the pipelines
   are for external pull requests.

 - `BUILDKITE_TAG`: The commit tag name. Present only when building tags.

The `BUILDKITE_*` variables are set automatically on GitLab. More information on how
Buildkite sets the `BUILDKITE_*` variables can be found in the
[Buildkite documentation](https://buildkite.com/docs/pipelines/environment-variables).
"""
struct Buildkite <: DeployConfig
    commit_branch::String
    pull_request::String
    commit_tag::String
end

function Buildkite()
    commit_branch = get(ENV, "BUILDKITE_BRANCH", "")
    pull_request = get(ENV, "BUILDKITE_PULL_REQUEST", "false")
    commit_tag = get(ENV, "BUILDKITE_TAG", "")
    Buildkite(commit_branch, pull_request, commit_tag)
end

function deploy_folder(
    cfg::Buildkite;
    repo,
    repo_previews = repo,
    devbranch,
    push_preview,
    devurl,
    branch = "gh-pages",
    branch_previews = branch,
    kwargs...,
)

    marker(x) = x ? "✔" : "✘"

    io = IOBuffer()
    all_ok = true

    println(io, "\nBuildkite config:")
    println(io, "  Commit branch: \"", cfg.commit_branch, "\"")
    println(io, "  Pull request: \"", cfg.pull_request, "\"")
    println(io, "  Commit tag: \"", cfg.commit_tag, "\"")

    build_type = if cfg.pull_request != "false"
        :preview
    elseif cfg.commit_tag != ""
        :release
    else
        :devbranch
    end

    println(io, "Detected build type: ", build_type)

    if build_type == :release
        tag_nobuild = version_tag_strip_build(cfg.commit_tag)
        ## If a tag exist it should be a valid VersionNumber
        tag_ok = tag_nobuild !== nothing

        println(
            io,
            "- $(marker(tag_ok)) ENV[\"BUILDKITE_TAG\"] contains a valid VersionNumber",
        )
        all_ok &= tag_ok

        is_preview = false
        subfolder = tag_nobuild
        deploy_branch = branch
        deploy_repo = repo
    elseif build_type == :preview
        pr_number = tryparse(Int, cfg.pull_request)
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(
            io,
            "- $(marker(pr_ok)) ENV[\"BUILDKITE_PULL_REQUEST\"]=\"$(cfg.pull_request)\" is a number",
        )
        btype_ok = push_preview
        all_ok &= btype_ok
        is_preview = true
        println(
            io,
            "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`",
        )
        ## deploy to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
        deploy_branch = branch_previews
        deploy_repo = repo_previews
    else
        branch_ok = !isempty(cfg.commit_tag) || cfg.commit_branch == devbranch
        all_ok &= branch_ok
        println(
            io,
            "- $(marker(branch_ok)) ENV[\"BUILDKITE_BRANCH\"] matches devbranch=\"$(devbranch)\"",
        )
        is_preview = false
        subfolder = devurl
        deploy_branch = branch
        deploy_repo = repo
    end

    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists")
    all_ok &= key_ok

    print(io, "Deploying to folder $(repr(subfolder)): $(marker(all_ok))")
    @info String(take!(io))
    if build_type === :devbranch && !branch_ok && devbranch == "master" && cfg.commit_branch == "main"
        @warn """
        Possible deploydocs() misconfiguration: main vs master
        Documenter's configured primary development branch (`devbranch`) is "master", but the
        current branch (\$BUILDKITE_BRANCH) is "main". This can happen because Documenter uses
        GitHub's old default primary branch name as the default value for `devbranch`.

        If your primary development branch is 'main', you must explicitly pass `devbranch = "main"`
        to deploydocs.

        See #1443 for more discussion: https://github.com/JuliaDocs/Documenter.jl/issues/1443
        """
    end

    if all_ok
        return DeployDecision(;
            all_ok = true,
            branch = deploy_branch,
            repo = deploy_repo,
            subfolder = subfolder,
            is_preview = is_preview,
        )
    else
        return DeployDecision(; all_ok = false)
    end
end

authentication_method(::Buildkite) = Documenter.SSH

documenter_key(::Buildkite) = ENV["DOCUMENTER_KEY"]

##################
# Auto-detection #
##################
function auto_detect_deploy_system()
    if haskey(ENV, "TRAVIS_REPO_SLUG")
        return Travis()
    elseif haskey(ENV, "GITHUB_REPOSITORY")
        return GitHubActions()
    elseif haskey(ENV, "GITLAB_CI")
        return GitLab()
    elseif haskey(ENV, "BUILDKITE")
        return Buildkite()
    else
        return nothing
    end
end

ext: jl


text:
using RoME
using DistributedFactorGraphs
using Test

@testset "Test Hexagonal specifically with LightGraphsDFG..." begin

# start with an empty factor graph object
fg = LightDFG{SolverParams}(solverParams=SolverParams())

# Add the first pose :x0
x0 = addVariable!(fg, :x0, Pose2)

# Add at a fixed location PriorPose2 to pin :x0 to a starting location (10,10, pi/4)
prior = addFactor!(fg, [:x0], PriorPose2( MvNormal([10; 10; 1.0/8.0], Matrix(Diagonal([0.1;0.1;0.05].^2))) ) )

# Drive around in a hexagon in the cloud
for i in 0:5
    psym = Symbol("x$i")
    nsym = Symbol("x$(i+1)")
    addVariable!(fg, nsym, Pose2)
    pp = Pose2Pose2(MvNormal([10.0;0;pi/3], Matrix(Diagonal([0.1;0.1;0.1].^2))))
    addFactor!(fg, [psym;nsym], pp )
end

# Alrighty! At this point, we should be able to solve locally...
# perform inference, and remember first runs are slower owing to Julia's just-in-time compiling
# Can do with graph too!

tree = solveTree!(fg)

@test 80 < sum(-3.0+10 .< getPoints(getKDE(fg, :x0))[1,:] .< 3.0+10)
@test 80 < sum(-3.0+10 .< getPoints(getKDE(fg, :x0))[2,:] .< 3.0+10)
@test 80 < sum(-0.3 .< getPoints(getKDE(fg, :x0))[3,:] .< 0.3)

@test 80 < sum(7.0+10 .< getPoints(getKDE(fg, :x1))[1,:] .< 13.0+10)
@test 80 < sum(-3.0+10 .< getPoints(getKDE(fg, :x1))[2,:] .< 3.0+10)
@test 80 < sum(0.7 .< getPoints(getKDE(fg, :x1))[3,:] .< 1.3)

@test 80 < sum(12.0+10 .< getPoints(getKDE(fg, :x2))[1,:] .< 18.0+10)
@test 80 < sum(6.0+10 .< getPoints(getKDE(fg, :x2))[2,:] .< 11.0+10)
@test 80 < sum(1.8 .< getPoints(getKDE(fg, :x2))[3,:] .< 2.4)

@test 80 < sum(7.0+10 .< getPoints(getKDE(fg, :x3))[1,:] .< 13.0+10)
@test 80 < sum(15.0+10 .< getPoints(getKDE(fg, :x3))[2,:] .< 20.0+10)
# @test 80 < sum(-0.3 .< getPoints(getKDE(fg, :x3))[3,:] .< 0.3)

@test 80 < sum(-4.0+10 .< getPoints(getKDE(fg, :x4))[1,:] .< 4.0+10)
@test 80 < sum(15.0+10 .< getPoints(getKDE(fg, :x4))[2,:] .< 20.0+10)
@test 80 < sum(-2.4 .< getPoints(getKDE(fg, :x4))[3,:] .< -1.8)

@test 80 < sum(-8.0+10 .< getPoints(getKDE(fg, :x5))[1,:] .< -2.0+10)
@test 80 < sum(6.0+10 .< getPoints(getKDE(fg, :x5))[2,:] .< 11.0+10)
@test 80 < sum(-1.3 .< getPoints(getKDE(fg, :x5))[3,:] .< -0.7)

@test 80 < sum(-3.0+10 .< getPoints(getKDE(fg, :x6))[1,:] .< 3.0+10)
@test 80 < sum(-3.0+10 .< getPoints(getKDE(fg, :x6))[2,:] .< 3.0+10)
@test 80 < sum(-0.3 .< getPoints(getKDE(fg, :x6))[3,:] .< 0.3)

# @test 80 < sum(17.0+10 .< getPoints(getKDE(fg, :l1))[1,:] .< 23.0+10)
# @test 80 < sum(-5.0+10 .< getPoints(getKDE(fg, :l1))[2,:] .< 5.0+10)

end


text:
using Test
import DifferentiableBackwardEuler
import DPFEHM
import NonlinearEquations
import PyPlot
import SparseArrays
import Zygote

#solve u_t + div(v * u) - div(D * grad(u)) - Q = 0 on an unstructured mesh using an upwind discretization of the velocity term (or downwind if use_upwind=false)
@NonlinearEquations.equations exclude=(areasoverlengths, volumes, dirichletnodes, coords) function transport(u, vxs, vys, vzs, Ds, neighbors, areasoverlengths, dirichletnodes, dirichletus, Qs, volumes, coords; use_upwind=true)
	@assert size(coords, 1) == 3#it is assumed that this is a 3d problem
	NonlinearEquations.setnumequations(length(u))
	for i = 1:length(u)
		if i in dirichletnodes
			NonlinearEquations.addterm(i, u[i] - dirichletus[i])
		else
			NonlinearEquations.addterm(i, Qs[i] / (volumes[i]))
		end
	end
	delta = zeros(eltype(coords), size(coords, 1))
	for (i, (node_a, node_b)) in enumerate(neighbors)
		for (node1, node2) in [(node_a, node_b), (node_b, node_a)]
			#add the advection term
			delta .= coords[:, node1] .- coords[:, node2]
			is_upwind = ((vxs[i] * delta[1] + vys[i] * delta[2] + vzs[i] * delta[3]) > 0)
			if (is_upwind && use_upwind) || (!is_upwind && !use_upwind)#!use_upwind basically means use_downwind and !is_upwind basically means is_downwind
				if !(node1 in dirichletnodes) && !(node2 in dirichletnodes)
					NonlinearEquations.addterm(node1, -(vxs[i] * delta[1] + vys[i] * delta[2] + vzs[i] * delta[3]) * (u[node1] - u[node2]) * areasoverlengths[i] / (volumes[node1]))
				elseif !(node1 in dirichletnodes) && node2 in dirichletnodes
					NonlinearEquations.addterm(node1, -(vxs[i] * delta[1] + vys[i] * delta[2] + vzs[i] * delta[3]) * (u[node1] - dirichletus[node2]) * areasoverlengths[i] / (volumes[node1]))
				end
			end
			#add the diffusion term
			if !(node1 in dirichletnodes) && !(node2 in dirichletnodes)
				NonlinearEquations.addterm(node1, Ds[i] * (u[node2] - u[node1]) * areasoverlengths[i] / (volumes[node1]))
			elseif !(node1 in dirichletnodes) && node2 in dirichletnodes
				NonlinearEquations.addterm(node1, Ds[i] * (dirichletus[node2] - u[node1]) * areasoverlengths[i] / (volumes[node1]))
			end
		end
	end
end

mins = [0, 0]; maxs = [3, 2]#size of the domain, in meters
ns = [30, 20]#number of nodes on the grid
coords, neighbors, areasoverlengths, volumes = DPFEHM.regulargrid2d(mins, maxs, ns, 1.0)#build the grid
coords = vcat(coords, zeros(size(coords, 2))')#turn it into a 3d coords

c = 1.0
tspan = [0.0, 1.0 / abs(c)]
u0 = map(i->coords[1, i] < 1 / 2  && coords[1, i] > 0 && coords[2, i] > 0.25 && coords[2, i] < 0.75 ? 1.0 : 0.0, 1:size(coords, 2)) + map(i->exp(-((coords[1, i] - 1.0) ^ 2 + (coords[2, i] - 1.0) ^ 2) * 64), 1:size(coords, 2))
vxs = map(neighbor->c, neighbors)
vys = map(neighbor->c * 0.5, neighbors)
vzs = zeros(length(neighbors))
Ds = 1e-2 * ones(length(neighbors))
Qs = zeros(size(coords, 2))
dirichletnodes = [collect(1:ns[2]); collect(size(coords, 2) - ns[2] + 1:size(coords, 2))]
dirichletus = zeros(size(coords, 2))
f(u, p, t) = transport_residuals(u, p, vys, vzs, Ds, neighbors, areasoverlengths, dirichletnodes, dirichletus, Qs, volumes, coords)
f_u(u, p, t) = transport_u(u, p, vys, vzs, Ds, neighbors, areasoverlengths, dirichletnodes, dirichletus, Qs, volumes, coords)
f_p(u, p, t) = transport_vxs(u, p, vys, vzs, Ds, neighbors, areasoverlengths, dirichletnodes, dirichletus, Qs, volumes, coords)
f_t(u, p, t) = zeros(length(u))
function solveforu(vxs)
	DifferentiableBackwardEuler.steps(u0, f, f_u, f_p, f_t, vxs, tspan[1], tspan[2]; abstol=1e-4, reltol=1e-4)
end
@time u_implicit = solveforu(vxs)

fig, axs = PyPlot.subplots(2, 1; figsize=(8, 8))
img = axs[1].imshow(reshape(u0, reverse(ns)...), extent=[mins[1], maxs[1], mins[2], maxs[2]], origin="lower")
fig.colorbar(img, ax=axs[1])
img = axs[2].imshow(reshape(u_implicit[:, end], reverse(ns)...), extent=[mins[1], maxs[1], mins[2], maxs[2]], origin="lower")
fig.colorbar(img, ax=axs[2])
fig.tight_layout()
display(fig)
PyPlot.println()
PyPlot.close(fig)

_, gradient_node = findmin(map(i->sum((coords[:, i] .- [1.5, 1.0, 0.0]) .^ 2), 1:size(coords, 2)))
g = x->solveforu(x)[gradient_node, end]
@time gradg = Zygote.gradient(g, vxs)[1]
function checkgradientquickly(f, x0, gradf, n; delta::Float64=1e-8, kwargs...)
	indicestocheck = sort(collect(1:length(x0)), by=i->abs(gradf[i]), rev=true)[1:n]
	f0 = f(x0)
	for i in indicestocheck
		x = copy(x0)
		x[i] += delta
		fval = f(x)
		grad_f_i = (fval - f0) / delta
		@test isapprox(gradf[i], grad_f_i; kwargs...)
	end
end
checkgradientquickly(g, vxs, gradg, 3; atol=1e-4, rtol=1e-3)
nothing


text:
using Sonums
using SoftPosit
using Test

data = randn(100_000)
trainSonum8(abs.(data))
fillSonum8Tables()

@testset "Arithmetics" begin
    f0,f1,f2,f3,f4 = -1.0,-0.5,0.25,1.0,1.5

    # basic arithmetic operations
    @testset for T in (Sonum8,) # Sonum16)
        @test T(f3) - T(f4) == -(T(f4) - T(f3))         # anti symmetry of subtraction
        @test zero(T) == T(f0) + T(f3)                  #
        @test T(f1) + T(f2) == T(f2) + T(f1)            # symmetry of addition
        @test T(Inf) == one(T)/zero(T)                  # infinity case
        @test zero(T) == T(0.0)*one(T)                  # zero element
        @test floatmax(T) == floatmax(T)*floatmax(T)    # no overflow
        @test zero(T) == floatmin(T)*floatmin(T)        # but underflow
        @test T(f0)*T(f1) == -T(f0) * -T(f1)            # symmetry of multiplication
    end
end

@testset "Conversions" begin

    @testset for T in (Sonum8,) # Sonum16)
        @test one(T) == T(1.0)
        @test zero(T) == T(0.0)
        @test -one(T) == T(-1.0)

        for x in [-Inf,-1.0,-0.25,0.0,0.25,1.0,Inf]
            @test T(x) == T(Float64(T(x)))
        end

        @test floatmin(T) == T(Float64(floatmin(T)))
        @test floatmax(T) == T(Float64(floatmax(T)))

    end
end

@testset "CompareToPosit8" begin

    p8 = Float64.(Posit8.(UInt8.(collect(1:127))))
    setSonum8(p8)
    setUnderflow8(false)
    fillSonum8Tables()

    n = 256

    @testset "Addition" begin
        for i in 0:n-1
            for j in 0:n-1
                p = UInt8(Posit8(UInt8(i))+Posit8(UInt8(j)))
                s = UInt8(Sonum8(UInt8(i))+Sonum8(UInt8(j)))
                @test p == s
            end
        end
    end

    @testset "Subtraction" begin
        for i in 0:n-1
            for j in 0:n-1
                p = UInt8(Posit8(UInt8(i))-Posit8(UInt8(j)))
                s = UInt8(Sonum8(UInt8(i))-Sonum8(UInt8(j)))
                @test p == s
            end
        end
    end

    @testset "Multiplication" begin
        for i in 0:n-1
            for j in 0:n-1
                p = UInt8(Posit8(UInt8(i))*Posit8(UInt8(j)))
                s = UInt8(Sonum8(UInt8(i))*Sonum8(UInt8(j)))
                @test p == s
            end
        end
    end

    @testset "Division" begin
        for i in 0:n-1
            for j in 0:n-1
                p = UInt8(Posit8(UInt8(i))/Posit8(UInt8(j)))
                s = UInt8(Sonum8(UInt8(i))/Sonum8(UInt8(j)))
                @test p == s
            end
        end
    end

    @testset "Sqrt" begin
        for i in 0:n-1
            p = UInt8(sqrt(Posit8(UInt8(i))))
            s = UInt8(sqrt(Sonum8(UInt8(i))))
            @test p == s
        end
    end

end


text:
module Getopt

export getopt

struct GetoptIter
	args::Array{String}
	ostr::String
	longopts::Array{String}
end

function Base.iterate(g::GetoptIter, (pos, ind) = (firstindex(g.args), 1))
	if g.ostr[1] != '+' # allow options to appear after main arguments
		while ind <= lastindex(g.args) && (g.args[ind][1] != '-' || g.args[ind] == "-")
			ind += 1
		end
	end
	if ind > lastindex(g.args) || g.args[ind][1] != '-' || g.args[ind] == "-" return nothing end
	if length(g.args[ind]) >= 2 && g.args[ind][1] == '-' && g.args[ind][2] == '-'
		if length(g.args[ind]) == 2 # actually, Julia will always filter out "--" in ARGS. Ugh!
			deleteat!(g.args, ind)
			return nothing
		end
		optopt, optarg, pos = "?", "", 0
		if length(g.longopts) > 0
			eqpos = findfirst(isequal('='), g.args[ind])
			a = eqpos == nothing ? g.args[ind][3:end] : g.args[ind][3:eqpos-1]
			n_matches, match = 0, ""
			for l in g.longopts
				r = findfirst(a, l)
				if r != nothing && r[1] == 1
					n_matches += 1
					match = l
				end
			end
			if n_matches == 1
				optopt = string("--", match[end] == '=' ? match[1:end-1] : match);
				if eqpos != nothing
					optarg = g.args[ind][eqpos+1:end]
				elseif match[end] == '=' && ind + 1 <= lastindex(g.args)
					deleteat!(g.args, ind)
					optarg = g.args[ind]
				end
			end
		end
	else
		if pos == 1 pos = 2 end
		optopt, optarg = g.args[ind][pos], ""
		pos += 1
		i = findfirst(isequal(optopt), g.ostr)
		if i == nothing # unknown option
			optopt = '?'
		elseif i < length(g.ostr) && g.ostr[i + 1] == ':' # require argument
			if pos <= length(g.args[ind])
				optarg = g.args[ind][pos:end]
			elseif ind + 1 <= lastindex(g.args)
				deleteat!(g.args, ind)
				optarg = g.args[ind]
			end
			pos = 0
		end
		optopt = optopt == '?' ? "?" : string('-', optopt)
	end
	if pos == 0 || pos > length(g.args[ind])
		deleteat!(g.args, ind) # FIXME: can be slow when ostr[1] == '-'
		pos = 1
	end
	return ((optopt, optarg), (pos, ind))
end

"""
    getopt(args::Array{String}, ostr::String, longopts::Array{String}=String[])

Iterate through command line options with a getopt-like interface and remove
options from `args`.

`args` is typically `ARGS`. By default, options are allowed to occur after
non-option arguments. If `ostr[1]=='+'`, the default behavior is disabled.

# Examples
```julia
for (opt, arg) in getopt(ARGS, "xy:", ["foo", "bar="])
	@show (opt, arg)
end
@show ARGS # only non-option arguments remain
```
"""
getopt(args::Array{String}, ostr::String, longopts::Array{String} = String[]) = GetoptIter(args, ostr, longopts)

end


text:
struct DummyTemplate <: Template end
struct DummyModel <: Model end

@testset "traits.jl" begin

    estimates = (PointEstimate, DistributionEstimate)
    outputs = (SingleOutput, MultiOutput)

    @testset "$est, $out" for (est, out) in Iterators.product(estimates, outputs)

        @testset "Errors if traits are not defined" begin
            @test_throws MethodError estimate_type(DummyTemplate)
            @test_throws MethodError output_type(DummyTemplate)

            @test_throws MethodError estimate_type(DummyModel)
            @test_throws MethodError output_type(DummyModel)
        end

        @testset "Traits are defined" begin
            estimate_type(m::Type{<:DummyTemplate}) = est
            estimate_type(m::Type{<:DummyModel}) = est

            output_type(m::Type{<:DummyTemplate}) = out
            output_type(m::Type{<:DummyModel}) = out

            @test estimate_type(DummyTemplate) == estimate_type(DummyModel) == est
            @test output_type(DummyTemplate) == output_type(DummyModel) == out
        end

    end

    @testset "PredictInputTrait" begin

        @testset "Default" begin
            @test predict_input_type(DummyTemplate) == predict_input_type(DummyModel) == PointPredictInput
        end

        @testset "PointOrDistributionPredictInput is defined" begin
            predict_input_type(m::Type{<:DummyTemplate}) = PointOrDistributionPredictInput
            predict_input_type(m::Type{<:DummyModel}) = PointOrDistributionPredictInput

            @test predict_input_type(DummyTemplate) ==
                predict_input_type(DummyModel) ==
                PointOrDistributionPredictInput
        end
    end
end


text:
module TestManualMemory

using ManualMemory
import ManualMemory; const MM = ManualMemory
using Base.Test

# basic sanity checks

struct Foo
    x::Int64
    y::Float32 # tests conversion and alignment
end

foo = Manual{Foo}(Libc.malloc(sizeof(Foo))) # display should work in 0.7 TODO fix for 0.6?
@v foo.x = 1
@test @v(foo.x) == 1
@v foo.y = 2.5
@test (@v foo.y) == 2.5
@test (@v foo) == Foo(1,2.5)
# tests interior pointers
@test (@a foo) == foo
@test (@a foo.y).ptr == foo.ptr + sizeof(Int64)

# tests ambiguous syntax
@test_throws ErrorException eval(:(@v foo.y == 2.5))
@test_throws ErrorException eval(:(@a foo.y == 2.5))

mpv = MM.malloc(ManualVector{Foo}, 3)
pv = @v mpv
pv[2] = Foo(2, 2.2)
@test pv[2] == Foo(2, 2.2)
@test (@v pv[2]) == Foo(2, 2.2)
@test length(pv) == 3
pv[1] = Foo(1, 1.1)
pv[3] = Foo(3, 3.3)
# tests iteration
@test collect(pv) == [Foo(1,1.1), Foo(2,2.2), Foo(3,3.3)]
# tests interior pointers
@test (@a pv[2]).ptr == pv.ptr.ptr + sizeof(Foo)

mpbv = MM.malloc(ManualBitVector, 3)
pbv = @v mpbv
pbv[2] = true
@test pbv[2] == true
@test (@v pv[2]) == Foo(2, 2.2)
pbv[1] = false
pbv[3] = false
# tests iteration
@test collect(pbv) == [false, true, false]
fill!(pbv, false)
@test collect(pbv) == [false, false, false]
# tests interior pointers
pbv2 = @a pbv[2]
@test (@v pbv2) == false
@v pbv2 = true
@test (@v pbv2) == true
@test pbv[2] == true

# strings and unicode

s = "普通话/普通話"
mp = MM.malloc(ManualString, s)
p = @v mp
@test p == s
@test repr(p) == repr(s)
@test collect(p) == collect(s)
@test search(p, "通") == search(s, "通")
@test rsearch(p, "通") == rsearch(s, "通")
@test reverse(p) == reverse(s)

# test right-to-left

s = "سلام"
mp = MM.malloc(ManualString, s)
p = @v mp
@test p == s
@test repr(p) == repr(s)
@test collect(p) == collect(s)
@test search(p, "ا") == search(s, "ا")
@test rsearch(p, "ا") == rsearch(s, "ا")
@test reverse(p) == reverse(s)

# test string conversions

@test isbits(p)
@test reverse(p) isa RevString{ManualString}
@test String(p) isa String
@test String(p) == s
@test string(p) isa String

# test string slices

s = "aye bee sea"
mp = MM.malloc(ManualString, s)
p = @v mp
@test p[5:7] isa ManualString
@test p[5:7] == "bee"

# sketch of paged pmas

struct PackedMemoryArray{K,V}
     keys::ManualVector{K}
     values::ManualVector{V}
     mask::ManualBitVector
     count::Int
     #...other stuff
end

function MM.alloc_size(::Type{PackedMemoryArray{K,V}}, length::Int64) where {K,V}
    T = PackedMemoryArray{K,V}
    +(MM.alloc_size(fieldtype(T, :keys), length),
      MM.alloc_size(fieldtype(T, :values), length),
      MM.alloc_size(fieldtype(T, :mask), length))
  end

function MM.init(ptr::Ptr{Void}, pma::Manual{PackedMemoryArray{K,V}}, length::Int64) where {K,V}
    ptr = MM.init(ptr, (@a pma.keys), length)
    ptr = MM.init(ptr, (@a pma.values), length)
    ptr = MM.init(ptr, (@a pma.mask), length)
    fill!((@v pma.mask), false)
    @v pma.count = 0
    ptr
end

pma = MM.malloc(PackedMemoryArray{Int64, Float32}, 3)
@test (@v pma.count) == 0
@test (@v pma.keys.length) == 3
# tests fill!
@test !any(@v pma.mask)
# tests pointer <-> offset conversion
@test unsafe_load(convert(Ptr{UInt64}, pma.ptr), 1) == sizeof(PackedMemoryArray{Int64, Float32})
# tests nested interior pointers
pma2 = @a pma.mask[2]
@test (@v pma2) == false
@v pma2 = true
@test (@v pma2) == true
@test (@v pma.mask[2]) == true

# test api works ok with varying sizes

struct Quux
    x::ManualVector{Int}
    y::Float64
end

struct Bar
    a::Int
    b::ManualBitVector
    c::Bool
    d::ManualVector{Float64}
    e::Manual{Quux}
end

function MM.alloc_size(::Type{Quux}, x_len::Int64, y::Float64)
    T = Quux
    +(MM.alloc_size(fieldtype(T, :x), x_len))
end

function MM.alloc_size(::Type{Bar}, b_len::Int64, c::Bool, d_len::Int64, x_len::Int64, y::Float64)
    T = Bar
    +(MM.alloc_size(fieldtype(T, :b), b_len),
      MM.alloc_size(fieldtype(T, :d), d_len),
      MM.alloc_size(fieldtype(T, :e), x_len, y))
end

function MM.init(ptr::Ptr{Void}, quux::Manual{Quux}, x_len::Int64, y::Float64)
    ptr = MM.init(ptr, (@a quux.x), x_len)
    @v quux.y = y
    ptr
end

function MM.init(ptr::Ptr{Void}, bar::Manual{Bar}, b_len::Int64, c::Bool, d_len::Int64, x_len::Int64, y::Float64)
    ptr = MM.init(ptr, (@a bar.b), b_len)
    ptr = MM.init(ptr, (@a bar.d), d_len)
    ptr = MM.init(ptr, (@a bar.e), x_len, y)
    @v bar.c = c
    ptr
end

bar = MM.malloc(Bar, 10, false, 20, 15, 1.5)
quux = @v bar.e

@test length(@v bar.b) == 10
@test (@v bar.c) == false
@test length(@v bar.d) == 20
@test length(@v quux.x) == 15
@test (@v quux.y) == 1.5

end


text:
# Hodkin-Huxley spiking layer & net

### HH FUNCTIONS

# helper function: traps for 0 in denominator of rate eqns.
vtrap = (x,y) -> (abs(x/y)<1e-6) ? y*(1 - x/y/2) : x/(exp(x/y) - 1);
# Na channel activation
αM = v-> .1 * vtrap(-(v+40),10)
βM = v-> 4exp(-(v+65)/18)
# Na channel inactivation
αH = v-> .07exp(-(v+65)/20)
βH = v-> 1/(exp(-(v+35)/10)+1)
# K channel activation
αN = v-> .01vtrap(-(v+55),10)
βN = v-> 0.125*exp(-(v+65)/80);
inf = (α,β) -> α/(α+β) # for computing minf, hinf, ninf
Minf = v-> inf(αM(v),βM(v))
Hinf = v-> inf(αH(v),βH(v))
Ninf = v-> inf(αN(v),βN(v))

# Layer parameters
const Cm = 10 # Capacitative density [μF/cm^2]
const gNa = 120 # maximum Na channel conductance
const gK = 36
const gL = 1
const ENa = 115
const EK = -12
const ELeak = 0
const v_r = -65

const e_rev = 0 # reversal potential for AMPA synapses (mV)
const i_rev = -80 # reversal potential for GABA synapses (mV)
const abs_refr=5 # ms
const v_th=-20

function HHNeuron()
    HHNeuron(Minf(v_r), Hinf(v_r), Ninf(v_r), v_r, -Inf, false)
end

function update!(neuron::HHNeuron, Ge_syn::Float64, Gi_syn::Float64, Iinj::Float64, t::Float64)
	m = neuron.m
	h = neuron.h
	n = neuron.n
	v = neuron.v
	Isyn = Ge_syn.*(e_rev-v)-Gi_syn.*(i_rev-v) # total synaptic current
	vdot =  (gNa*m^3*h*(ENa-(v+65)) + gK*n^4*(EK-(v+65)) + gL*(ELeak-(v+65)) + Isyn + Iinj)/Cm

    neuron.v += dt*vdot
    neuron.m += dt*(αM(v)*(1-m) - βM(v)*m);
    neuron.h += dt*(αH(v)*(1-h) - βH(v)*h);
    neuron.n += dt*(αN(v)*(1-n) - βN(v)*n);

    neuron.didSpike = (v > v_th) & (t > neuron.lastSpike+abs_refr)
    if neuron.didSpike
    	neuron.lastSpike = t
    end
end

function reset!(neuron::HHNeuron)
    neuron.m = Minf(v_r)
    neuron.h = Hinf(v_r)
    neuron.n = Ninf(v_r)
    neuron.v = v_r
    neuron.lastSpike = -Inf
    neuron.didSpike = false
end


text:
#
function dictweightcoordinate(D::AbstractDict{Int, Professional})
    return  function weightcoordinate(s,t)
        return distance(D[s], D[t])/1000
    end
end

mutable struct FinderGraph
    graph::DictGraph
    pmap::AbstractDict{Int, Professional}
    user::Pair{Int, Professional}

    #Constructor
    function FinderGraph(file::String;
                        user = Professional("usuario", LLA(-15.836073, -47.912019), "Programador",true),
                        radius = 100.0)
        #regex for indentify the pattern of the input in each line
        r = r"^(.*?);(.*?);(.*?);([Tt][Rr][Uu][Ee]|[Ff][aA][Ll][Ss][Ee])$";
        #dict of all workers availabel
        D = SortedDict{Int, Professional}(0 => user)
        #opening the file
        open(file) do data
            #read each line of the file
            for (index, line) in enumerate(eachline(data))
                #check if the line matches with r
                occursin(r, line) || error("invalid input at line $index \n-> $line");

                #split the line in ;
                cut = split(line, ";");

                #if the worker isnt availabel, jump him
                lowercase(string(cut[4])) == "false" && continue

                #read the coordinate and parse it to Float64
                lat, lon = map(x -> parse(Float64, x), split(cut[2], ","));

                #compute the distance between the user and the worker.
                #If the distance is greater than radius, jump him
                distance(LLA(lat, lon), user.coord)/1000 > radius && continue

                #parse the availabel string to bool
                avlb = parse(Bool, lowercase(string(cut[4])))

                #put that worker on the dict D
                push!(D, index => Professional(string(cut[1]), LLA(lat, lon), string(cut[2]), avlb))
            end
        end

        G = buildconnectedgraph(collect(keys(workers)); f = dictweightcoordinate(workers))
        #return a new object Finder Graph
        new(G, D, 0 => user)
    end

    function FinderGraph(D::AbstractDict{Int, Professional})
        G = buildconnectedgraph(collect(keys(D)); f = dictweightcoordinate(D))
        new(G, D, 0 => D[0])
    end
end

function makeroute(F::FinderGraph)
    return makecirclepath(F.graph;source = F.user.first, ending = F.user.first)
end


text:
const VLSpec = typeof(VegaLite.@vlplot())

"""
    Orihime.readablestring(x) -> str::AbstractString

DWIM-formatter.
"""
Orihime.readablestring
Orihime.readablestring(x) = string(x)

function Orihime.readablestring(x::Integer)
    if abs(x) >= 2^10
        e = round(Int, log2(abs(x)))
        if 2^e == abs(x)
            if x > 0
                return "2^$e"
            else
                return "-2^$e"
            end
        end
    end
    if abs(x) >= 1_000
        e = round(Int, log10(abs(x)))
        if 10^e == abs(x)
            if x > 0
                return "10^$e"
            else
                return "-10^$e"
            end
        end
    end
    if abs(x) > 1000
        return format_with_thousands_separator(x)
    end
    return string(x)
end

function format_with_thousands_separator(x::Integer)
    isnegative = x < 0
    nd = ndigits(x)
    ngroups = (nd - 1) ÷ 3
    buffer = Vector{UInt8}(undef, nd + isnegative + ngroups)
    fill!(buffer, 0)  # TODO: remove
    buffer[1] = '-'  # will be overwritten if !isnegative
    x = abs(x)
    i = lastindex(buffer)
    c = 0
    while true
        if c == 3
            buffer[i] = ','
            c = 0
            i -= 1
        end
        c += 1

        d, r = divrem(x, 10)
        buffer[i] = '0' + r
        i -= 1
        d == 0 && break
        x = d
    end
    @assert all(>(0), buffer)  # TODO: remove
    return String(buffer)
end


text:
using Documenter
using CFMG

makedocs(
    sitename = "CFMG",
    format = Documenter.HTML(
        prettyurls = get(ENV, "CI", nothing) == "true"
    ),
    modules = [CFMG],
    pages = [
    "Home" => "index.md",
    "Introduction" => "introduction.md",
    "Installation" => "installation.md",
    "VFF format" => "vffformat.md",
    "Examples" => "example.md",
    ],
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
deploydocs(
    repo = "github.com/varnerlab/CFMG.git",
    devurl = "stable",
)

max_stars_repo_name: varnerlab/CFMG

max_issues_repo_name: varnerlab/CFMG

max_forks_repo_name: varnerlab/CFMG


text:
# MIT License
#
# Copyright (c) 2020 Joshua E Gentry

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# =================================================================================================
# Test writing a BMP image.
@testset "pix_write_bmp/pix_read_bmp" begin
    local pix      = pix_with()
    local original = pix_write_pam(pix)

    function unchanged(test)
        test != nothing && pix_write_pam(test) == original
    end

    # ---------------------------------------------------------------------------------------------
    # To/From a file.
    local filename = safe_tmp_file()
    @test pix_write_bmp(filename, pix) == true
    @test unchanged(pix_read_bmp(filename))
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To/From an IOBuffer.
    local buffer = IOBuffer()
    @test pix_write_bmp(buffer, pix) == true
    @test position(buffer) != 0
    seekstart(buffer)
    @test unchanged(pix_read_bmp(buffer))

    # ---------------------------------------------------------------------------------------------
    # To/From an IOStream
    local filename = safe_tmp_file()
    local file     = open(filename, read=true, write=true)
    @test pix_write_bmp(file, pix) == true
    @test position(file) != 0
    seekstart(file)
    @test unchanged(pix_read_bmp(file))
    close(file)
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To/From a byte array.
    local bytes = pix_write_bmp(pix)
    @test length(bytes) > 0
    @test unchanged(pix_read_bmp(bytes))
end

# =================================================================================================
# Test reading empty data.
@testset "pix_read_bmp /w empty data" begin
    @suppress begin
        # -----------------------------------------------------------------------------------------
        # From a file.
        local filename = safe_tmp_file()
        @test pix_read_bmp(filename) == nothing
        @test pix_read_bmp("xyzzy.bmp") == nothing
        rm(filename)

        # -----------------------------------------------------------------------------------------
        # From an IOBuffer.
        local buffer = IOBuffer()
        @test pix_read_bmp(buffer) == nothing

        # -----------------------------------------------------------------------------------------
        # To/From a byte array.
        local bytes = Vector{UInt8}()
        @test pix_read_bmp(bytes) == nothing
    end
end

# =================================================================================================
# Test writing a Pix that has been freed.
@testset "pix_write_bmp /w freed Pix" begin
    local err = "Pix has been freed."
    local pix = pix_with()
    pix_delete!(pix)

    # ---------------------------------------------------------------------------------------------
    # To a file.
    local filename = safe_tmp_file()
    @test (@test_logs (:error, err) pix_write_bmp(filename, pix)) == false
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To an IOBuffer.
    local buffer = IOBuffer()
    @test (@test_logs (:error, err) pix_write_bmp(buffer, pix)) == false

    # ---------------------------------------------------------------------------------------------
    # To a byte array.
    @test (@test_logs (:error, err) pix_write_bmp(pix)) == nothing
end


text:
using SeaPearl
using SeaPearlExtras
using ReinforcementLearning
const RL = ReinforcementLearning
using Flux
using GeometricFlux
using BSON: @save, @load
using Random
using Statistics

include("rewards.jl")
include("features.jl")

# -------------------
# Generator
# -------------------
nqueens_generator = SeaPearl.NQueensGenerator(25)
#model = model_queens(4)
SR = SeaPearl.DefaultStateRepresentation{SeaPearl.DefaultFeaturization, SeaPearl.DefaultTrajectoryState}
#gplot(SR.cplayergraph)

# -------------------
# Internal variables
# -------------------
numInFeatures = SeaPearl.feature_length(SR)

# -------------------
# Experience variables
# -------------------
nbEpisodes = 100
evalFreq = 300
nbInstances = 1
nbRandomHeuristics = 0

# -------------------
# Agent definition
# -------------------
include("agents.jl")

# -------------------
# Value Heuristic definition
# -------------------
learnedHeuristic = SeaPearl.LearnedHeuristic{SR, InspectReward, SeaPearl.FixedOutput}(agent)

# Basic value-selection heuristic
selectMin(x::SeaPearl.IntVar; cpmodel=nothing) = SeaPearl.minimum(x.domain)
heuristic_min = SeaPearl.BasicHeuristic(selectMin)

function select_random_value(x::SeaPearl.IntVar; cpmodel=nothing)
    selected_number = rand(1:length(x.domain))
    i = 1
    for value in x.domain
        if i == selected_number
            return value
        end
        i += 1
    end
    @assert false "This should not happen"
end

randomHeuristics = []
for i in 1:nbRandomHeuristics
    push!(randomHeuristics, SeaPearl.BasicHeuristic(select_random_value))
end

valueSelectionArray = [learnedHeuristic, heuristic_min]
append!(valueSelectionArray, randomHeuristics)
# -------------------
# Variable Heuristic definition
# -------------------
variableSelection = SeaPearl.MinDomainVariableSelection{false}()

# -------------------
# -------------------
# Core function
# -------------------
# -------------------

function trytrain(nbEpisodes::Int)

    metricsArray, eval_metricsArray = SeaPearl.train!(
        valueSelectionArray=valueSelectionArray,
        generator=nqueens_generator,
        nbEpisodes=nbEpisodes,
        strategy=SeaPearl.DFSearch(),
        variableHeuristic=variableSelection,
        out_solver=false,
        verbose = false,
        evaluator=SeaPearl.SameInstancesEvaluator(valueSelectionArray,nqueens_generator; evalFreq = evalFreq, nbInstances = nbInstances),
        restartPerInstances = 1
    )

    #saving model weights

    return metricsArray, eval_metricsArray
end

# -------------------
# -------------------

metricsArray, eval_metricsArray = trytrain(nbEpisodes)
nothing

max_stars_repo_name: corail-research/SeaPearlZoo

max_issues_repo_name: corail-research/SeaPearlZoo

max_forks_repo_name: corail-research/SeaPearlZoo


text:
### A Pluto.jl notebook ###
# v0.19.3

using Markdown
using InteractiveUtils

# ╔═╡ 5d760db0-8906-11ec-1a77-572cffd0d661
md"""
# `@revise`

The `@revise` macro is useful when developing a package.
Suppose that we're developing a package called `MyPackage`.
Pluto can be set to monitor the package via

```julia
@revise using MyPackage
```

When this package changes, all cells which depend on source code from the package will automatically run again.

Here is an example workflow of editing a package in another editor and testing it reactively in a Pluto notebook:

![Example of using the `@revise` macro](https://user-images.githubusercontent.com/9824244/152143460-798ea60c-a3ec-446f-ba12-c11b513281fe.gif)

In order to include a single file, the [`@ingredients`](./ingredients) macro can be used instead.

### Working on tests

For example, to use this hook to work on tests, the following code can be used.
Note that this code installs [TestEnv](https://github.com/JuliaTesting/TestEnv.jl) in the global Julia environment.

```julia
dir = joinpath(homedir(), "git", "MyPackage.jl")
```

```julia
begin
    using Pkg
    Pkg.add("TestEnv")
    Pkg.activate(dir)
    using TestEnv
    TestEnv.activate()
    Pkg.add("PlutoLinks")
end
```

```julia
using PlutoLinks: @revise
```

```julia
@revise using MyPackage
```

### Acknowledgments

 - The `@revise` macro could not be possible without [Revise.jl](https://github.com/timholy/Revise.jl) (as the name suggests :)).
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

julia_version = "1.7.2"
manifest_format = "2.0"

[deps]
"""

# ╔═╡ Cell order:
# ╟─5d760db0-8906-11ec-1a77-572cffd0d661
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
# Visualize output from running multiple experiments on regions of the brain
include("Plot_Helper.jl")
using Plots
using LaTeXStrings
# Specify which experiment you want
epsi = .1
for seedtype = 2:4
alpha = .6

prPPR, rePPR, f1PPR, Sizes = LoadPPRTrain(alpha,seedtype)

ppr_best,~ = findmax(f1PPR,1)

x_label = "Number of Nodes in Region"
y_label1 = "Precision"
y_label2 = "Recall"
y_label3 = "F1 Score"
numregions = size(f1s,2)
l_place = :false

# F1 Plots
lstyle = :solid
lw = 2
s1 = 400
s2 = 300

plot(f1PPR[1,:], labels = "\\varepsilon_{pr} = 10^{ -7}",grid = false,size = (s1,s2),
    xlabel = x_label,ylabel = y_label3,legend = l_place,linewidth = lw,
    xticks = (1:numregions,Sizes), xrotation=50,markershape = :circle,linestyle = lstyle,
)

plot!(f1PPR[2,:], labels = "\\varepsilon_{pr} = 10^{ -8}",
    xlabel = x_label,ylabel = y_label3,legend = l_place,linewidth = lw,size = (s1,s2),
    xticks = (1:numregions,Sizes), xrotation=50,markershape = :circle,linestyle = lstyle,
)

plot!(f1PPR[3,:], labels = "\\varepsilon_{pr} = 10^{ -9}",
    xlabel = x_label,ylabel = y_label3,legend = l_place,linewidth = lw,size = (s1,s2),
    xticks = (1:numregions,Sizes), xrotation=50,markershape = :circle,linestyle = lstyle,
)

plot!(f1PPR[4,:], labels = "\\varepsilon_{pr} = 10^{ -10}",
    xlabel = x_label,ylabel = y_label3,legend = l_place,linewidth = lw,size = (s1,s2),
    xticks = (1:numregions,Sizes), xrotation=50,markershape = :circle,linestyle = lstyle,
)

savefig("JustPush_SeedType_"*string(seedtype)*"alpha60.pdf")
end

max_stars_repo_name: nveldt/FlowSeed

max_issues_repo_name: nveldt/FlowSeed

max_forks_repo_name: nveldt/FlowSeed


text:
module My_iesh

curr_vers = "v.0.8"
using LinearAlgebra
using Random
using StaticArrays
using Plots
using HDF5
using Statistics
using Dates

include("read_au_data.jl");
include("setup_parameters.jl");
include("types.jl");
include("misc_func.jl");
include("constructor.jl");
include("get_neighbors.jl");
include("get_energies.jl");
include("get_forces.jl");
include("propagation.jl");
include("run_trajectory.jl");

multiple_trajectory()

end


text:
const RESERVED_WORDS = ASCIIString["begin", "while", "if", "for", "try",
    "return", "break", "continue", "function", "macro", "quote", "let",
    "local", "global", "const", "abstract", "typealias", "type", "bitstype",
    "immutable", "ccall", "do", "module", "baremodule", "using", "import",
    "export", "importall", "end", "else", "elseif", "catch", "finally"]

 function is_valid_identifier(sym::Symbol)
    s = string(sym)
    s == normalize_string(s) && isidentifier(s)
 end

function identifier(s::String)
    s = normalize_string(s)
    symbol(isidentifier(s) ? s : makeidentifier(s))
end

function isidentifier(s::String)
    n = length(s)
    if n == 0 || in(s, RESERVED_WORDS)
        return false
    end
    firstchar = true
    for c in s
        if firstchar
            if !(isalpha(c) || c == '_')
                return false
            end
            firstchar = false
        elseif !(isalpha(c) || isdigit(c) || c == '_' || c == '!')
            return false
        end
    end
    return true
end

function makeidentifier(s::String)
    ind = 0
    n = endof(s)
    res = Array(Char, 0)

    while isempty(res)
        if ind >= n
            return "x"
        end

        ind = nextind(s, ind)
        c = s[ind]

        if isalpha(c)
            push!(res, c)
        elseif isdigit(c) || c == '!'
            push!(res, 'x')
            push!(res, c)
        end
    end

    while ind < n
        ind = nextind(s, ind)
        c = s[ind]

        if isalpha(c) || isdigit(c) || c == '!'
            push!(res, c)
        else
            while ind < n
                ind = nextind(s, ind)
                c = s[ind]

                if isalpha(c) || isdigit(c) || c == '!'
                    push!(res, '_')
                    push!(res, c)
                    break
                end
            end
        end
    end

    cs = utf8(res)
    return in(cs, RESERVED_WORDS) ? "_"*cs : cs
end

function make_unique(names::Vector{Symbol})
    x = Index()
    names = copy(names)
    dups = Int[]
    for i in 1:length(names)
        if haskey(x, names[i])
            push!(dups, i)
        else
            push!(x, names[i])
        end
    end
    for i in dups
        nm = names[i]
        newnm = nm
        k = 1
        while true
            newnm = symbol("$(nm)_$k")
            if !haskey(x, newnm)
                push!(x, newnm)
                break
            end
            k += 1
        end
        names[i] = newnm
    end
    names
end

#' @description
#'
#' Generate standardized names for columns of a DataFrame. The
#' first name will be :x1, the second :x2, etc.
#'
#' @field n::Integer The number of names to generate.
#'
#' @returns names::Vector{Symbol} A vector of standardized column names.
#'
#' @examples
#'
#' DataFrames.gennames(10)
function gennames(n::Integer)
    res = Array(Symbol, n)
    for i in 1:n
        res[i] = symbol(@sprintf "x%d" i)
    end
    return res
end

#' @description
#'
#' Count the number of missing values in an Array.
#'
#' NOTE: This function always returns 0.
#'
#' @field a::Array The Array whose missing values are to be counted.
#'
#' @returns count::Int The number of missing values in `a`.
#'
#' @examples
#'
#' DataFrames.countna([1, 2, 3])
countna(a::Array) = 0

#' @description
#'
#' Count the number of missing values in a DataArray.
#'
#' @field da::DataArray The DataArray whose missing values are to be counted.
#'
#' @returns count::Int The number of missing values in `a`.
#'
#' @examples
#'
#' DataFrames.countna(@data([1, 2, 3]))
function countna(da::DataArray)
    n = length(da)
    res = 0
    for i in 1:n
        if da.na[i]
            res += 1
        end
    end
    return res
end

#' @description
#'
#' Count the number of missing values in a PooledDataArray.
#'
#' @field pda::PooledDataArray The PooledDataArray whose missing values
#'        are to be counted.
#'
#' @returns count::Int The number of missing values in `a`.
#'
#' @examples
#'
#' DataFrames.countna(@pdata([1, 2, 3]))
function countna(da::PooledDataArray)
    n = length(da)
    res = 0
    for i in 1:n
        if da.refs[i] == 0
            res += 1
        end
    end
    return res
end

# slow, but maintains order and seems to work:
function _setdiff(a::Vector, b::Vector)
    idx = Int[]
    for i in 1:length(a)
        if !(a[i] in b)
            push!(idx, i)
        end
    end
    a[idx]
end

function _uniqueofsorted(x::Vector)
    idx = fill(true, length(x))
    lastx = x[1]
    for i = 2:length(x)
        if lastx == x[i]
            idx[i] = false
        else
            lastx = x[i]
        end
    end
    x[idx]
end