text:
# # 110: 1D Reaction Diffusion equation with two species
# ([source code](SOURCE_URL))
#
# Solve the nonlinear coupled reaction diffusion problem
#
# ```math
# -\nabla (0.01+2u_2)\nabla u_1 + u_1u_2= 0.0001(0.01+x)
# ```
#
# ```math
# -\nabla (0.01+2u_1)\nabla u_2 - u_1u_2 = 0.0001(1.01-x)
# ```
#
#
# in $\Omega=(0,1)$ with boundary condition $u_1(0)=1$, $u_2(0)=0$ and $u_1(1)=1$, $u_2(1)=1$.
#

module Example110_ReactionDiffusion1D_TwoSpecies

using Printf
using VoronoiFVM
using ExtendableGrids
using GridVisualize

function main(;n=100,Plotter=nothing,verbose=false,unknown_storage=:sparse)
    h=1/n
    grid=VoronoiFVM.Grid(collect(0:h:1))

    eps::Vector{Float64}=[1.0,1.0]

    physics=VoronoiFVM.Physics(num_species=2,

                               reaction=function(f,u,node)
                               f[1]=u[1]*u[2]
                               f[2]=-u[1]*u[2]
                               end,

                               flux=function(f,u0,edge)
                               u=unknowns(edge,u0)

                               nspecies=2
                               f[1]=eps[1]*(u[1,1]-u[1,2])*(0.01+u[2,1]+u[2,2])
                               f[2]=eps[2]*(u[2,1]-u[2,2])*(0.01+u[1,1]+u[1,2])
                               end,

                               source=function(f,node)
                               f[1]=1.0e-4*(0.01+node[1])
                               f[2]=1.0e-4*(0.01+1.0-node[1])
                               end,

                               storage=function(f,u,node)
                               f[1]=u[1]
                               f[2]=u[2]
                               end
                               )

    sys=VoronoiFVM.System(grid,physics,unknown_storage=unknown_storage)

    enable_species!(sys,1,[1])
    enable_species!(sys,2,[1])

    boundary_dirichlet!(sys,1,1,1.0)
    boundary_dirichlet!(sys,1,2,0.0)

    boundary_dirichlet!(sys,2,1,1.0)
    boundary_dirichlet!(sys,2,2,0.0)

    inival=unknowns(sys)
    U=unknowns(sys)
    inival.=0

    control=VoronoiFVM.NewtonControl()
    control.verbose=verbose
    control.damp_initial=0.1
    u5=0
    p=GridVisualizer(Plotter=Plotter,layout=(2,1))
    for xeps in [1.0,0.5,0.25,0.1,0.05,0.025,0.01]
        eps=[xeps,xeps]
        solve!(U,inival,sys,control=control)
        inival.=U
        scalarplot!(p[1,1],grid,U[1,:],clear=true,title="U1, eps=$(xeps)")
        scalarplot!(p[2,1],grid,U[2,:],clear=true,title="U2, eps=$(xeps)",reveal=true)
        sleep(0.2)
        u5=U[5]
    end
    return u5
end

function test()
    testval=0.7117546972922056
    main(unknown_storage=:sparse) ≈ testval && main(unknown_storage=:dense) ≈ testval
end
end


text:
import CoverageTools

const MallocInfo = CoverageTools.MallocInfo
const analyze_malloc = CoverageTools.analyze_malloc
const analyze_malloc_files = CoverageTools.analyze_malloc_files
const find_malloc_files = CoverageTools.find_malloc_files
const sortbybytes = CoverageTools.sortbybytes

# Support Unix command line usage like `julia Coverage.jl $(find ~/.julia/v0.6 -name "*.jl.mem")`
if abspath(PROGRAM_FILE) == joinpath(@__DIR__, "Coverage.jl")
    bc = analyze_malloc_files(ARGS)
    println(bc)
end

ext: jl


text:
using Turing
using Test

@model hmcmatrixsup() = begin
  v ~ Wishart(7, [1 0.5; 0.5 1])
  v
end

model_f = hmcmatrixsup()
vs = []
chain = nothing
for _ in 1:5
  chain = sample(model_f, HMC(3000, 0.1, 3))
  push!(vs, mean(chain[:v]))
end

@test mean(vs) ≈ (7 * [1 0.5; 0.5 1]) atol=0.5


text:
import SparseArrays
import Base.push!
import SparseArrays
import DataStructures

include("index_util.jl")

struct SimHash
    salt::UInt
    n_hashes :: Int
    HashVectorType :: DataType
    SimHash(seed = rand(UInt)) = new(seed, 64, BitVector)
end

function fingerprint(sh::SimHash, features_weights)
    fingerprints = zeros(64)
    for (feature, weight) in features_weights
        hash_ = hash(feature, sh.salt)
        @inbounds @simd for i in 1:64
            if (hash_ & _bitmask(i)) != 0
                fingerprints[i] += weight
            else
                fingerprints[i] -= weight
            end
        end
    end
    signature = UInt(0)
    for (i, finger) in enumerate(fingerprints)
        if finger > 0
            signature |= _bitmask(i)
        end
    end
    signature
end

function _bitmask(i)
    UInt(1) << (i - 1)
end

function fingerprint_all(sh::SimHash, data::SparseArrays.SparseMatrixCSC)
    n = size(data, 2)
    fingerprints = Vector{UInt64}(undef, n)
    Threads.@threads for i in 1:n
        col = data[:,i]
        fingerprints[i] = fingerprint(sh, col)
    end
    fingerprints
end

function fingerprint(sh::SimHash, features_weights::SparseArrays.SparseVector)
    fingerprint(sh, zip(features_weights.nzind, features_weights.nzval))
end

"""
    Using the function P(h(a) = h(b)) = 1 - θ/π, return an estimate of the cosine distance.
"""
function estimate_cosine(a, b)
    cos((hamming(a,b) / 64) * π )
end

#TODO: Benchmark xor popcnt instead of distances packages
@inline function hamming(a, b)
    count_ones(xor(a,b))
end

mutable struct SimHashIndex <: LSHIndex
    tables :: Vector{DataStructures.DefaultDict{UInt64, Vector{Int}}}
    bands :: Int
    rows :: Int
    current_index :: Int
    masks :: Vector{UInt}
    function SimHashIndex(cosine_similarity_threshold)
        bands, rows = _get_partition(_cosine_to_probability(cosine_similarity_threshold), 64)
        tables = [DataStructures.DefaultDict{UInt64, Vector{Int}}(Vector{Int}) for _ in 1:bands]
        masks = [_mask((i - 1) * rows + 1 : min(i * rows, 64)) for i in 1:bands]
        new(tables, bands, rows, 1, masks)
    end
end

"""
Push new fingerprint to index.
"""
function push!(shind::SimHashIndex, fp::UInt)
    index = shind.current_index
    masks = shind.masks
    bands = fp .& masks
    for (fingerprint_part, table) in zip(bands, shind.tables)
        push!(table[fingerprint_part], index)
    end
    shind.current_index += 1;
end

"""
Find probably similar indices in index.
"""
function find_similar(shind::SimHashIndex, fp::UInt)::Vector{Int}
    indices = Vector{Vector{Int}}()
    bands = fp .& shind.masks
    for (band, table) in zip(bands, shind.tables)
        push!(indices, table[band])
    end
    unique(vcat(indices...))
end

function _mask(r::UnitRange{Int})
    z = UInt(0)
    for i in r
        z |= (UInt(1) << (i - 1))
    end
    z
end

function _cosine_to_hamming(t)
    Int(round(acos(t)/π*64))
end

function _cosine_to_probability(t)
    1-acos(t)/π
end


text:
const CompletionItemKind = Int
const CompletionItemKinds = (Text = 1,
                            Method = 2,
                            Function = 3,
                            Constructor = 4,
                            Field = 5,
                            Variable = 6,
                            Class = 7,
                            Interface = 8,
                            Module = 9,
                            Property = 10,
                            Unit = 11,
                            Value = 12,
                            Enum = 13,
                            Keyword = 14,
                            Snippet = 15,
                            Color = 16,
                            File = 17,
                            Reference = 18,
                            Folder = 19,
                            EnumMember = 20,
                            Constant = 21,
                            Struct = 22,
                            Event = 23,
                            Operator = 24,
                            TypeParameter = 25)

const CompletionItemTag = Int
const CompletionItemTags = (Deprecated = 1)

const CompletionTriggerKind = Int
const CompletionTriggerKinds = (Invoked = 1,
                                TriggerCharacter = 2,
                                TriggerForIncompleteCompletion = 3)

const InsertTextFormat = Int
const InsertTextFormats = (PlainText = 1,
                           Snippet = 2)

@dict_readable struct CompletionTagClientCapabilities
    valueSet::Vector{CompletionItemTag}
end

@dict_readable struct CompletionItemClientCapabilities <: Outbound
    snippetSupport::Union{Bool,Missing}
    commitCharactersSupport::Union{Bool,Missing}
    documentationFormat::Union{Vector{String},Missing}
    deprecatedSupport::Union{Bool,Missing}
    preselectSupport::Union{Bool,Missing}
    tagSupport::Union{CompletionTagClientCapabilities,Missing}
end

@dict_readable struct CompletionItemKindCapabilities <: Outbound
    valueSet::Union{Vector{CompletionItemKind},Missing}
end

@dict_readable struct CompletionClientCapabilities <: Outbound
    dynamicRegistration::Union{Bool,Missing}
    completionItem::Union{CompletionItemClientCapabilities,Missing}
    completionItemKind::Union{CompletionItemKindCapabilities,Missing}
    contextSupport::Union{Bool,Missing}
end

struct CompletionOptions <: Outbound
    resolveProvider::Union{Bool,Missing}
    triggerCharacters::Union{Vector{String},Missing}
    workDoneProgress::Union{Bool,Missing}
end

struct CompletionRegistrationOptions <: Outbound
    documentSelector::Union{DocumentSelector,Nothing}
    triggerCharacters::Union{Vector{String},Missing}
    allCommitCharacters::Union{Vector{String},Missing}
    resolveProvider::Union{Bool,Missing}
end

struct CompletionContext <: Outbound
    triggerKind::CompletionTriggerKind
    triggerCharacter::Union{String,Missing}
end

function CompletionContext(d::Dict)
    CompletionContext(d["triggerKind"], haskey(d, "triggerCharacter") && d["triggerCharacter"] isa String ? d["triggerCharacter"] : missing)
end

@dict_readable struct CompletionParams <: Outbound
    textDocument::TextDocumentIdentifier
    position::Position
    context::Union{CompletionContext,Missing}
end

struct CompletionItem <: Outbound
    label::String
    kind::Union{Int,Missing}
    tags::Union{CompletionItemTag,Missing}
    detail::Union{String,Missing}
    documentation::Union{String,MarkupContent,Missing}
    deprecated::Union{Bool,Missing}
    preselect::Union{Bool,Missing}
    sortText::Union{String,Missing}
    filterText::Union{String,Missing}
    insertText::Union{String,Missing}
    insertTextFormat::Union{InsertTextFormat,Missing}
    textEdit::Union{TextEdit,Missing}
    additionalTextEdits::Union{Vector{TextEdit},Missing}
    commitCharacters::Union{Vector{String},Missing}
    command::Union{Command,Missing}
    data::Union{Any,Missing}
end
CompletionItem(label, kind, documentation, textEdit) = CompletionItem(label, kind, missing, missing, documentation, missing, missing, missing, missing, missing, InsertTextFormats.PlainText, textEdit, missing, missing, missing, missing)
CompletionItem(label, kind, detail, documentation, textEdit) = CompletionItem(label, kind, missing, detail, documentation, missing, missing, missing, missing, missing, InsertTextFormats.PlainText, textEdit, missing, missing, missing, missing)

struct CompletionList <: Outbound
    isIncomplete::Bool
    items::Vector{CompletionItem}
end


text:
#
# Potential energy and force
#
function forcepair(x,y,r,data)
  r6 = r^6
  r12 = r6^2
  r7 = r6*r
  r13 = r12*r

  drdx1 = -(x[1]-y[1])/r
  drdx2 = -(x[2]-y[2])/r

  sigfac1 = -12*(data.sig12/r13)
  sigfac2 = -6*(data.sig6/r7)

  dfacdr = -data.eps4*(sigfac1 - sigfac2)

  upair = data.eps4*(data.sig12/r12 - data.sig6/r6)
  fx = ( -dfacdr*drdx1, -dfacdr*drdx2 )

  return upair, fx
end


text:
include("euler/euler.jl")
using .Calculus: triangular_numbers

function compute(n::Int)::Int
    limit = trunc(Int, √n)
    triangles = triangular_numbers(limit)
    min_difference = n
    nearest = 0
    for i ∈ 1:limit
        for j ∈ 1:i
            rectangles = triangles[i] * triangles[j]
            difference = abs(n - rectangles)
            if difference < min_difference
                min_difference = difference
                nearest = i * j
            end
            if rectangles > n break end
        end
    end
    return nearest
end

max_stars_repo_name: Dynortice/Project-Euler

max_issues_repo_name: Dynortice/Project-Euler

max_forks_repo_name: Dynortice/Project-Euler


text:
using JLD2
using Plots
using ProgressMeter

include("experiments/markov_chain_var.jl")
include("plot/plot_utils.jl")

function main()

    plot_settings = Dict{Symbol, Any}(
        :linewidth=>2,
        # :color=>[:purple, :blue, :purple, :blue, :green],
        # :linestyle=>[:solid, :solid, :dot, :dot, :solid],
        :color=>[:green, :blue, :green, :purple],
        :linestyle=>[:solid, :solid, :dot, :solid],
        :grid=>false,
        :size=>(432,244),
        :tick_direction=>:out,
        # :ylim=>(0,0.2),
        :legend=>false
    )
    policies = ["easy", "hard", "hardest"]
    # policies = ["hardest"]
    num_runs = 100
    measure_steps=0:5:250
    x = convert(Array{Float64,1}, collect(measure_steps))
    lck = Threads.Mutex()

    # plot_algorithms = ["IS", "IR", "BC-IR", "WIS-Buffer", "WIS-Batch"]
    plot_algorithms = ["IS", "IR", "VTrace_1.0", "WIS-Batch"]

    # Threads.@threads for policy in policies
    @showprogress 0.1 "Policy: " for policy in policies
    # for policy in policies
        ret = measure_variance_over_time_norm(
            num_runs, 15000, 1000, 16,
            policy_settings[policy][1], policy_settings[policy][2];
            measure_steps=measure_steps, random_value=false, norm_l=1)
        lock(lck)

        p = plot_dict(x,
                      filter(p -> p[1] in plot_algorithms, ret[2]),
                      filter(p -> p[1] in plot_algorithms, ret[3]);
                      plot_order=plot_algorithms,
                      plot_settings...)
        savefig(p, "markov_chain_variance_l1_$(policy).pdf")
        @save "markov_chain_variance_l1_$policy.jld" ret
        unlock(lck)
    end

end

main()


text:
function NonLinearOptics(physic::MeanField, atoms, laser)
    @debug "start: NonLinearOptics - $( typeof(physic) )"

    excitations = Dict()
    data = Dict()

    @debug "end  : NonLinearOptics - $( typeof(physic) )"
    return NonLinearOptics(physic, atoms, laser, excitations, data)
end


text:
struct IMDB{S}
    filepaths::Vector{S}    #paths to all the files of specified category
end

function init_datadeps(::Type{IMDB})
    register(DataDep(
        "IMDB movie reviews dataset",
        """
        author    : Maas, Andrew L.  and  Daly, Raymond E.  and  Pham, Peter T.  and  Huang, Dan  and  Ng, Andrew Y.  and  Potts, Christopher
        title     : Learning Word Vectors for Sentiment Analysis
        booktitle : Proceedings of the 49th Annual Meeting of the Association for Computational Linguistics: Human Language Technologies
        month     : June
        year      : 2011
        address   : Portland, Oregon, USA
        publisher : Association for Computational Linguistics

        This is a dataset for binary sentiment classification.:
        25,000 highly polar reviews for training, 25,000 reviews for testing and additional unlabelled examples.

        Website:https://ai.stanford.edu/~amaas/data/sentiment/

        When using this dataset, please cite this paper
        http://www.aclweb.org/anthology/P11-1015
        """,
        "http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz",
        "c40f74a18d3b61f90feba1e17730e0d38e8b97c05fde7008942e91923d1658fe",
        post_fetch_method = unpack
    ))
end

function IMDB(category = "train_pos")
    paths = Dict{String, Vector{String}}()
    folder, subfolder = split(category, "_")
    basepath = joinpath(datadep"IMDB movie reviews dataset", "aclImdb", "$folder", "$subfolder")
    paths = joinpath.(basepath, readdir(basepath))
    IMDB(paths)
end

MultiResolutionIterators.levelname_map(::Type{IMDB}) = [
    :documents => 1,
    :sentences => 2,
    :words => 3, :tokens => 3,
    :characters => 4]

function load(dataset::IMDB)
    Channel(ctype=@NestedVector(String, 2), csize=4) do docs
        for path in dataset.filepaths   #extract data from the files in directory and put into channel
            open(path) do fileio
                cur_text = read(fileio, String)
                sents = [intern.(tokenize(sent)) for sent in split_sentences(cur_text)]
                put!(docs, sents)
            end #open
        end #for
    end #channel
end


text:
#### Domain decomposition
export DomainSet

import Base
abstract type Domain{N} end
struct GridMean{N} <: Domain{N} end
struct Environment{N} <: Domain{N} end
struct Updraft{N} <: Domain{N} end

"""
    n_subdomains(::Type{Domain})

The number of domains in domain `Domain`
"""
n_subdomains(::Type{GridMean{N}}) where {N} = N
n_subdomains(::Type{Environment{N}}) where {N} = N
n_subdomains(::Type{Updraft{N}}) where {N} = N

"""
    DomainSet{GM,EN,UD}

Decomposition of number of domains
 - `gm` number of grid-mean domain
 - `en` number of environment sub-domain
 - `ud` number of updraft sub-domains
"""
struct DomainSet{GM, EN, UD}
    function DomainSet(;
        gm::GM = 0,
        en::EN = 0,
        ud::UD = 0,
    ) where {GM <: Int, EN <: Int, UD <: Int}
        @assert 0 <= gm <= 1
        @assert 0 <= en <= 1
        @assert 0 <= ud
        return new{GridMean{gm}, Environment{en}, Updraft{ud}}()
    end
end

Base.sum(::DomainSet{GM, EN, UD}) where {GM, EN, UD} =
    n_subdomains(GM) + n_subdomains(EN) + n_subdomains(UD)
n_subdomains(::DomainSet{GM, EN, UD}) where {GM, EN, UD} =
    (n_subdomains(GM), n_subdomains(EN), n_subdomains(UD))

"""
    DomainIdx{GM,EN,UD}

Decomposition of domain indexes, including indexes for
 - `i_gm` grid-mean domain
 - `i_en` environment sub-domain
 - `i_ud` updraft sub-domains

Note that the index ordering logic is defined here.
"""
struct DomainIdx{GM, EN, UD} end

"""
    DomainDecomposition{VM}

A domain decomposition into
 - `grid_mean`
 - `environment`
 - `updrafts`
"""
struct DomainDecomposition{VM}
    """
    A `DomainIdx`, containing the complete set of sub-domain indexes
    """
    idx::DomainIdx
    """
    A `NamedTuple` variable mapper, containing indexes per variable
    """
    var_mapper::VM
    """
    A `Dict` index per sub-domain per variable
    """
    idx_ss_per_var::Dict
    """
    A mapper, containing mapping from global sub-domain indexes to sub-domain indexes per variable
    """
    a_map::Dict
    """
    A `NamedTuple` sub-domain mapper, containing indexes per variable which are sub-domain specific
    """
    sd_unmapped::Dict
    """
    A `DomainSet`, which specifies the complete set of `DomainSet` across all variables
    """
    domain_set::DomainSet
    """
    A `DomainSet` per variable
    """
    dss_per_var::NamedTuple
end


text:
module ClusterQueryUtils

using Distributed
using DataStructures

export hostnames
export nodenames
export procs_node
export nprocs_node
export workersactive
export chooseworkers
export maybetrimmedworkerpool
export workerpool_nodes
export workers_myhost

"""
    hostnames([procs = workers()])

Return the hostname of each worker in `procs`. This is obtained by evaluating
`Libc.gethostname()` on each worker asynchronously.
"""
function hostnames(procs = workers())
    hostnames = Vector{String}(undef, length(procs))

    @sync for (ind,p) in enumerate(procs)
        @async hostnames[ind] = @fetchfrom p Libc.gethostname()
    end
    return hostnames
end

"""
    nodenames([procs = workers()])

Return the unique hostnames that the workers in `procs` lie on.
On an HPC system these are usually the hostnames of the nodes involved.
"""
nodenames(procs = workers()) = nodenames(hostnames(procs))

function nodenames(hostnames::AbstractVector{String})
    unique(hostnames)
end

"""
    procs_node([procs = workers()])

Return the worker ids on each host of the cluster.
On an HPC system this would return the workers on each node.
"""
function procs_node(procs = workers())
    hosts = hostnames(procs)
    nodes = nodenames(hosts)
    procs_node(procs, hosts, nodes)
end

function procs_node(procs, hosts, nodes)
    OrderedDict(node => procs[findall(isequal(node),hosts)] for node in nodes)
end

"""
    nprocs_node([procs = workers()])

Return the number of workers on each host.
On an HPC system this would return the number of workers on each node.
"""
function nprocs_node(procs = workers())
    nprocs_node(hostnames(procs))
end

function nprocs_node(hostnames::AbstractVector{String})
    nodes = nodenames(hostnames)
    nprocs_node(hostnames, nodes)
end

function nprocs_node(hostnames::AbstractVector, nodes::AbstractVector)
    OrderedDict(node => count(isequal(node), hostnames) for node in nodes)
end

function nprocs_node(d::AbstractDict)
    OrderedDict(node => length(procs) for (node, procs) in d)
end

function workersactive(pool::AbstractWorkerPool, len::Integer,
    workers_on_hosts::AbstractDict = procs_node(workers(pool)))

    nw = min(nworkers(pool), len)
    chooseworkers(workers(pool), len, workers_on_hosts)
end

function chooseworkers(workerspool, n::Integer, workers_on_hosts::AbstractDict = procs_node(workerspool))
    n >= 1 || throw(ArgumentError("number of workers to choose must be >= 1"))
    length(workerspool) <= n && return workerspool
    myhost = Libc.gethostname()
    if myhost in keys(workers_on_hosts)
        if length(workers_on_hosts[myhost]) >= n
            return workers_on_hosts[myhost][1:n]
        else
            w_chosen = workers_on_hosts[myhost]
            np_left = n - length(w_chosen)
            for (host, workers_host) in workers_on_hosts
                np_left <= 0 && break
                host == myhost && continue
                workers_host_section = @view workers_host[1:min(length(workers_host), np_left)]
                w_chosen = vcat(w_chosen, workers_host_section)
                np_left -= length(workers_host_section)
            end
            return w_chosen
        end
    else
        return workerspool[1:n]
    end
end

function maybetrimmedworkerpool(workers, N)
    w = chooseworkers(workers, N)
    WorkerPool(w)
end

"""
    workerpool_nodes([pool::AbstractWorkerPool = WorkerPool(workers())])

Return a `WorkerPool` with one worker per machine/node of the cluster.
"""
workerpool_nodes(pool::AbstractWorkerPool) = WorkerPool(oneworkerpernode(workers(pool)))
workerpool_nodes() = WorkerPool(oneworkerpernode(workers()))

"""
    oneworkerpernode([workers::AbstractVector{<:Integer} = workers()])

Return a subsample of workers such that each `pid` in the returned vector is located on
one machine/node of the cluster.
"""
function oneworkerpernode(workers::AbstractVector{<:Integer} = workers())
    workers_on_hosts = procs_node(workers)
    [first(v) for v in values(workers_on_hosts)]
end

"""
    workers_myhost([workers::AbstractVector{<:Integer} = workers()])

Return a list of all workers that are on the local machine/node of the cluster.
"""
workers_myhost(workers::AbstractVector{<:Integer} = workers()) = procs_node(workers)[Libc.gethostname()]
workers_myhost(pool::AbstractWorkerPool) = workers_myhost(workers(pool))

end


text:
const makie_linetype = Dict{Symbol, Any}(
    :auto => nothing,
    :solid => nothing,
    :dash => :dash,
    :dot => :dot,
    :dashdot => :dashdot,
    :dashdotdot => [0.4, 0.2, 0.1, 0.2, 0.4]
)

function makie_color(c)
    if color === :match
        return Colors.colorant"blue"
    end
    convert(RGBA, c)
end

makie_seriestype_map = Dict{Symbol, Type}(
    :path => Lines,
    :path3d => Lines,
    :scatter => Scatter,
    :linesegments => LineSegments,
    :heatmap => Heatmap,
    :image => Image,
    :spy => Spy,
    :surface => Surface,
    :shape => Poly,
    :contour => Contour,
    :curves => Bezier,
    :bar => BarPlot,
    # TODO: line, contour,
)


text:
using BinaryProvider # requires BinaryProvider 0.3.0 or later

# Parse some basic command-line arguments
const verbose = "--verbose" in ARGS
const prefix = Prefix(get([a for a in ARGS if a != "--verbose"], 1, joinpath(@__DIR__, "usr")))
products = [
    LibraryProduct(prefix, ["libxgboost", "xgboost"], :libxgboost),
]

# Listing of files generated by BinaryBuilder:
download_info = Dict(
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.aarch64-linux-gnu-cxx03.tar.gz", "b599fac7b8416309421e9b0731dbc4f91a5bec207be5302ecbbf743e1e0dca48"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.aarch64-linux-gnu-cxx11.tar.gz", "752f619f32f874e0b379a8e947dfa955155a8924990f9015e90c9f93d99922d6"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.aarch64-linux-musl-cxx03.tar.gz", "fb55dd7aad759f73e0654aeefa4d7435eed98aa17678c088dbfbd6bd79c9607a"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.aarch64-linux-musl-cxx11.tar.gz", "5224fd2154051eab20acfb71fec4c1cd365ff268dab8b4333f7694c84c4a3096"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.arm-linux-gnueabihf-cxx03.tar.gz", "656a4eb4a2a44f05b83509471fa2046d946088240dd90eabfffb96dbae9a31d4"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.arm-linux-gnueabihf-cxx11.tar.gz", "ee683b6df2f2cecb2a1c88262f6757c31bac576be7fb55c49be21f62fc352a48"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.arm-linux-musleabihf-cxx03.tar.gz", "270d398fafa2cb082a1f382f29c3a2e62ae0d66d66b87fc9ca61b50d51767099"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.arm-linux-musleabihf-cxx11.tar.gz", "d0439f8f30558160e17b13410a79c32bd206e42247ba56a3b4b7d3adaaebefb7"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.i686-linux-gnu-cxx03.tar.gz", "fd9801855a15f0bffa3d0b92dc31bb63f31c9ace482ad25c6f530f713537cce9"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.i686-linux-gnu-cxx11.tar.gz", "eefe95c4a7156d0d561ad8d508d75b85706507174fa08868577ff7f7ff26965b"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.i686-linux-musl-cxx03.tar.gz", "e2831d6abe161966bc4f9b267bae3e928418509b7e3e3272db19e052d6d84b83"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.i686-linux-musl-cxx11.tar.gz", "115198a90d8084c587195407ee154ded8cafc64f2c34ec24f0496618b726b6a1"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.i686-w64-mingw32-cxx03.tar.gz", "7a9b377d1b0934508b9f806639a78b6fcf387373fc6224fe022ede86a52a2620"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.i686-w64-mingw32-cxx11.tar.gz", "e978a037ac8e24913179802023c65021b34a09d2571b883fef6b0eb560931ddc"),
    Linux(:powerpc64le, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.powerpc64le-linux-gnu-cxx03.tar.gz", "9d6e028b794b755564fa1e9bff58c49be661b64e9beee4441c670eca3c0576ff"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-apple-darwin14-cxx03.tar.gz", "8518d582a25eb12b8e0ef7f2dec49b6e1e84c0e9fe5f4eee616f9afdb4224578"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-apple-darwin14-cxx11.tar.gz", "7b35da5c646c04b2fecef829b84bf6efc77ae396ef35fa48fd21465dae8b346a"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-linux-gnu-cxx03.tar.gz", "2f20ff0361701246f0be3f757f06896f7917ec13b3ed0733492333928a99e566"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-linux-gnu-cxx11.tar.gz", "0521ddeca97c5f00c056312fd21957862ee7e624ec810602c0e7b277905edff8"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-linux-musl-cxx03.tar.gz", "a6b104e8bbdee41bbe7d19cc66919a7f58699ad862d448a4749d4eb4e8be7de7"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-linux-musl-cxx11.tar.gz", "f805bb141150b225b84e33b9b5f8cb2a40b4ea081b65958e5d2782fd439520d5"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc_any, :cxx03)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-w64-mingw32-cxx03.tar.gz", "b2d880cdf5b8246ada770df54c49e9904dceea2f168ca238e16f76b01261c675"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc_any, :cxx11)) => ("https://github.com/JuliaPackaging/Yggdrasil/releases/download/XGBoost-v0.82-2/XGBoost.v0.82.0.x86_64-w64-mingw32-cxx11.tar.gz", "59b4ae4edd24916a241ee6e695d220927836e66b7cc678d530f26a0a7b0c3b1f"),
)

# Install unsatisfied or updated dependencies:
unsatisfied = any(!satisfied(p; verbose=verbose) for p in products)
dl_info = choose_download(download_info, platform_key_abi())
if dl_info === nothing && unsatisfied
    # If we don't have a compatible .tar.gz to download, complain.
    # Alternatively, you could attempt to install from a separate provider,
    # build from source or something even more ambitious here.
    error("Your platform (\"$(Sys.MACHINE)\", parsed as \"$(triplet(platform_key_abi()))\") is not supported by this package!")
end

# If we have a download, and we are unsatisfied (or the version we're
# trying to install is not itself installed) then load it up!
if unsatisfied || !isinstalled(dl_info...; prefix=prefix)
    # Download and install binaries
    install(dl_info...; prefix=prefix, force=true, verbose=verbose)
end

# Write out a deps.jl file that will contain mappings for our products
write_deps_file(joinpath(@__DIR__, "deps.jl"), products, verbose=verbose)


text:
# ==================================== step125(x) ============================================================

"""
    step125(x)

Step used for deviding the number x in steps according to 1-2-5 scheme
#### Examples:
```
step125.([5,10,21.3,50,100.1])
5-element Vector{Int64}:
  1
  2
  5
 10
 20
```
"""
function step125(x::Real)

    m = log10_mantissa(x)
    p = log10_characteristic_power(x)
    v = 10^m
    d = v > 7.9 ? 2.0 : v > 3.9 ? 1.0 : v > 1.49 ? 0.5 : 0.2

    return max(1,round(Int, d *= 10^p))

end

# ==================================== select125(x) ===============================================================

"""
    select125(x)

Select elements of the collection x by index according to 1-2-5 scheme
#### Examples:
```@docs
x = [1,2,4,6,8,10,13,16,18,20,40,60,80,100]
select125(x)
 [2, 6, 10, 16, 20, 60, 100]

x = string.(x)
select125(x)
 ["2", "6", "10", "16", "20", "60", "100"]

x = 1:100
select125(x)
 [20, 40, 60, 80, 100]
```
"""
select125(x) = (n = length(x); return [x[i] for i=step125(n):step125(n):n])

# ==================================== edges(x) ===============================================================

"""
    edges(px [, Δx[, x0]])

Heatmap range transformation from pixel coordinates to physical coordinates,
with pixelsize Δx and offset x0, both in physical units.
#### Examples:
```@docs
px = 1:5
Δx = 2.5
x0 = 2.5
edges(px)
 [0.5, 1.5, 2.5, 3.5, 4.5]

edges(px, Δx)
 [1.25, 3.75, 6.25, 8.75, 11.25]

edges(px, Δx, x0)
 [-1.25, 1.25, 3.75, 6.25, 8.75]
```
"""
edges(px, Δx=1.0, x0=0.0) = collect(px .* Δx) .-(x0 + 0.5Δx)

# =================================== steps(x) =============================================================

"""
    steps(x)

Heatmap range transformation for steplength specification vector x
#### Examples:
```@docs
x = [4,2,6]
steps(x)
 [0, 4, 6, 12]
```
"""
function steps(x::Vector{T} where T<:Real)

    sum(x .< 0) == 0 || error("Error: $x - nagative step length not allowed")

    return (s = append!(eltype(x)[0],x); [Base.sum(s[1:i]) for i ∈ Base.eachindex(s)])

end

# =================================== stepcenters(x) =============================================================

"""
    stepcenters(x)

Stepcenter positions for steplength specification vector x
#### Examples:
```@docs
x = [4,2,6]
stepcenters(x)
 [2.0, 5.0, 9.0]
```
"""
function stepcenters(x::Vector{T} where T<:Real)

    s = append!(eltype(x)[0],x)

    return [Base.sum(s[1:i]) + 0.5x[i] for i ∈ Base.eachindex(x)]

end

# =================================== stepedges(x) =============================================================

"""
    stepedges(x)

Stepedges for steplength specification vector x
#### Examples:
```@docs
x = [4,2,6]
stepedges(x)
 [0, 4, 6, 12]
```
"""
function stepedges(x::Vector{T} where T<:Real)

    return steps(x)

end


text:
module Parser  # JSON

using ..Common

using Compat
import Compat: String

export parse

"""
Like `isspace`, but work on bytes and includes only the four whitespace
characters defined by the JSON standard: space, tab, line feed, and carriage
return.
"""
isjsonspace(b::UInt8) = b == SPACE || b == TAB || b == NEWLINE || b == RETURN

"""
Like `isdigit`, but for bytes.
"""
isjsondigit(b::UInt8) = DIGIT_ZERO ≤ b ≤ DIGIT_NINE

@compat abstract type ParserState end

type MemoryParserState <: ParserState
    utf8data::Vector{UInt8}
    s::Int
end

type StreamingParserState{T <: IO} <: ParserState
    io::T
    cur::UInt8
    used::Bool
end
StreamingParserState{T <: IO}(io::T) = StreamingParserState{T}(io, 0x00, true)

"""
Return the byte at the current position of the `ParserState`. If there is no
byte (that is, the `ParserState` is done), then an error is thrown that the
input ended unexpectedly.
"""
@inline function byteat(ps::MemoryParserState)
    @inbounds if hasmore(ps)
        return ps.utf8data[ps.s]
    else
        _error(E_UNEXPECTED_EOF, ps)
    end
end

@inline function byteat(ps::StreamingParserState)
    if ps.used
        ps.cur = read(ps.io, UInt8)
        ps.used = false
    end
    ps.cur
end

"""
Like `byteat`, but with no special bounds check and error message. Useful when
a current byte is known to exist.
"""
@inline current(ps::MemoryParserState) = ps.utf8data[ps.s]
@inline current(ps::StreamingParserState) = byteat(ps)

"""
Require the current byte of the `ParserState` to be the given byte, and then
skip past that byte. Otherwise, an error is thrown.
"""
@inline function skip!(ps::ParserState, c::UInt8)
    if byteat(ps) == c
        incr!(ps)
    else
        _error("Expected '$(Char(c))' here", ps)
    end
end

function skip!(ps::ParserState, cs::UInt8...)
    for c in cs
        skip!(ps, c)
    end
end

"""
Move the `ParserState` to the next byte.
"""
@inline incr!(ps::MemoryParserState) = (ps.s += 1)
@inline incr!(ps::StreamingParserState) = (ps.used = true)

"""
Move the `ParserState` to the next byte, and return the value at the byte before
the advancement. If the `ParserState` is already done, then throw an error.
"""
@inline advance!(ps::ParserState) = (b = byteat(ps); incr!(ps); b)

"""
Return `true` if there is a current byte, and `false` if all bytes have been
exausted.
"""
@inline hasmore(ps::MemoryParserState) = ps.s ≤ endof(ps.utf8data)
@inline hasmore(ps::StreamingParserState) = true  # no more now ≠ no more ever

"""
Remove as many whitespace bytes as possible from the `ParserState` starting from
the current byte.
"""
@inline function chomp_space!(ps::ParserState)
    @inbounds while hasmore(ps) && isjsonspace(current(ps))
        incr!(ps)
    end
end

# Used for line counts
function _count_before(haystack::AbstractString, needle::Char, _end::Int)
    count = 0
    for (i,c) in enumerate(haystack)
        i >= _end && return count
        count += c == needle
    end
    return count
end

# Throws an error message with an indicator to the source
function _error(message::AbstractString, ps::MemoryParserState)
    orig = Compat.UTF8String(ps.utf8data)
    lines = _count_before(orig, '\n', ps.s)
    # Replace all special multi-line/multi-space characters with a space.
    strnl = replace(orig, r"[\b\f\n\r\t\s]", " ")
    li = (ps.s > 20) ? ps.s - 9 : 1 # Left index
    ri = min(endof(orig), ps.s + 20)       # Right index
    error(message *
      "\nLine: " * string(lines) *
      "\nAround: ..." * strnl[li:ri] * "..." *
      "\n           " * (" " ^ (ps.s - li)) * "^\n"
    )
end

function _error(message::AbstractString, ps::StreamingParserState)
    error("$message\n ...when parsing byte with value '$(current(ps))'")
end

# PARSING

"""
Given a `ParserState`, after possibly any amount of whitespace, return the next
parseable value.
"""
function parse_value(ps::ParserState, dictT::Type)
    chomp_space!(ps)

    @inbounds byte = byteat(ps)
    if byte == STRING_DELIM
        parse_string(ps)
    elseif isjsondigit(byte) || byte == MINUS_SIGN
        parse_number(ps)
    elseif byte == OBJECT_BEGIN
        parse_object(ps, dictT)
    elseif byte == ARRAY_BEGIN
        parse_array(ps, dictT)
    else
        parse_jsconstant(ps::ParserState)
    end
end

function parse_jsconstant(ps::ParserState)
    c = advance!(ps)
    if c == LATIN_T      # true
        skip!(ps, LATIN_R, LATIN_U, LATIN_E)
        true
    elseif c == LATIN_F  # false
        skip!(ps, LATIN_A, LATIN_L, LATIN_S, LATIN_E)
        false
    elseif c == LATIN_N  # null
        skip!(ps, LATIN_U, LATIN_L, LATIN_L)
        nothing
    else
        _error(E_UNEXPECTED_CHAR, ps)
    end
end

function parse_array(ps::ParserState, dictT::Type)
    result = Any[]
    @inbounds incr!(ps)  # Skip over opening '['
    chomp_space!(ps)
    if byteat(ps) ≠ ARRAY_END  # special case for empty array
        @inbounds while true
            push!(result, parse_value(ps, dictT))
            chomp_space!(ps)
            byteat(ps) == ARRAY_END && break
            skip!(ps, DELIMITER)
        end
    end

    @inbounds incr!(ps)
    result
end

function parse_object(ps::ParserState, dictT::Type)
    obj = dictT()
    keyT = dictT.parameters[1]

    incr!(ps)  # Skip over opening '{'
    chomp_space!(ps)
    if byteat(ps) ≠ OBJECT_END  # special case for empty object
        @inbounds while true
            # Read key
            chomp_space!(ps)
            byteat(ps) == STRING_DELIM || _error(E_BAD_KEY, ps)
            key = parse_string(ps)
            chomp_space!(ps)
            skip!(ps, SEPARATOR)
            # Read value
            value = parse_value(ps, dictT)
            chomp_space!(ps)
            obj[convert(keyT, key)] = value
            byteat(ps) == OBJECT_END && break
            skip!(ps, DELIMITER)
        end
    end

    incr!(ps)
    obj
end

utf16_is_surrogate(c::UInt16) = (c & 0xf800) == 0xd800
utf16_get_supplementary(lead::UInt16, trail::UInt16) = Char(UInt32(lead-0xd7f7)<<10 + trail)

function read_four_hex_digits!(ps::ParserState)
    local n::UInt16 = 0

    for _ in 1:4
        b = advance!(ps)
        n = n << 4 + if isjsondigit(b)
            b - DIGIT_ZERO
        elseif LATIN_A ≤ b ≤ LATIN_F
            b - (LATIN_A - UInt8(10))
        elseif LATIN_UPPER_A ≤ b ≤ LATIN_UPPER_F
            b - (LATIN_UPPER_A - UInt8(10))
        else
            _error(E_BAD_ESCAPE, ps)
        end
    end

    n
end

function read_unicode_escape!(ps)
    u1 = read_four_hex_digits!(ps)
    if utf16_is_surrogate(u1)
        skip!(ps, BACKSLASH)
        skip!(ps, LATIN_U)
        u2 = read_four_hex_digits!(ps)
        utf16_get_supplementary(u1, u2)
    else
        Char(u1)
    end
end

function parse_string(ps::ParserState)
    b = UInt8[]
    incr!(ps)  # skip opening quote
    while true
        c = advance!(ps)

        if c == BACKSLASH
            c = advance!(ps)
            if c == LATIN_U  # Unicode escape
                append!(b, Vector{UInt8}(string(read_unicode_escape!(ps))))
            else
                c = get(ESCAPES, c, 0x00)
                c == 0x00 && _error(E_BAD_ESCAPE, ps)
                push!(b, c)
            end
            continue
        elseif c < SPACE
            _error(E_BAD_CONTROL, ps)
        elseif c == STRING_DELIM
            return Compat.UTF8String(b)
        end

        push!(b, c)
    end
end

"""
Return `true` if the given bytes vector, starting at `from` and ending at `to`,
has a leading zero.
"""
function hasleadingzero(bytes::Vector{UInt8}, from::Int, to::Int)
    c = bytes[from]
    from + 1 < to && c == UInt8('-') &&
            bytes[from + 1] == DIGIT_ZERO && isjsondigit(bytes[from + 2]) ||
    from < to && to > from + 1 && c == DIGIT_ZERO &&
            isjsondigit(bytes[from + 1])
end

"""
Parse a float from the given bytes vector, starting at `from` and ending at the
byte before `to`. Bytes enclosed should all be ASCII characters.
"""
function float_from_bytes(bytes::Vector{UInt8}, from::Int, to::Int)
    # The ccall is not ideal (Base.tryparse would be better), but it actually
    # makes an 2× difference to performance
    ccall(:jl_try_substrtod, Nullable{Float64},
            (Ptr{UInt8}, Csize_t, Csize_t), bytes, from - 1, to - from + 1)
end

"""
Parse an integer from the given bytes vector, starting at `from` and ending at
the byte before `to`. Bytes enclosed should all be ASCII characters.
"""
function int_from_bytes(bytes::Vector{UInt8}, from::Int, to::Int)
    @inbounds isnegative = bytes[from] == MINUS_SIGN ? (from += 1; true) : false
    num = Int64(0)
    @inbounds for i in from:to
        num = Int64(10) * num + Int64(bytes[i] - DIGIT_ZERO)
    end
    ifelse(isnegative, -num, num)
end

function number_from_bytes(
        ps::ParserState, isint::Bool, bytes::Vector{UInt8}, from::Int, to::Int)
    @inbounds if hasleadingzero(bytes, from, to)
        _error(E_LEADING_ZERO, ps)
    end

    if isint
        @inbounds if to == from && bytes[from] == MINUS_SIGN
            _error(E_BAD_NUMBER, ps)
        end
        int_from_bytes(bytes, from, to)
    else
        res = float_from_bytes(bytes, from, to)
        isnull(res) ? _error(E_BAD_NUMBER, ps) : get(res)
    end
end

function parse_number(ps::ParserState)
    # Determine the end of the floating point by skipping past ASCII values
    # 0-9, +, -, e, E, and .
    number = UInt8[]
    isint = true

    @inbounds while hasmore(ps)
        c = current(ps)

        if isjsondigit(c) || c == MINUS_SIGN
            push!(number, UInt8(c))
        elseif c in (PLUS_SIGN, LATIN_E, LATIN_UPPER_E, DECIMAL_POINT)
            push!(number, UInt8(c))
            isint = false
        else
            break
        end

        incr!(ps)
    end

    number_from_bytes(ps, isint, number, 1, length(number))
end

function unparameterize_type{T}(::Type{T})
    candidate = typeintersect(T, Associative{Compat.UTF8String, Any})
    candidate <: Union{} ? T : candidate
end

function parse{T<:Associative}(str::AbstractString; dicttype::Type{T}=Dict{Compat.UTF8String,Any})
    ps = MemoryParserState(Vector{UInt8}(Compat.UTF8String(str)), 1)
    v = parse_value(ps, unparameterize_type(T))
    chomp_space!(ps)
    if hasmore(ps)
        _error(E_EXPECTED_EOF, ps)
    end
    v
end

function parse{T<:Associative}(io::IO; dicttype::Type{T}=Dict{Compat.UTF8String,Any})
    ps = StreamingParserState(io)
    parse_value(ps, unparameterize_type(T))
end

# Efficient implementations of some of the above for in-memory parsing
include("specialized.jl")

end #module Parser


text:
# [
#  [ MPIStateArray Name, Field Name, Maximum, Minimum, Mean, Standard Deviation ],
#  [         :                :          :        :      :          :           ],
# ]

parr = [
    ["3D state", "u[1]", 12, 12, 0, 12],
    ["3D state", "u[2]", 0, 0, 0, 0],
    ["3D state", :η, 12, 12, 0, 12],
    ["3D state", :θ, 15, 15, 15, 15],
    ["3D aux", :y, 15, 15, 15, 15],
    ["3D aux", :w, 12, 12, 0, 12],
    ["3D aux", :pkin, 15, 15, 15, 15],
    ["3D aux", :wz0, 12, 12, 0, 12],
    ["3D aux", "uᵈ[1]", 12, 12, 0, 12],
    ["3D aux", "uᵈ[2]", 0, 0, 0, 0],
    ["3D aux", "ΔGᵘ[1]", 11, 11, 0, 12],
    ["3D aux", "ΔGᵘ[2]", 0, 0, 0, 0],
    ["2D state", :η, 12, 12, 0, 12],
    ["2D state", "U[1]", 12, 12, 0, 12],
    ["2D state", "U[2]", 0, 0, 0, 0],
    ["2D aux", :y, 15, 15, 15, 2],
    ["2D aux", "Gᵁ[1]", 11, 11, 0, 12],
    ["2D aux", "Gᵁ[2]", 0, 0, 0, 0],
    ["2D aux", "Δu[1]", 12, 12, 0, 12],
    ["2D aux", "Δu[2]", 0, 0, 0, 0],
]

uncoupled = [
    [
        "3D state",
        "u[1]",
        -9.58547428246830479637e-01,
        9.58547428246829258391e-01,
        -6.82121026329696195717e-18,
        4.45400655325317695876e-01,
    ],
    [
        "3D state",
        "u[2]",
        -4.13574641907576004779e-14,
        1.84325494853789287544e-14,
        1.94567057477529826177e-17,
        2.12815907156924710074e-15,
    ],
    [
        "3D state",
        :η,
        -8.52721734395889496838e-01,
        8.52834259382379111791e-01,
        2.18869899981655176688e-14,
        6.02983573168680453414e-01,
    ],
    [
        "3D state",
        :θ,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "3D aux",
        :w,
        -4.04489222828922911912e-04,
        4.04649979048336849198e-04,
        7.79376563286859913379e-19,
        1.63949585575251901345e-04,
    ],
    [
        "3D aux",
        :pkin,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :wz0,
        -2.00933099660541601228e-04,
        2.00809859469744893647e-04,
        2.62012633811536956698e-19,
        1.42064682697776552755e-04,
    ],
    [
        "3D aux",
        "uᵈ[1]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        "uᵈ[2]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        "ΔGᵘ[1]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        "ΔGᵘ[2]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "2D state",
        :η,
        -8.52715894965927923010e-01,
        8.52861218874714666072e-01,
        -7.70938868299708696148e-16,
        6.03458382295490314284e-01,
    ],
    [
        "2D state",
        "U[1]",
        -3.15423945317149438949e+01,
        3.15423945317147342848e+01,
        1.42724412149908289268e-14,
        2.24262219407601044452e+01,
    ],
    [
        "2D state",
        "U[2]",
        -9.38673083374999972374e-13,
        1.16534781393478256382e-12,
        1.93385973223830695204e-14,
        2.16634164433978071172e-13,
    ],
    [
        "2D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "2D aux",
        "Gᵁ[1]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "2D aux",
        "Gᵁ[2]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "2D aux",
        "Δu[1]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "2D aux",
        "Δu[2]",
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
]

coupled = [
    [
        "3D state",
        "u[1]",
        -9.58544854429326798062e-01,
        9.58544854429326798062e-01,
        -4.09272615797817732838e-17,
        4.45400401208546903309e-01,
    ],
    [
        "3D state",
        "u[2]",
        -4.09119364541065205420e-15,
        3.41954410407034240802e-15,
        1.57855184364031418231e-19,
        7.69982545727116047928e-16,
    ],
    [
        "3D state",
        :η,
        -8.52733075123627615177e-01,
        8.52843573070954374948e-01,
        7.27595761418342649852e-17,
        6.02992194663175995473e-01,
    ],
    [
        "3D state",
        :θ,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "3D aux",
        :w,
        -4.04456725919283483547e-04,
        4.04616610112968017130e-04,
        1.33892896769793873666e-18,
        1.63945200913093809460e-04,
    ],
    [
        "3D aux",
        :pkin,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :wz0,
        -2.00813442787548966564e-04,
        2.00686991710668984502e-04,
        1.68642877440561282675e-18,
        1.41979510156587984725e-04,
    ],
    [
        "3D aux",
        "uᵈ[1]",
        -8.79713597641291755735e-01,
        8.79713597641291200624e-01,
        -9.26547727431170607429e-17,
        4.41871673548253185437e-01,
    ],
    [
        "3D aux",
        "uᵈ[2]",
        -5.88884665747485312740e-28,
        6.47654803186450692243e-28,
        1.40221555861782666734e-30,
        4.22950073030020620100e-29,
    ],
    [
        "3D aux",
        "ΔGᵘ[1]",
        -1.50292385646252865781e-09,
        1.50292385640701573972e-09,
        -3.03178502810731714103e-21,
        6.72141424487058536370e-10,
    ],
    [
        "3D aux",
        "ΔGᵘ[2]",
        -2.50330922252795548175e-19,
        2.79421319642066487612e-19,
        8.87468518373638336981e-36,
        5.19465794381941235415e-20,
    ],
    [
        "2D state",
        :η,
        -8.52733075123627615177e-01,
        8.52843573070954374948e-01,
        1.61115565333602722195e-16,
        6.03463098440266798583e-01,
    ],
    [
        "2D state",
        "U[1]",
        -3.15402749945522060671e+01,
        3.15402749945527389741e+01,
        2.12974776703234167348e-14,
        2.24262621677375086904e+01,
    ],
    [
        "2D state",
        "U[2]",
        -1.63647745816416607002e-12,
        1.36781764162808076476e-12,
        6.31420737450488849608e-17,
        3.08233543917742288569e-13,
    ],
    [
        "2D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "2D aux",
        "Gᵁ[1]",
        -6.01169542562806285963e-07,
        6.01169542585011466433e-07,
        1.21270571032004377760e-18,
        2.69066532005499711718e-07,
    ],
    [
        "2D aux",
        "Gᵁ[2]",
        -1.11768527856826595815e-16,
        1.00132368901118217729e-16,
        -3.50057026691823957451e-33,
        2.07948587451660775232e-17,
    ],
    [
        "2D aux",
        "Δu[1]",
        -6.53936429129693404076e-04,
        6.53936429129569046087e-04,
        -2.70378337774435083119e-18,
        4.64975945389895016328e-04,
    ],
    [
        "2D aux",
        "Δu[2]",
        -3.76049176138251460297e-17,
        3.57641761985933683411e-17,
        -4.12443309266433447246e-19,
        7.09162842149286857838e-18,
    ],
]

thirty_minutes = [
    [
        "3D state",
        "u[1]",
        -9.58527737426365766815e-01,
        9.58527737426364989659e-01,
        -2.95585778076201651428e-17,
        4.45397585011924779241e-01,
    ],
    [
        "3D state",
        "u[2]",
        -3.91776258115697290002e-15,
        3.71573344368902785991e-15,
        9.99453214450878842160e-18,
        7.55533641450465537744e-16,
    ],
    [
        "3D state",
        :η,
        -8.52729582431237531637e-01,
        8.52830944991449846349e-01,
        -2.54658516496419884307e-16,
        6.02990141900936249542e-01,
    ],
    [
        "3D state",
        :θ,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "3D aux",
        :w,
        -4.06712181304362465229e-04,
        4.06869755985816939844e-04,
        9.07052211118752913371e-19,
        1.64290385445136678539e-04,
    ],
    [
        "3D aux",
        :pkin,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :wz0,
        -2.09003099782708142351e-04,
        2.08868143992394606845e-04,
        1.33781874467331362536e-18,
        1.47768608464728741676e-04,
    ],
    [
        "3D aux",
        "uᵈ[1]",
        -8.79792191807299950312e-01,
        8.79792191807298729067e-01,
        -9.54969436861574689412e-17,
        4.41911149114936674387e-01,
    ],
    [
        "3D aux",
        "uᵈ[2]",
        -4.49650715975976729085e-28,
        3.22939933074851707840e-28,
        1.28559371735991545201e-30,
        3.12162656228539824003e-29,
    ],
    [
        "3D aux",
        "ΔGᵘ[1]",
        -1.52356619979685413105e-09,
        1.52356619973800994984e-09,
        -3.00415481336788178489e-21,
        6.81373145706173027634e-10,
    ],
    [
        "3D aux",
        "ΔGᵘ[2]",
        -2.17115289587171316651e-19,
        2.99271337844227832879e-19,
        3.57452597678270976423e-36,
        5.23982958192948877053e-20,
    ],
    [
        "2D state",
        :η,
        -8.52729582431237531637e-01,
        8.52830944991449846349e-01,
        -3.20454773827805192348e-16,
        6.03461044074932395631e-01,
    ],
    [
        "2D state",
        "U[1]",
        -3.15407643291233448224e+01,
        3.15407643291237640426e+01,
        2.30678864898692384736e-14,
        2.24265574817805202201e+01,
    ],
    [
        "2D state",
        "U[2]",
        -1.56710503246274425174e-12,
        1.48629337747559491236e-12,
        3.99781285780299048032e-15,
        3.02449468686897625244e-13,
    ],
    [
        "2D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "2D aux",
        "Gᵁ[1]",
        -6.09426479895203986555e-07,
        6.09426479918741655731e-07,
        1.20165633492970096897e-18,
        2.72762104279987029506e-07,
    ],
    [
        "2D aux",
        "Gᵁ[2]",
        -1.19708535137691129300e-16,
        8.68461158348685235787e-17,
        -1.50869648123518501517e-33,
        2.09756864038773538444e-17,
    ],
    [
        "2D aux",
        "Δu[1]",
        -3.89449877638695009935e-03,
        3.89449877638627485824e-03,
        -2.02396849009311295664e-17,
        2.76903473119681107703e-03,
    ],
    [
        "2D aux",
        "Δu[2]",
        -2.13991366909841745553e-16,
        2.02991354131742782465e-16,
        -1.26781868574011981865e-18,
        4.34606820130057938426e-17,
    ],
]

sixty_minutes = [
    [
        "3D state",
        "u[1]",
        -9.58507705628042327994e-01,
        9.58507705628042550039e-01,
        -5.22959453519433752618e-17,
        4.45394100308371843067e-01,
    ],
    [
        "3D state",
        "u[2]",
        -4.09893498131451878178e-15,
        3.66595063838075513859e-15,
        3.52058149555652693147e-18,
        7.65972478919525466697e-16,
    ],
    [
        "3D state",
        :η,
        -8.52715753705294066123e-01,
        8.52819886810596838878e-01,
        0.00000000000000000000e+00,
        6.02986351734545844572e-01,
    ],
    [
        "3D state",
        :θ,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "3D aux",
        :w,
        -4.09355343664679748733e-04,
        4.09526445665209497954e-04,
        8.01581023779363006131e-19,
        1.64782726593626366188e-04,
    ],
    [
        "3D aux",
        :pkin,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :wz0,
        -2.18597874938533713813e-04,
        2.18512725277962983460e-04,
        1.16684439888103955116e-18,
        1.54581525183602542031e-04,
    ],
    [
        "3D aux",
        "uᵈ[1]",
        -8.79886113674857694988e-01,
        8.79886113674856806810e-01,
        -8.81072992342524199517e-17,
        4.41958323665245178535e-01,
    ],
    [
        "3D aux",
        "uᵈ[2]",
        -5.04476548888837049561e-28,
        4.09418809809705127009e-28,
        1.53142189509630563206e-30,
        3.27579623353337388378e-29,
    ],
    [
        "3D aux",
        "ΔGᵘ[1]",
        -1.12461707299293835875e-09,
        1.12461707291060059378e-09,
        -2.89803852573586336546e-21,
        5.02954103857782471789e-10,
    ],
    [
        "3D aux",
        "ΔGᵘ[2]",
        -2.29220407034248886795e-19,
        3.23795504633739099693e-19,
        4.19082355898662551731e-36,
        5.50021671320874203932e-20,
    ],
    [
        "2D state",
        :η,
        -8.52715753705294066123e-01,
        8.52819886810596838878e-01,
        -2.04281036531028799987e-17,
        6.03457250948630341547e-01,
    ],
    [
        "2D state",
        "U[1]",
        -3.15415180428826182890e+01,
        3.15415180428829948767e+01,
        1.77902803599749859788e-14,
        2.24265293235027023400e+01,
    ],
    [
        "2D state",
        "U[2]",
        -1.63957399252576030728e-12,
        1.46638025535227397199e-12,
        1.40823259822193190848e-15,
        3.06628264537953242560e-13,
    ],
    [
        "2D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "2D aux",
        "Gᵁ[1]",
        -4.49846829164240224277e-07,
        4.49846829197175353425e-07,
        1.15921837490966064206e-18,
        2.01338753352722549629e-07,
    ],
    [
        "2D aux",
        "Gᵁ[2]",
        -1.29518201853495637566e-16,
        9.16881628136995516365e-17,
        -2.08062063752041870574e-33,
        2.20180483211723137251e-17,
    ],
    [
        "2D aux",
        "Δu[1]",
        -7.71677156339491132631e-03,
        7.71677156339359640591e-03,
        -2.97775205101297201914e-17,
        5.48673197909435757247e-03,
    ],
    [
        "2D aux",
        "Δu[2]",
        -5.01794629524824969143e-16,
        5.51655281127317078031e-16,
        3.20748218455203547025e-18,
        9.36761976343254920594e-17,
    ],
]

ninety_minutes = [
    [
        "3D state",
        "u[1]",
        -9.58488051495743564878e-01,
        9.58488051495744008967e-01,
        -2.72848410531878478287e-17,
        4.45390687454859546257e-01,
    ],
    [
        "3D state",
        "u[2]",
        -4.05044403917298945083e-15,
        3.37351039462521829098e-15,
        2.85851376495618499592e-18,
        7.53889134369023261669e-16,
    ],
    [
        "3D state",
        :η,
        -8.52731405452108237597e-01,
        8.52809353848989259994e-01,
        -1.36424205265939223736e-16,
        6.02988963894262375298e-01,
    ],
    [
        "3D state",
        :θ,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "3D aux",
        :w,
        -4.11959288170114497438e-04,
        4.12126623290113712657e-04,
        8.97615315409439082099e-19,
        1.65355949740610678371e-04,
    ],
    [
        "3D aux",
        :pkin,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
        0.00000000000000000000e+00,
    ],
    [
        "3D aux",
        :wz0,
        -2.28048874140054377109e-04,
        2.27949849600823879541e-04,
        1.39666056497844683559e-18,
        1.61271245461141397555e-04,
    ],
    [
        "3D aux",
        "uᵈ[1]",
        -8.79979607451059520073e-01,
        8.79979607451058964962e-01,
        -9.32232069317251423826e-17,
        4.42005283454923125763e-01,
    ],
    [
        "3D aux",
        "uᵈ[2]",
        -5.48652759581213710664e-28,
        3.92852730800063879095e-28,
        1.36055159257466689218e-30,
        3.58669418355622340063e-29,
    ],
    [
        "3D aux",
        "ΔGᵘ[1]",
        -8.86991425948811828542e-10,
        8.86991425888052000430e-10,
        -2.87311034609816924955e-21,
        3.96682558396691740283e-10,
    ],
    [
        "3D aux",
        "ΔGᵘ[2]",
        -2.66880587297174792697e-19,
        3.32595187063539502433e-19,
        3.45126646034192634633e-36,
        5.71491963745139874533e-20,
    ],
    [
        "2D state",
        :η,
        -8.52731405452108237597e-01,
        8.52809353848989259994e-01,
        -1.20259358027396949872e-16,
        6.03459865148300300675e-01,
    ],
    [
        "2D state",
        "U[1]",
        -3.15423820746578229546e+01,
        3.15423820746582954655e+01,
        2.75259190440912945341e-14,
        2.24266774102465760166e+01,
    ],
    [
        "2D state",
        "U[2]",
        -1.62017761566911967103e-12,
        1.34940415784996204528e-12,
        1.14340550598186518880e-15,
        3.01791152146446424566e-13,
    ],
    [
        "2D aux",
        :y,
        0.00000000000000000000e+00,
        1.00000000000000011642e+06,
        5.00000000000000000000e+05,
        2.92775877460665535182e+05,
    ],
    [
        "2D aux",
        "Gᵁ[1]",
        -3.54796570355220815061e-07,
        3.54796570379524731417e-07,
        1.14925108410943513278e-18,
        1.58796938272805492033e-07,
    ],
    [
        "2D aux",
        "Gᵁ[2]",
        -1.33038074825415802514e-16,
        1.06752234918869909375e-16,
        -1.45939267465887190460e-33,
        2.28775307028972280128e-17,
    ],
    [
        "2D aux",
        "Δu[1]",
        -1.14629123365995507638e-02,
        1.14629123365980502280e-02,
        -3.95057006856089328375e-17,
        8.15149713200660763768e-03,
    ],
    [
        "2D aux",
        "Δu[2]",
        -7.52467366304891760665e-16,
        5.94961001279223574512e-16,
        6.25849006198159897312e-19,
        1.42516297748358802032e-16,
    ],
]

refVals = (
    uncoupled = (uncoupled, parr),
    coupled = (coupled, parr),
    thirty_minutes = (thirty_minutes, parr),
    sixty_minutes = (sixty_minutes, parr),
    ninety_minutes = (ninety_minutes, parr),
)

max_stars_repo_name: mwarusz/CLIMA

max_issues_repo_name: mwarusz/CLIMA

max_forks_repo_name: mwarusz/CLIMA


text:
@block SimonDanisch [recipe] begin

    @cell "Type recipe for molecule simulation" [meshscatter, linesegments, molecules] begin
        import AbstractPlotting: Plot, default_theme, plot!, to_value

        struct Simulation
            grid::Vector{Point3f0}
        end
        # Probably worth having a macro for this!
        function default_theme(scene::SceneLike, ::Type{<: Plot(Simulation)})
            Theme(
                advance = 0,
                molecule_sizes = [0.08, 0.04, 0.04],
                molecule_colors = [:maroon, :deepskyblue2, :deepskyblue2]
            )
        end

        # The recipe! - will get called for plot(!)(x::SimulationResult)
        function AbstractPlotting.plot!(p::Plot(Simulation))
            sim = to_value(p[1]) # first argument is the SimulationResult
            # when advance changes, get new positions from the simulation
            mpos = lift(p[:advance]) do i
                sim.grid .+ rand(Point3f0, length(sim.grid)) .* 0.01f0
            end
            # size shouldn't change, so we might as well get the value instead of signal
            pos = to_value(mpos)
            N = length(pos)
            sizes = lift(p[:molecule_sizes]) do s
                repeat(s, outer = N ÷ 3)
            end
            sizes = lift(p[:molecule_sizes]) do s
                repeat(s, outer = N ÷ 3)
            end
            colors = lift(p[:molecule_colors]) do c
                repeat(c, outer = N ÷ 3)
            end
            scene = meshscatter!(p, mpos, markersize = sizes, color = colors)
            indices = Int[]
            for i in 1:3:N
                push!(indices, i, i + 1, i, i + 2)
            end
            meshplot = p.plots[end] # meshplot is the last plot we added to p
            # meshplot[1] -> the positions (first argument) converted to points, so
            # we don't do the conversion 2 times for linesegments!
            linesegments!(p, lift(x-> view(x, indices), meshplot[1]))
        end

        # To write out a video of the whole simulation
        using Makie
        n = 5
        r = range(-1, stop = 1, length = n)
        grid = Point3f0.(r, reshape(r, (1, n, 1)), reshape(r, (1, 1, n)))
        molecules = map(1:(n^3) * 3) do i
            i3 = ((i - 1) ÷ 3) + 1
            xy = 0.1; z = 0.08
            i % 3 == 1 && return grid[i3]
            i % 3 == 2 && return grid[i3] + Point3f0(xy, xy, z)
            i % 3 == 0 && return grid[i3] + Point3f0(-xy, xy, z)
        end
        result = Simulation(molecules)
        scene = plot(result)
        N = 100
        record(scene, @replace_with_a_path(mp4), 1:N) do i
            scene[end][:advance] = i
        end
    end
end

#
#
# # or a slider example
# scene = Scene()
# # the whole layouting is still horrible - which is next on the todo list!
# ui_scene = Scene(scene, lift(x-> IRect(0, 0, widths(x)[1], 50), pixelarea(scene)))
# campixel!(ui_scene)
# plot_scene = Scene(scene, lift(x-> IRect(0, 50, widths(x) .- Vec(0, 50)), pixelarea(scene)))
# sim = plot!(plot_scene, result)[end]
# sim_index = slider!(ui_scene, 1:length(result.solution.t), raw = true)[end]
# foreach(sim_index[:value]) do idx
#     sim[:simulation_index] = idx
# end
# scene
#
# #the above should soon become:
# sim = plot(result)
# slider = slider(1:N)
# foreach(slider) do idx
#     sim[end][:simulation_index] = idx
# end
# hbox(sim, slider)


text:
using ModelicaScriptingTools: withOMC, testmodel, installAndLoad
using Base.Filesystem
using Test
using OMJulia: sendExpression

outdir = joinpath(@__DIR__, "../out")
modeldir = joinpath(@__DIR__, "..")
refdir = joinpath(@__DIR__, "../regRefData")

# create outdir and move working directory there to capture OMC outputs
if !ispath(outdir)
    mkdir(outdir)
end

withOMC(outdir, modeldir) do omc
    sendExpression(omc, "setCommandLineOptions(\"-d=newInst,nfAPI\")")
    installAndLoad(omc, "Modelica"; version="3.2.3")
    @testset "Simulate examples" begin
        @testset "HHmono" begin
            testmodel(omc, "HHmodelica.CompleteModels.HHmono"; refdir=refdir)
        end
        @testset "HHmodHier" begin
            testmodel(omc, "HHmodelica.CompleteModels.HHmodHier"; refdir=refdir)
        end
        @testset "HHmodular" begin
            testmodel(omc, "HHmodelica.CompleteModels.HHmodular"; refdir=refdir)
        end
        @testset "HHmodular1p" begin
            testmodel(omc, "HHmodelica.CompleteModels.HHmodular1p"; refdir=refdir)
        end
    end
end

max_stars_repo_name: CSchoel/hh-modelica

max_issues_repo_name: CSchoel/hh-modelica

max_forks_repo_name: CSchoel/hh-modelica


text:
# follow.jl
## https://developer.spotify.com/documentation/web-api/reference/follow/

#### GET ####

## https://developer.spotify.com/documentation/web-api/reference/follow/check-current-user-follows/
@doc """
# Check if Current User Follows Artists or Users
**Summary**: Check to see if the current user is following one or more artists or other Spotify Users

`type` _Required_: The ID type, either `artist` or `user`.\n
`ids` _Required_: A comma separated list of the artist or user Spotify IDS to check. Maximum 50.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/check-current-user-follows/)
""" ->
function follow_check(type, ids)
    return spotify_request("me/following/contains?type=$type&ids=$ids"; scope = "user-follow-read")
end

## https://developer.spotify.com/documentation/web-api/reference/follow/check-user-following-playlist/
@doc """
# Check if Users Follow a Playlist
**Summary**: Check to see if one or more Spotify users are following a specified playlist_id.\n

`playlist_id` _Required_: The Spotify ID of the playlist.\n
`ids` _Required_: A comma separated list of the user Spotify IDS to check. Maximum 5.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/check-user-following-playlist/)
""" ->
function follow_check_playlist(playlist_id, ids)
    return spotify_request("playlists/$playlist_id/followers/contains?ids=$ids"; scope = "playlist-read-private")
end

## https://developer.spotify.com/documentation/web-api/reference/follow/get-followed/
@doc """
# Get User's Followed Artists
**Summary**: Get the current user's followed artists.

`type` _Required_: The ID type. Currently only `artist` is supported. Default `artist`.\n
`limit` _Optional_: The maximum number of items to return. Default 20, Minimum 1, Maximum 50. \n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/get-followed/)
""" ->
function follow_artists(type="artist", limit=20)
    return spotify_request("me/following?type=$type"; scope = "user-follow-modify")
end

#### PUT ####

## https://developer.spotify.com/documentation/web-api/reference/follow/follow-artists-users/
@doc """
# Follow Artists or Users
**Summary**: Add the current user as a follower of one or more artists or other Spotify users.\n

`type` _Required_: The ID type: either `artist` or `user`. \n
`ids` _Required_: A comma-separated list of the artists or users Spotify IDs. Maximum 50.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/follow-artists-users/)
""" ->
function follow_artists_users(type, ids)
    return spotify_request("me/following?type=$type&ids=$ids", method="PUT"; scope = "user-follow-modify")
end

## https://developer.spotify.com/documentation/web-api/reference/follow/follow-playlist/
@doc """
# Follow a Playlist
**Summary**: Add the currend user as a follower of a playlist. \n

`playlist_id` _Required_: The Spotify ID of the playlist. Any playlist can be followed regardless of it's private/public status, as long as the ID is known.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/follow-playlist/)
""" ->
function follow_playlist(playlist_id)
    return spotify_request("playlists/$playlist_id/followers", method="PUT"; scope = "user-follow-modify")
end

#### DELETE ####

## https://developer.spotify.com/documentation/web-api/reference/follow/unfollow-artists-users/
@doc """
# Unfollow Artists or Users
**Summary**: Remove the current user as a follower of one or more artists or other Spotify users.\n

`type` _Required_: The ID type: either `artist` or `user`. \n
`ids` _Required_: A comma-separated list of the artists or users Spotify IDs. Maximum 50.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/unfollow-artists-users/)
""" ->
function unfollow_artists_users(type, ids)
    return spotify_request("me/following?type=$type&ids=$ids", method="DELETE"; scope = "user-follow-modify")
end

## https://developer.spotify.com/documentation/web-api/reference/follow/unfollow-playlist/
@doc """
# Unfollow a Playlist
**Summary**: Remove the current user as a follower of a playlist.\n

`playlist_id` _Required_: The Spotify ID of the playlist. Any playlist can be followed regardless of it's private/public status, as long as the ID is known.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/follow/unfollow-playlist/)
""" ->
function unfollow_playlist(playlist_id)
    return spotify_request("playlists/$playlist_id/followers", method="DELETE"; scope = "playlist-modify-private")
end


text:
"""
    Allof(iterables...)
combine the iterables into a single iterable.
"""
struct AllOf
    length::Int
    lengths
    iterables

    function AllOf(iterables...)
        lengths = length.(iterables)
        new(sum(lengths), lengths, iterables)
    end
end

function Base.length(x::AllOf)::Int
    x.length
end

Base.firstindex(x::AllOf) = 1

Base.lastindex(x::AllOf) = x.length

function Base.getindex(x::AllOf, index::Int)
    if index < 1
	throw(BoundsError(x, index))
    end
    idx = index
    for i in 1:length(x.lengths)
        if idx <= x.lengths[i]
            return x.iterables[i][idx]
        end
        idx -= x.lengths[i]
    end
    throw(BoundsError(x, index))
end

Base.iterate(iter::AllOf) = iterate(iter, firstindex(iter))

function Base.iterate(iter::AllOf, state::Int)
    if state > lastindex(iter)
        return
    end
    return iter[state], state + 1
end

export AllOf


text:
using FFTW

export TwoDPoissonPeriodic

"""
    TwoDPoissonPeriodic

Derived type to solve the Poisson equation on 2d regular
cartesian mesh with periodic boundary conditions on both sides
- kx   : wave number in x
- ky   : wave number in y
- k2   : ``k_x^2 + k_y^2``
- nc_x : cells number in x
- nc_y : cells number in y
- dx   : x step size
- dy   : y step size
- rht  : fft(rho)
"""
struct TwoDPoissonPeriodic

    grid::TwoDGrid
    kx::Array{Float64,2}
    ky::Array{Float64,2}
    k2::Array{Float64,2}
    rht::Array{ComplexF64,2}

    function TwoDPoissonPeriodic(grid::TwoDGrid)

        nc_x = grid.nx
        nc_y = grid.ny

        rht = zeros(ComplexF64, (div(nc_x, 2) + 1, nc_y))

        kx = zeros(nc_x ÷ 2 + 1, nc_y)
        ky = zeros(nc_x ÷ 2 + 1, nc_y)
        k2 = zeros(nc_x ÷ 2 + 1, nc_y)

        kx0 = 2π / grid.dimx
        ky0 = 2π / grid.dimy

        for ik = 1:nc_x÷2+1
            kx1 = (ik - 1) * kx0
            for jk = 1:nc_y÷2
                kx[ik, jk] = kx1
                ky[ik, jk] = (jk - 1) * ky0
            end
            for jk = nc_y÷2+1:nc_y
                kx[ik, jk] = kx1
                ky[ik, jk] = (jk - 1 - nc_y) * ky0
            end
        end

        kx[1, 1] = 1.0
        k2 .= kx .* kx .+ ky .* ky
        kx .= kx ./ k2
        ky .= ky ./ k2

        new(grid, kx, ky, k2, rht)

    end

end

export solve!

"""
    solve!( poisson, phi, rho )

computes `phi` from `rho`

```math
-\\Delta phi(x,y) = rho(x,y)
```

"""
function solve!(phi, poisson::TwoDPoissonPeriodic, rho)

    poisson.rht .= rfft(rho)
    poisson.rht ./= poisson.k2
    phi .= irfft(poisson.rht, poisson.grid.nx)

end

"""
    solve!( ex, ey, poisson, rho )

solves Poisson equation to compute electric fields

```math
E(x,y) = -\\nabla \\phi(x,y) \\\\
-\\Delta \\phi(x,y) = \\rho(x,y)
```

"""
function solve!(ex, ey, poisson::TwoDPoissonPeriodic, rho)

    poisson.rht .= rfft(rho)

    poisson.rht[1, 1] = 0.0
    poisson.rht .*= -1im .* poisson.kx

    ex .= irfft(poisson.rht, poisson.grid.nx)

    poisson.rht .= rfft(rho)

    poisson.rht[1, 1] = 0.0
    poisson.rht .*= -1im .* poisson.ky

    ey .= irfft(poisson.rht, poisson.grid.nx)

end

export TwoDPoissonPIC

"""
    TwoDPoissonPIC( poisson, kernel )

- kernel : Kernel smoother taking care of charge deposition and field evaluation
- poisson : Poisson solver
- rho_dofs : Coefficients of expansion of rho (MPI global version)
- efield_dofs : Coefficients of expansion of electric field
- phi_dofs : Coefficients of expansion of potential
- rho2d : 2d version of rho_dofs to adjust to field solver format
- efield : 2d version of efield_dofs to adjust to field solver format
- phi2d : 2d version of phi_dofs to adjust to field solver format
"""
struct TwoDPoissonPIC

    ndofs::Int
    kernel::ParticleMeshCoupling2D
    poisson::TwoDPoissonPeriodic
    rho_dofs::Vector{Float64}
    efield_dofs::Vector{Vector{Float64}}
    phi_dofs::Vector{Float64}
    rho2d::Array{Float64,2}
    efield::Vector{Array{Float64,2}}
    phi2d::Array{Float64,2}

    function TwoDPoissonPIC(poisson::TwoDPoissonPeriodic, kernel::ParticleMeshCoupling2D)

        nx, ny = poisson.grid.nx, poisson.grid.ny
        ndofs = nx * ny

        rho_dofs = zeros(ndofs)
        rho_dofs_local = zeros(ndofs)
        rho_analyt_dofs = zeros(ndofs)
        efield_dofs = [zeros(ndofs), zeros(ndofs)]
        phi_dofs = zeros(ndofs)
        rho2d = zeros(nx, ny)
        efield = [zeros(nx, ny), zeros(nx, ny)]
        phi2d = zeros(nx, ny)

        new(ndofs, kernel, poisson, rho_dofs, efield_dofs, phi_dofs, rho2d, efield, phi2d)

    end

end

"""
    add_charge!(pic, position, marker_charge)

Add charge from one particle
- self : Pic Poisson solver object
- position : Position of the particle
- marker_charge : Particle weight times charge
"""
function add_charge!(pic::TwoDPoissonPIC, position, marker_charge)

    xp, yp = position

    GEMPIC.add_charge!(pic.rho_dofs, pic.kernel, xp, yp, marker_charge)

end

"""
    evaluate_rho!(pic, position)

Evaluate charge density at rho at one position
- self : Pic Poisson solver object
- position : Position of the particle
- func_value : Value of rho at given position
"""
function evaluate_rho!(pic, position)

    GEMPIC.evaluate!(kernel, position, pic.rho_dofs)

end

"""
    evaluate_phi!(pic, position)

Evaluate potential at one position
- self : Pic Poisson solver object
- position : Position of the particle
- func_value : Value of phi at given position
"""
function evaluate_rho(pic, position)

    GEMPIC.evaluate!(kernel, position, pic.phi_dofs)

end

"""
    solve!( pic )

Solve for phi and fields
- poisson : Pic Poisson solver object
"""
function solve!(pic :: TwoDPoissonPIC)

    solve_phi!(pic)
    solve_fields!(pic)

end

"""
    solve_phi!( pic )

Solve for potential
"""
function solve_phi!(pic :: TwoDPoissonPIC)

    nx, ny = pic.poisson.grid.nx, pic.poisson.grid.ny
    pic.rho2d .= reshape(pic.rho_dofs, nx, ny)
    solve!(pic.phi2d, pic.poisson, pic.rho2d)
    pic.phi_dofs .= vec(pic.phi2d)

end

"""
    solve_fields!( pic )

Solve efields from rho
"""
function solve_fields!(pic :: TwoDPoissonPIC)

    solve!(pic.efield[1], pic.efield[2], pic.poisson, pic.rho2d)

    pic.efield_dofs[1] .= vec(pic.efield[1])
    pic.efield_dofs[2] .= vec(pic.efield[2])

end

export SplittingOperator

"""
Operator splitting type for 2d2v Vlasov-Poisson
- pic :: PIC poisson solver
- pg :: Particle group
"""
struct SplittingOperator

    pic::TwoDPoissonPIC
    pg::ParticleGroup

end

"""
    operator_t(split, dt)
Push x
- split :: time splitting object
- dt   :: time step
"""
function operator_t!(split::SplittingOperator, dt)

    # x_new = x_old + dt * v

    for i_part = 1:split.pg.n_particles
        split.pg.array[1, i_part] += dt * split.pg.array[3, i_part]
        split.pg.array[2, i_part] += dt * split.pg.array[4, i_part]

    end

end

function operator_v!(split::SplittingOperator, dt)

    # v_new = v_old + dt * q/m * E

    qm = split.pg.q_over_m

    for i_part = 1:split.pg.n_particles

        # Evaluate efields at particle position

        xi = split.pg.array[1, i_part]
        yi = split.pg.array[2, i_part]

        ex, ey = GEMPIC.evaluate_multiple(split.pic.kernel, (xi, yi), split.pic.efield_dofs)

        vx_new = split.pg.array[3, i_part]
        vy_new = split.pg.array[4, i_part]

        vx_new += dt * qm * ex
        vy_new += dt * qm * ey

        split.pg.array[3, i_part] = vx_new
        split.pg.array[4, i_part] = vy_new

    end

end

export charge_deposition!

function charge_deposition!(split::SplittingOperator)

    fill!(split.pic.rho_dofs, 0.0)
    for i_part = 1:split.pg.n_particles
        xp = split.pg.array[1, i_part]
        yp = split.pg.array[2, i_part]
        wp = split.pg.array[5, i_part]
        GEMPIC.add_charge!(split.pic.rho_dofs, split.pic.kernel, xp, yp, wp)
    end
    nx, ny = split.pic.poisson.grid.nx, split.pic.poisson.grid.ny
    split.pic.rho2d .= reshape(split.pic.rho_dofs, nx, ny)

end

export solve_fields!

"""
Solve Poisson's equation for the electric field
"""
solve_fields!(split::SplittingOperator) = solve_fields!(split.pic)

export strang_splitting!

"""
Strang splitting
- split :: time splitting object
- dt   :: time step
"""
function strang_splitting!(split::SplittingOperator, dt)

    operator_t!(split, 0.5dt)
    charge_deposition!(split)
    solve_fields!(split)
    operator_v!(split, dt)
    operator_t!(split, 0.5dt)

end

export compute_field_energy

"""
    compute_field_energy

Compute the squared l2 norm of electric field
"""
compute_field_energy(pic::TwoDPoissonPIC, component::Int) =
    sum(pic.efield_dofs[component] .^ 2) * pic.poisson.grid.dx * pic.poisson.grid.dy


text:
using Symbolics
using Test

@variables x, y, z

@test isequal(degree(1), 0)
@test isequal(degree(x), 1)
@test isequal(degree(x, x), 1)
@test isequal(degree(x, y), 0)

@test isequal(degree(x*y, y), 1)
@test isequal(degree(x*y, x), 1)
@test isequal(degree(x*y, x*y), 1)
@test isequal(degree(x*y, z), 0)

@test isequal(degree(x*y^2+2*x+y^3*x^3), 6)
@test isequal(degree(x*y^2+2*x+y^3*x^3, y), 3)
@test isequal(degree(x*y^2+2*x+y^3*x^6, x), 6)

@test isequal(degree(x*y^2+2*x+y^3*x^6, z), 0)
@test isequal(degree(x*y^2+2*x+y^3*x^6+z, z), 1)
@test isequal(degree(x*y^2+2*x+y^3*x^6*z, z), 1)

@test isequal(degree(x^2 + y^2 +z^2, sin(z)), 0)
@test isequal(degree(sin(z), z), 0)
@test isequal(degree(sin(z)), 1)

@test isequal(degree(x^2*sin(z), x), 2)
@test isequal(degree(x+exp(z), x), 1)

@test isequal(degree((x - y)^2*((y + x*y)^3)), 8)
@test isequal(degree((x + z)*((y + x*y)^3), x), 4)


text:
using GenomeLoader
using Test

@testset "GenomeLoader.jl" begin
    # Write your own tests here.
end


text:
# Typical usage: run `generate_artifacts.jl . --deploy` from within the directory that contains the files
using Pkg.Artifacts, LibGit2, ghr_jll

deploy = true
if deploy && !haskey(ENV, "GITHUB_TOKEN")
    error("For automatic github deployment, export GITHUB_TOKEN!")
end

data_dir = @__DIR__

if deploy
    # Where we will put our tarballs
    tempdir = mktempdir()

    function get_git_remote_url(repo_path::String)
        repo = LibGit2.GitRepo(repo_path)
        origin = LibGit2.get(LibGit2.GitRemote, repo, "origin")
        return LibGit2.url(origin)
    end

    # Try to detect where we should upload these weights to (or just override
    # as shown in the commented-out line)
    origin_url = get_git_remote_url(dirname(@__DIR__))
    deploy_repo = "$(basename(dirname(origin_url)))/$(splitext(basename(origin_url))[1])"

    #deploy_repo = "staticfloat/ObjectDetector.jl"
    tag = "weights"
end

weightsfiles = filter(x->endswith(x,".weights"), readdir(data_dir))

for weightsfile in weightsfiles
    name = splitext(weightsfile)[1]
    @info("Generating artifact for $(weightsfile)")
    # Create a local artifact
    hash = create_artifact() do artifact_dir
        # Copy in weights
        cp(joinpath(data_dir, weightsfile), joinpath(artifact_dir, weightsfile))
    end

    # Spit tarballs to be hosted out to local temporary directory:
    if deploy
        tarball_hash = archive_artifact(hash, joinpath(tempdir, "$(name).tar.gz"))

        # Calculate tarball url
        tarball_url = "https://github.com/$(deploy_repo)/releases/download/$(tag)/$(name).tar.gz"

        # Bind this to an Artifacts.toml file
        @info("Binding $(name) in Artifacts.toml...")
        bind_artifact!(joinpath(@__DIR__, "..", "Artifacts.toml"), name, hash; download_info=[(tarball_url, tarball_hash)], lazy=true, force=true)
    end
end

if deploy
    # Upload tarballs to a special github release
    @info("Uploading tarballs to $(deploy_repo) tag `$(tag)`")
    ghr() do ghr_exe
        run(`$ghr_exe -replace -u $(dirname(deploy_repo)) -r $(basename(deploy_repo)) $(tag) $(tempdir)`)
    end

    @info("Artifacts.toml file now contains all bound artifact names")
end


text:
using ResumableFunctions
using DataStructures
import Base.deepcopy

VertexType = String
KeyType = Int64
EdgeType = Tuple{VertexType, VertexType}
MultiEdgeType = Tuple{VertexType, VertexType, KeyType}
AdjencyView = Dict{VertexType, Dict{VertexType, Set{KeyType}}}
WeightType = Float64
FlowType = Float64
FlowDictType = Dict{MultiEdgeType, FlowType}

mutable struct MultiDiGraph
    succ::AdjencyView
    pred::AdjencyView
end

function deepcopy(mdg::MultiDiGraph) :: MultiDiGraph
    return MultiDiGraph(Base.deepcopy(mdg.succ), Base.deepcopy(mdg.pred))
end

function _init_multidigraph(edges) :: MultiDiGraph
    mdg = MultiDiGraph(AdjencyView(), AdjencyView())
    for e in edges
        add_edge!(mdg, e)
    end
    return mdg
end

function fill_adjency_view(adj::AdjencyView, e::MultiEdgeType)
    if e[1] in keys(adj)
        if e[2] in keys(adj[e[1]])
            push!(adj[e[1]][e[2]], e[3])
        else
            adj[e[1]][e[2]] = Set{KeyType}([e[3]])
        end
        if ! (e[2] in keys(adj))
            adj[e[2]] = Dict{VertexType, Set{KeyType}}()
        end
    else
        adj[e[1]] = Dict{VertexType, Set{KeyType}}(e[2] => Set{KeyType}([e[3]]))
    end
end

MultiDiGraph(edges) = _init_multidigraph(edges)

function get_sub_graph(mdg::MultiDiGraph, sub_nodes::Set{VertexType}, from_new=true) :: MultiDiGraph
    if from_new
        sub_graph = MultiDiGraph(AdjencyView(), AdjencyView())
        for e in get_edges(mdg)
            if (e[1] in sub_nodes) && (e[2] in sub_nodes)
                add_edge!(sub_graph, e)
            end
        end
        return sub_graph
    else
        sub_graph = deepcopy(mdg)
        for u in setdiff(get_nodes(mdg), sub_nodes)
            remove_node!(sub_graph, u)
        end
        return sub_graph
    end
end

function get_edge_sub_graph(mdg::MultiDiGraph, edges, from_new=true) :: MultiDiGraph
    if from_new
        sub_graph = MultiDiGraph(AdjencyView(), AdjencyView())
        for e in edges
            add_edge!(sub_graph, e)
        end
        return sub_graph
    else
        sub_graph = deepcopy(mdg)
        for e in get_edges(mdg)
            if !(e in edges)
                remove_edge!(sub_graph, e)
            end
        end
        remove_isolates!(sub_graph)
        return sub_graph
    end
end

"""
Return set of nodes from an adjency dictionary.
"""
function get_nodes(mdg::MultiDiGraph)
    return keys(mdg.succ)
end

#=
Yields all edges in the given graph.
=#
@resumable function get_edges(mdg::MultiDiGraph)
    for (u, ns) in mdg.succ
        for (v, ks) in ns
            for k in ks
                @yield (u, v, k)
            end
        end
    end
end

#=
Yields an outgoing edge to the given node with the given adjency dictionary.
=#
@resumable function out_edges(mdg::MultiDiGraph, u::VertexType) :: Union{MultiEdgeType, Nothing}
    if u in keys(mdg.succ)
        for (v, ks) in mdg.succ[u]
            for k in ks
                @yield (u, v, k)
            end
        end
    end
end

#=
Yields an incoming edge to the given node with the given adjency dictionary.
=#
@resumable function in_edges(mdg::MultiDiGraph, v::VertexType) :: Union{MultiEdgeType, Nothing}
    if v in keys(mdg.pred)
        for (u, ks) in mdg.pred[v]
            for k in ks
                @yield (u, v, k)
            end
        end
    end
end

#=
Yields reachable edges in a DFS way from a start node.
=#
@resumable function edges_dfs(mdg::MultiDiGraph, start_node::VertexType) :: Union{MultiEdgeType, Nothing}
    visited_nodes = Set{VertexType}()
    visited_edges = Set{MultiEdgeType}()
    edges = Dict{VertexType, Any}()
    stack = Array{VertexType, 1}([start_node])
    while ! isempty(stack)
        current_node = last(stack)
        if ! (current_node in visited_nodes)
            edges[current_node] = out_edges(mdg, current_node)
            push!(visited_nodes, current_node)
        end
        edge = edges[current_node]()
        if edge === nothing
            pop!(stack)
        else
            if ! (edge in visited_edges)
                push!(visited_edges, edge)
                push!(stack, edge[2])
                @yield edge
            end
        end
    end
end

"""
Find a cycle with the given adjency dictionary. Return `nothing` if no cycle is
found.
"""
function find_cycle(mdg::MultiDiGraph, start_nodes=nothing::Union{Nothing, Set{VertexType}}) :: Union{Array{MultiEdgeType, 1}, Nothing}
    explored = Set{VertexType}()
    cycle = Array{MultiEdgeType, 1}()
    final_node = nothing
    if start_nodes === nothing
        nodes = get_nodes(mdg)
    else
        nodes = start_nodes
    end
    for start_node in nodes
        if start_node in explored
            # No loop is possible.
            continue
        end
        edges = Array{MultiEdgeType, 1}()
        # All nodes seen in this iteration of edge_dfs
        seen = Set{VertexType}()
        push!(seen, start_node)
        # Nodes in active path.
        active_nodes = Set{VertexType}()
        push!(active_nodes, start_node)
        previous_head = nothing
        for edge in edges_dfs(mdg, start_node)
            # Determine if this edge is a continuation of the active path.
            tail, head = edge[1], edge[2]
            if head in explored
                # Then we've already explored it. No loop is possible.
                continue
            end
            if (previous_head !== nothing) && (tail != previous_head)
                # This edge results from backtracking.
                # Pop until we get a node whose head equals the current tail.
                # So for example, we might have:
                #  (0, 1), (1, 2), (2, 3), (1, 4)
                # which must become:
                #  (0, 1), (1, 4)
                while true
                    if ! isempty(edges)
                        popped_edge = pop!(edges)
                        popped_head = popped_edge[2]
                        filter!(x -> x != popped_head, active_nodes)
                    else
                        edges = Array{MultiEdgeType, 1}()
                        active_nodes = Set{VertexType}([tail])
                        break
                    end
                    if ! isempty(edges)
                        last_head = last(edges)[2]
                        if tail == last_head
                            break
                        end
                    end
                end
            end
            push!(edges, edge)
            if head in active_nodes
                # We have a loop!
                cycle = vcat(cycle, edges)
                final_node = head
                break
            else
                push!(seen, head)
                push!(active_nodes, head)
                previous_head = head
            end
        end
        if ! isempty(cycle)
            break
        else
            for n in seen
                push!(explored, n)
            end
        end
    end
    if isempty(cycle)
        return nothing
    end
    # We now have a list of edges which ends on a cycle.
    # So we need to remove from the beginning edges that are not relevant.
    trimming_i = 1
    for (i, edge) in enumerate(cycle)
        tail, head = edge[1], edge[2]
        if tail == final_node
            trimming_i = i
            break
        end
    end
    return cycle[trimming_i:length(cycle)]
end

"""
Return all nodes reachable from `source` in the given adjency dictionary.
"""
function descendants(mdg::MultiDiGraph, source::VertexType, strict=false) :: Set{VertexType}
    des = Set{VertexType}()
    node_stack = [source]
    while ! isempty(node_stack)
        current_node = pop!(node_stack)
        if ! (current_node in des)
            push!(des, current_node)
            for e in out_edges(mdg, current_node)
                push!(node_stack, e[2])
            end
        end
    end
    if strict
        filter!(x -> x != source, des)
    end
    return des
end

"""
Compute the shortest path from a set of sources to a target node.
"""
function dijkstra_multisource(mdg::MultiDiGraph, sources::Set{VertexType}, target::VertexType, weight::Dict)
    dists = DefaultDict{VertexType, WeightType}(typemax(WeightType))
    visited = DefaultDict{VertexType, Bool}(false)
    parents = Dict{VertexType, VertexType}()
    fringe = PriorityQueue{VertexType, WeightType}()

    for s=sources
        dists[s] = zero(WeightType)
        visited[s] = true
        fringe[s] = zero(WeightType)
    end

    while !isempty(fringe)
        u = dequeue!(fringe)
        d = dists[u]
        if u == target
            break
        end
        if u in keys(mdg.succ)
            for v in keys(mdg.succ[u])
                alt = d + weight[(u, v)]
                if (visited[v]) && alt < dists[u]
                    throw(ErrorException("Contradictory paths found:
                                            negative weights?: alt='$(alt)', dists[v]='$(dists[v])"))
                end
                if (!visited[v]) || (alt < dists[v])
                    visited[v] = true
                    dists[v] = alt
                    parents[v] = u
                    fringe[v] = alt
                end
            end
        end
    end
    if ! visited[target]
        return nothing
    end
    path = [target]
    node = target
    while ! (node in sources)
        node = parents[node]
        push!(path, node)
    end
    return (path[end:-1:1], dists[target])
end

"""
Return the lowest common ancestor of the two given nodes in the given DAG.
Return `nothing` if none is found.
"""
function lowest_common_ancestor(mdg::MultiDiGraph, node1::VertexType, node2::VertexType) :: Union{VertexType, Nothing}
    # First node backtracking
    nodes_stack = [node1]
    ancestors1 = Set{VertexType}([node1])
    while !isempty(nodes_stack)
        current_node = pop!(nodes_stack)
        for e in in_edges(mdg, current_node)
            if e[1] in ancestors1
                continue
            end
            push!(ancestors1, e[1])
            push!(nodes_stack, e[1])
        end
    end
    # Second node backtracking
    ancestors2 = Set{VertexType}([node2])
    nodes_stack = [node2]
    while !isempty(nodes_stack)
        current_node = pop!(nodes_stack)
        if current_node in ancestors1
            return current_node  # lca found
        end
        for e in in_edges(mdg, current_node)
            if e[1] in ancestors2
                continue
            end
            push!(ancestors2, e[1])
            push!(nodes_stack, e[1])
        end
    end
    # no common ancestor found
    return nothing
end

function add_node!(graph::MultiDiGraph, n::VertexType)
    if ! (n in keys(graph.succ))
        graph.succ[n] = Dict{VertexType, Set{KeyType}}()
    end
    if ! (n in keys(graph.pred))
        graph.pred[n] = Dict{VertexType, Set{KeyType}}()
    end
end

function add_edge!(graph::MultiDiGraph, e::MultiEdgeType)
    u, v, k = e
    add_node!(graph, u)
    add_node!(graph, v)
    if v in keys(graph.succ[u])
        push!(graph.succ[u][v], k)
    else
        graph.succ[u][v] = Set{KeyType}([k])
    end
    if u in keys(graph.pred[v])
        push!(graph.pred[v][u], k)
    else
        graph.pred[v][u] = Set{KeyType}([k])
    end
end

function remove_node!(graph::MultiDiGraph, n::VertexType)
    for u in keys(graph.pred[n])
        delete!(graph.succ[u], n)
    end
    for v in keys(graph.succ[n])
        delete!(graph.pred[v], n)
    end
    delete!(graph.succ, n)
    delete!(graph.pred, n)
end

function remove_edge!(graph::MultiDiGraph, e::MultiEdgeType)
    u, v, k = e
    delete!(graph.succ[u][v], k)
    if isempty(graph.succ[u][v])
        delete!(graph.succ[u], v)
    end
    delete!(graph.pred[v][u], k)
    if isempty(graph.pred[v][u])
        delete!(graph.pred[v], u)
    end
end

function has_one_parent(graph::MultiDiGraph, n::VertexType) :: Bool
    parent_found = false
    for (u, ks) in graph.pred[n]
        if parent_found
            return false
        end
        parent_found = true
    end
    return parent_found
end

function has_no_parent(graph::MultiDiGraph, n::VertexType) :: Bool
    return isempty(graph.pred[n])
end

function remove_isolates!(graph::MultiDiGraph)
    nodes_to_remove = Set{VertexType}()
    for (u, ns) in graph.succ
        if isempty(ns) && isempty(graph.pred[u])
            push!(nodes_to_remove, u)
        end
    end
    for n in nodes_to_remove
        remove_node!(graph, n)
    end
end

max_stars_repo_name: Planheat/Planheat-Tool

max_issues_repo_name: Planheat/Planheat-Tool

max_forks_repo_name: Planheat/Planheat-Tool


text:
module ImageShow

using Requires
using FileIO
using ImageCore, OffsetArrays, Colors, FixedPointNumbers

const _have_restrict=Ref(false)
function _use_restrict(val::Bool)
    _have_restrict[] = val
end
function __init__()
    @require ImageTransformations="02fcd773-0e25-5acc-982a-7f6622650795" _use_restrict(true)
end
include("showmime.jl")

# facilitate testing from importers
testdir() = joinpath(@__DIR__,"..","test")

end