text:
#####
##### stepsize heuristics and adaptation
#####

####
#### initial stepsize
####

"""
$(TYPEDEF)

Parameters for the search algorithm for the initial stepsize.

The algorithm finds an initial stepsize ``ϵ`` so that the local acceptance ratio
``A(ϵ)`` satisfies

```math
a_\\text{min} ≤ A(ϵ) ≤ a_\\text{max}
```

This is achieved by an initial bracketing, then bisection.

$FIELDS

!!! note

    Cf. Hoffman and Gelman (2014), which does not ensure bounds for the
    acceptance ratio, just that it has crossed a threshold. This version seems
    to work better for some tricky posteriors with high curvature.
"""
struct InitialStepsizeSearch
    "Lowest local acceptance rate."
    a_min::Float64
    "Highest local acceptance rate."
    a_max::Float64
    "Initial stepsize."
    ϵ₀::Float64
    "Scale factor for initial bracketing, > 1. *Default*: `2.0`."
    C::Float64
    "Maximum number of iterations for initial bracketing."
    maxiter_crossing::Int
    "Maximum number of iterations for bisection."
    maxiter_bisect::Int
    function InitialStepsizeSearch(; a_min = 0.25, a_max = 0.75, ϵ₀ = 1.0, C = 2.0,
                                   maxiter_crossing = 400, maxiter_bisect = 400)
        @argcheck 0 < a_min < a_max < 1
        @argcheck 0 < ϵ₀
        @argcheck 1 < C
        @argcheck maxiter_crossing ≥ 50
        @argcheck maxiter_bisect ≥ 50
        new(a_min, a_max, ϵ₀, C, maxiter_crossing, maxiter_bisect)
    end
end

"""
$(SIGNATURES)

Find the stepsize for which the local acceptance rate `A(ϵ)` crosses `a`.

Return `ϵ₀, A(ϵ₀), ϵ₁`, A(ϵ₁)`, where `ϵ₀` and `ϵ₁` are stepsizes before and
after crossing `a` with `A(ϵ)`, respectively.

Assumes that ``A(ϵ₀) ∉ (a_\\text{min}, a_\\text{max})``, where the latter are
defined in `parameters`.

- `parameters`: parameters for the iteration.

- `A`: local acceptance ratio (uncapped), a function of stepsize `ϵ`

- `ϵ₀`, `Aϵ₀`: initial value of `ϵ`, and `A(ϵ₀)`
"""
function find_crossing_stepsize(parameters, A, ϵ₀, Aϵ₀ = A(ϵ₀))
    @unpack a_min, a_max, C, maxiter_crossing = parameters
    s, a = Aϵ₀ > a_max ? (1.0, a_max) : (-1.0, a_min)
    if s < 0                    # when A(ϵ) < a,
        C = 1/C                 # decrease ϵ
    end
    for _ in 1:maxiter_crossing
        ϵ = ϵ₀ * C
        Aϵ = A(ϵ)
        if s*(Aϵ - a) ≤ 0
            return ϵ₀, Aϵ₀, ϵ, Aϵ
        else
            ϵ₀ = ϵ
            Aϵ₀ = Aϵ
        end
    end
    # should never each this, miscoded log density?
    dir = s > 0 ? "below" : "above"
    error("Reached maximum number of iterations searching for ϵ from $(dir).")
end

"""
$(SIGNATURES)

Return the desired stepsize `ϵ` by bisection.

- `parameters`: algorithm parameters, see [`InitialStepsizeSearch`](@ref)

- `A`: local acceptance ratio (uncapped), a function of stepsize `ϵ`

- `ϵ₀`, `ϵ₁`, `Aϵ₀`, `Aϵ₁`: stepsizes and acceptance rates (latter optional).

This function assumes that ``ϵ₀ < ϵ₁``, the stepsize is not yet acceptable, and
the cached `A` values have the correct ordering.
"""
function bisect_stepsize(parameters, A, ϵ₀, ϵ₁, Aϵ₀ = A(ϵ₀), Aϵ₁ = A(ϵ₁))
    @unpack a_min, a_max, maxiter_bisect = parameters
    @argcheck ϵ₀ < ϵ₁
    @argcheck Aϵ₀ > a_max && Aϵ₁ < a_min
    for _ in 1:maxiter_bisect
        ϵₘ = middle(ϵ₀, ϵ₁)
        Aϵₘ = A(ϵₘ)
        if a_min ≤ Aϵₘ ≤ a_max  # in
            return ϵₘ
        elseif Aϵₘ < a_min      # above
            ϵ₁ = ϵₘ
            Aϵ₁ = Aϵₘ
        else                    # below
            ϵ₀ = ϵₘ
            Aϵ₀ = Aϵₘ
        end
    end
    # should never each this, miscoded log density?
    error("Reached maximum number of iterations while bisecting interval for ϵ.")
end

"""
$(SIGNATURES)

Find an initial stepsize that matches the conditions of `parameters` (see
[`InitialStepsizeSearch`](@ref)).

`A` is the local acceptance ratio (uncapped). When given a Hamiltonian `H` and a
phasepoint `z`, it will be calculated using [`local_acceptance_ratio`](@ref).
"""
function find_initial_stepsize(parameters::InitialStepsizeSearch, A)
    @unpack a_min, a_max, ϵ₀ = parameters
    Aϵ₀ = A(ϵ₀)
    if a_min ≤ Aϵ₀ ≤ a_max
        ϵ₀
    else
        ϵ₀, Aϵ₀, ϵ₁, Aϵ₁ = find_crossing_stepsize(parameters, A, ϵ₀, Aϵ₀)
        if a_min ≤ Aϵ₁ ≤ a_max  # in interval
            ϵ₁
        elseif ϵ₀ < ϵ₁          # order as necessary
            bisect_stepsize(parameters, A, ϵ₀, ϵ₁, Aϵ₀, Aϵ₁)
        else
            bisect_stepsize(parameters, A, ϵ₁, ϵ₀, Aϵ₁, Aϵ₀)
        end
    end
end

"""
$(SIGNATURES)

Uncapped log acceptance ratio of a Langevin step.
"""
function log_acceptance_ratio(H, z, ϵ)
    target = logdensity(H, z)
    isfinite(target) || throw(DomainError(z, "Starting point has non-finite density."))
    logdensity(H, leapfrog(H, z, ϵ)) - target
end

"""
$(SIGNATURES)

Return a function of the stepsize (``ϵ``) that calculates the local acceptance
ratio for a single leapfrog step around `z` along the Hamiltonian `H`. Formally,
let

```julia
A(ϵ) = exp(logdensity(H, leapfrog(H, z, ϵ)) - logdensity(H, z))
```

Note that the ratio is not capped by `1`, so it is not a valid probability *per se*.
"""
function local_acceptance_ratio(H, z)
    target = logdensity(H, z)
    isfinite(target) ||
        throw(DomainError(z.p, "Starting point has non-finite density."))
    ϵ -> exp(logdensity(H, leapfrog(H, z, ϵ)) - target)
end

function find_initial_stepsize(parameters::InitialStepsizeSearch, H, z)
    find_initial_stepsize(parameters, local_acceptance_ratio(H, z))
end

"""
$(TYPEDEF)

Parameters for the dual averaging algorithm of Gelman and Hoffman (2014, Algorithm 6).

To get reasonable defaults, initialize with `DualAveraging()`.

# Fields

$(FIELDS)
"""
struct DualAveraging{T}
    "target acceptance rate"
    δ::T
    "regularization scale"
    γ::T
    "relaxation exponent"
    κ::T
    "offset"
    t₀::Int
    function DualAveraging(δ::T, γ::T, κ::T, t₀::Int) where {T <: Real}
        @argcheck 0 < δ < 1
        @argcheck γ > 0
        @argcheck 0.5 < κ ≤ 1
        @argcheck t₀ ≥ 0
        new{T}(δ, γ, κ, t₀)
    end
end

function DualAveraging(; δ = 0.8, γ = 0.05, κ = 0.75, t₀ = 10)
    DualAveraging(promote(δ, γ, κ)..., t₀)
end

"Current state of adaptation for `ϵ`."
struct DualAveragingState{T <: AbstractFloat}
    μ::T
    m::Int
    H̄::T
    logϵ::T
    logϵ̄::T
end

"""
$(SIGNATURES)

Return an initial adaptation state for the adaptation method and a stepsize `ϵ`.
"""
function initial_adaptation_state(::DualAveraging, ϵ)
    @argcheck ϵ > 0
    logϵ = log(ϵ)
    DualAveragingState(log(10) + logϵ, 0, zero(logϵ), logϵ, zero(logϵ))
end

"""
$(SIGNATURES)

Update the adaptation `A` of log stepsize `logϵ` with average Metropolis acceptance rate `a`
over the whole visited trajectory, using the dual averaging algorithm of Gelman and Hoffman
(2014, Algorithm 6). Return the new adaptation state.
"""
function adapt_stepsize(parameters::DualAveraging, A::DualAveragingState, a)
    @argcheck 0 ≤ a ≤ 1
    @unpack δ, γ, κ, t₀ = parameters
    @unpack μ, m, H̄, logϵ, logϵ̄ = A
    m += 1
    H̄ += (δ - a - H̄) / (m + t₀)
    logϵ = μ - √m/γ * H̄
    logϵ̄ += m^(-κ)*(logϵ - logϵ̄)
    DualAveragingState(μ, m, H̄, logϵ, logϵ̄)
end

"""
$(SIGNATURES)

Return the stepsize `ϵ` for the next HMC step while adapting.
"""
current_ϵ(A::DualAveragingState, tuning = true) = exp(A.logϵ)

"""
$(SIGNATURES)

Return the final stepsize `ϵ` after adaptation.
"""
final_ϵ(A::DualAveragingState, tuning = true) = exp(A.logϵ̄)

###
### fixed stepsize adaptation placeholder
###

"""
$(SIGNATURES)

Adaptation with fixed stepsize. Leaves `ϵ` unchanged.
"""
struct FixedStepsize end

initial_adaptation_state(::FixedStepsize, ϵ) = ϵ

adapt_stepsize(::FixedStepsize, ϵ, a) = ϵ

current_ϵ(ϵ::Real) = ϵ

final_ϵ(ϵ::Real) = ϵ


text:
# The BiMix distribution samples from a `leftdistribution` distribution with
# probability `p` and the `rightdistribution` with probability `1-p`. this
# is useful for efficiently defining mixture models upon two distributions,
# since it computes a marginalized pdf, thus saving the extra node in the
# execution trace for the mixture assignment.

struct BiMix{T<:Distribution,U<:Distribution}
    prob :: Float64
    leftdistribution :: T
    rightdistribution :: U
end

function Distributions.logpdf(bimix::BiMix, value)
    pleft = log(bimix.prob) + logpdf(bimix.leftdistribution, value)
    pright = log1p(-bimix.prob) + logpdf(bimix.rightdistribution, value)

    m = max(pleft, pright)
    m + log(exp(pleft - m) + exp(pright - m))
end

function Base.rand(bimix::BiMix)
    left = rand() <= bimix.prob
    rand(left ? bimix.leftdistribution : bimix.rightdistribution)
end

ext: jl


text:
function fibonnaci(n)
	t1 = 0
	t2 = 1
	nextTerm = 0
	for i in 1:n
		if i == 1
			nextTerm = t1
		elseif i == 2
			nextTerm = t2
		end
		nextTerm = t1 + t2
		t1 = t2
		t2 = nextTerm
	#println(nextTerm)
	end
	println(nextTerm)
end

fibonnaci(1500)


text:
Base.parent(reg::AdjointRegister) = reg.parent

function Base.summary(io::IO, reg::AdjointRegister{B,RT}) where {B,RT}
    print(io, "adjoint(", summary(reg.parent), ")")
end

"""
    adjoint(register) -> register

Lazy adjoint for quantum registers.
"""
Base.adjoint(reg::AbstractRegister) = AdjointRegister(reg)
Base.adjoint(reg::AdjointRegister) = parent(reg)

viewbatch(reg::AdjointRegister, i::Int) = adjoint(viewbatch(parent(reg), i))

for FUNC in [:nqubits, :nremain, :nactive]
    @eval $FUNC(r::AdjointRegister) = $FUNC(r.parent)
end


text:
### ============== ### ============== ###
##    Short and Long-range             ##
##    interactions models              ##
##    (topological short-range)        ##
##    Martin Zumaya Hernandez          ##
##    EXAMPLE SIMULATION SCRIPT        ##
### ============== ### ============== ###

@everywhere using Distributions, CollectiveDynamics.ShortLongRange

### ============== ### ============== ### ============== ###
### SYSTEM'S PARAMETERS
### ============== ### ============== ### ============== ###

n   = parse(Int64, ARGS[1]) # number of particles
k   = parse(Float64, ARGS[2]) # average non-local interactions
w   = parse(Float64, ARGS[3]) # interactions relative weight
e   = parse(Float64, ARGS[4]) # noise intensity

Ti   = parse(Int, ARGS[5]) # start of integration time steps (10^Ti)
Tf   = parse(Int, ARGS[6]) # end of integration time steps (10^Tf)

rep = parse(Int, ARGS[7])

### ============== ### ============== ### ============== ###

@eval @everywhere N = $n
@eval @everywhere κ = $k
@eval @everywhere ω = $w
@eval @everywhere η = $e

@everywhere k_sh = 6

@everywhere ρ = 0.3
@everywhere v0 = 1.0
@everywhere dt = 1.0
@everywhere l = 0.5

@everywhere L  = cbrt(N / ρ) # size of box

@everywhere κ_dist = Poisson(κ)

pos = SharedArray{Float64}(3N) # particles positions
vel = SharedArray{Float64}(3N) # array of particles' velocities

v_r = SharedArray{Float64}(3N) # local metric interactions
v_n = SharedArray{Float64}(3N) # non local topological interactions

R_ij = SharedArray{Float64}(N,N)

### ============== ### ============== ### ============== ###
### INITIALIZATION FROM FILE
### ============== ### ============== ### ============== ###

# pos_data = reshape(raw_data, 3N, div(length(raw_data), 3N))
# vel_data = reshape(raw_data, 3N, div(length(raw_data), 3N))

data_path = joinpath(homedir(),"art_DATA","testing", "NLOC_TOP_3D_EXT_CP","DATA","data_N_$(N)","data_N_$(N)_k_$(κ)_w_$(ω)")

raw_data = reinterpret(Float64, read(joinpath(data_path,"pos_$(rep).dat")))
pos_data = raw_data[(end-3N+1):end]

raw_data = reinterpret(Float64, read(joinpath(data_path,"vel_$(rep).dat")))
vel_data = raw_data[(end-3N+1):end]

for i in 1:length(pos)
    pos[i] = pos_data[i]
    vel[i] = vel_data[i]
end

### ============== ### ============== ### ============== ###
### SET OUTPUT
### ============== ### ============== ### ============== ###

output_path = set_output_data_structure("SLR_TOP", N, κ, ω)
# output_path = set_output_data_structure("SLR_TOP", N, κ, ω, η)

pos_file = open(joinpath(output_path,"pos_$(rep).dat"), "w+")
vel_file = open(joinpath(output_path,"vel_$(rep).dat"), "w+")

# write initial conditions
println("//////// ", 1)
write(pos_file, pos)
write(vel_file, vel)

### ============== ### ============== ### ============== ###
### TIME EVOLUTION
### ============== ### ============== ### ============== ###

times = [convert(Int, exp10(i)) for i in Ti:Tf]

for i in 1:(length(times) - 1)

    for t in (times[i]+1):times[i+1]

        evolve_topological_system(pos, vel, v_r, v_n, R_ij, N, k_sh, η, ω, κ_dist)

        if t % times[i] == 0 || t % div(times[i], exp10(1)) == 0
            println("//////// ", t)
            write(pos_file, pos)
            write(vel_file, vel)
        end
    end

end

close(pos_file)
close(vel_file)

rmprocs(workers())

println("Done all")

### ============== ### ============== ### ============== ###


text:
#### Tutorial 2 ###

my_integer = 5
my_float = 5
my_range = 1:7
my_vector = [1,3,5]
my_string = "hello"
my_character = 'o'
my_boolean = true

typeof(my_integer)
typeof(my_float)

typeof(
    my_character
)
α = 5;
@show α + my_integer
print("α = $α\n", "My string is: $(my_string)")

#####****** Accessing packages
#* To enter the package manager: "]" in REPL
#* Current environment shown in parenthesis
#* Type activate . after (bin) pkg to create a new environment
#- want to have a new environment when working on new projects


text:
# --- Setup

# Imports
using Documenter
using Boid

# Make sure that the Julia source code directory is on LOAD_PATH
push!(LOAD_PATH, "../src/")

# Set up DocMeta
DocMeta.setdocmeta!(Boid, :DocTestSetup, :(using Boid); recursive=true)

# --- Generate documentation

makedocs(;
    modules=[Boid],
    authors="Kevin Chu <kevin@velexi.com> and contributors",
    repo="https://github.com/ktchu/Boid.jl/blob/{commit}{path}#{line}",
    sitename="Boid",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://ktchu.github.io/Boid.jl/stable",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
        #"Installation" => "installation.md",
        #"API" => "api.md",
        #"Acknowledgements" => "acknowledgements.md",
        "Index" => "docs-index.md",
    ],
)

# --- Deploy documentation

deploydocs(; repo="github.com/ktchu/Boid.jl", devbranch="main")


text:
using Underscores
using Utils

function parse_file(f)
    @_ map(parse(Int16, _), eachline(f))
end

function load()
    open(joinpath(@__DIR__, "10_jolt.txt"), "r") do f
        parse_file(f)
    end
end

function ex1(adapters)
    sort!(adapters)
    diffs::Array{Int16} = zeros(3)
    i = 2
    while i <= length(adapters)
        if adapters[i] > adapters[i-1] + 3
            throw("out")
        end
        diffs[adapters[i]-adapters[i-1]] += 1
        i += 1
    end
    (diffs[1] + 1) * (diffs[3] + 1)
end

const DIFF = 3

function ex2(cache, adapters, i)
    haskey(cache, i) && return cache[i]

    if i == 1
        Int(adapters[1] <= DIFF)
    elseif i == 2
        Int(adapters[2] - adapters[1] <= DIFF >= adapters[1]) + Int(adapters[2] <= DIFF)
    elseif i == 3
        jolt = adapters[i]
        base = ex2(cache, adapters, i - 1) + Int(jolt <= DIFF)
        if jolt - adapters[i-2] <= DIFF
            base += ex2(cache, adapters, i - 2)
        end
        base
    else
        jolt = adapters[i]
        base = ex2(cache, adapters, i - 1)
        if jolt - adapters[i-2] <= DIFF
            base += ex2(cache, adapters, i - 2)
        end
        if jolt - adapters[i-3] <= DIFF
            base += ex2(cache, adapters, i - 3)
        end
        cache[i] = base
        base
    end
end

function ex2(adapters)
    sort!(adapters)
    cache = Dict(0 => 1)
    ex2(cache, adapters, length(adapters))
end

adapters = load()

println("Product of Jolt differences: ", ex1(adapters))
println("Number of combinations: ", ex2(adapters))

using Test

@testset "Adapter Array" begin
    example = [16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4]

    example2 = [
        28,
        33,
        18,
        42,
        31,
        14,
        46,
        20,
        48,
        47,
        24,
        23,
        49,
        45,
        19,
        38,
        39,
        11,
        1,
        32,
        25,
        35,
        8,
        17,
        7,
        9,
        4,
        2,
        34,
        10,
        3,
    ]

    @testset "example 1" begin
        @test ex1(example) == 7 * 5
        @test ex1(example2) == 22 * 10
    end

    @testset "example 2" begin
        @test ex2([1, 4, 7, 10]) == 1
        @test ex2([1, 4, 5, 7, 10]) == 2
        @test ex2([1, 4, 5, 6, 7, 10]) == 4
        @test ex2([1, 4, 5, 6, 7, 9, 10]) == 10

        @test ex2(example) == 8
        @test ex2(example2) == 19208
    end

    @testset "results" begin
        @test ex1(adapters) == 2277
        @test ex2(adapters) == 37024595836928
    end
end

max_stars_repo_name: CmdQ/AoC


text:
#reload("Nodes.jl")
#reload("Expressions.jl")
#reload("Parsimonious.jl")

using Base.Test
using Parsimonious

type TestExpression <: Expression
    name
end

# function wrap so I can time running independent of compilation
# function go()

function len_eq(node, len)
    return textlength(node) == len
end

# -- Length Tests --

# Literal tests
@show m = match(Literal("hello"), "ehello", 2)
@show m.match
@show endof(m.match)
@show textlength(match(Literal("hello"), "ehello", 2))
@test len_eq(match(Literal("hello"), "ehello", 2), 5)
# Not sure if it's possible to have a match-nothing literal
# @test len_eq(match(Literal(""), ""), 0)
@test len_eq(match(Literal("a"), "a"), 1)

# Regex tests
@test len_eq(match(Regex("hello*"), "hellooo"), 7)  # *
@test len_eq(match(Regex("hello*"), "hellooono"), 7)  # *
@test_throws match(Regex("hello*"), "goodbye")  # no match
@test len_eq(match(Regex("hello", options="i"), "HELLO"), 5)

# test_sequence:
@test len_eq(match(Sequence(Regex("hi*"), Literal("lo"), Regex(".ingo")), "hiiiilobingo1234"), 12)  # succeed
@test_throws len_eq(match(Sequence(Regex("hi*"), Literal("lo"), Regex(".ingo")), "ohiiiilobingo1234"), 12)  # fail
@test_throws match(Sequence(Regex("hi*"), Literal("lo"), Regex(".ingo")), "hiiiilobing")  # don"t
@test len_eq(match(Sequence(Regex("hi*")), ">hiiii", 2), 5)  # non-0 pos
#
## test_one_of:
@test len_eq(match(Literal("aaa"), "aaa"), 3)  # first alternative
@test len_eq(match(OneOf(Literal("aaa"), Literal("bb")), "aaa"), 3)  # first alternative
@test len_eq(match(OneOf(Literal("aaa"), Literal("bb")), "bbaaa"), 2)  # second
@test_throws match(OneOf(Literal("aaa"), Literal("bb")), "aa")  # no match
#
## test_not:
@test len_eq(match(Not(Regex(".")), ""), 0)  # match
@test_throws match(Not(Regex(".")), "Hi")  # don"t
#
## test_optional:
@test len_eq(match(Sequence(Optional(Literal("a")), Literal("b")), "b"), 1)  # contained expr fails
@test len_eq(match(Sequence(Optional(Literal("a")), Literal("b")), "ab"), 2)  # contained expr succeeds
#
## test_zero_or_more:
@test len_eq(match(ZeroOrMore(Literal("b")), ""), 0)  # zero
@test len_eq(match(ZeroOrMore(Literal("b")), "bbb"), 3)  # more
#
@test len_eq(match(Regex("^"), ""), 0)  # Validate the next test.
#
# Try to make it loop infinitely using a zero-length contained expression:
@test len_eq(match(ZeroOrMore(Regex("^")), ""), 0)
#
## test_one_or_more:
@test len_eq(match(OneOrMore(Literal("b")), "b"), 1)  # one
@test len_eq(match(OneOrMore(Literal("b")), "bbb"), 3)  # more
@test len_eq(match(OneOrMore(Literal("b"), _min=3), "bbb"), 3)  # with custom min; success
@test_throws match(OneOrMore(Literal("b"), _min=3), "bb")  # with custom min; failure
@test len_eq(match(OneOrMore(Regex("^")), "bb"), 0)  # attempt infinite loop

# -- TreeTests --
#    """Tests for building the right trees
#
#    We have only to test successes here; failures (None-returning cases) are
#    covered above.
#
#    """
# test_simple_node
#        """Test that leaf expressions like ``Literal`` make the right nodes."""
h = Literal("hello", name="greeting")
m = match(h, "hello")
n = Node("greeting", "hello", 1, 5)
@test isequal(m, n)
#
# test_sequence_nodes
#        """Assert that ``Sequence`` produces nodes with the right children."""
# TODO: Inconsistent ways of passing 'name' to Sequence and Literal constructors. Make them all like Sequence
s = Sequence(Literal("heigh", name="greeting1"), Literal("ho", name="greeting2"), name="dwarf")
text = "heighho"
@test isequal(match(s, text), Node("dwarf", text, 1, 7, (Node("greeting1", text, 1, 5), Node("greeting2", text, 6, 7))))
#
# test_one_of
#        """``OneOf`` should return its own node, wrapping the child that succeeds."""
o = OneOf(Literal("a", name="lit"), name="one_of")
text = "aa"
@test isequal(match(o, text), Node("one_of", text, 1, 1, (Node("lit", text, 1, 1),)))
#
# test_optional
#        """``Optional`` should return its own node wrapping the succeeded child."""
expr = Optional(Literal("a", name="lit"), name="opt")
text = "a"
@test isequal(match(expr, text), Node("opt", text, 1, 1, (Node("lit", text, 1, 1),)))
#
#        # Test failure of the Literal inside the Optional; the
#        # LengthTests.test_optional is ambiguous for that.
text = ""
@test isequal(match(expr, text), Node("opt", text, 1, 0))
#
# test_zero_or_more_zero
#        """Test the 0 case of ``ZeroOrMore``; it should still return a node."""
expr = ZeroOrMore(Literal("a"), name="zero")
text = ""
@test match(expr, text) == Node("zero", text, 1, 0)
#match(expr,text) => ParentNode{:zero}(ZeroLengthMatch(1),())
#Node("zero",text,1,0) => ChildlessNode{:zero}(ZeroLengthMatch(1))
#Not sure if it should be Parent or Childless
#@test isequal(match(expr, text), Node("zero", text, 1, 0))

# test_one_or_more_one
#        """Test the 1 case of ``OneOrMore``; it should return a node with a child."""
expr = OneOrMore(Literal("a", name="lit"), name="one")
text = "a"
isequal(match(expr, text), Node("one", text, 1, 1, (Node("lit", text, 1, 1),)))
#
#    # Things added since Grammar got implemented are covered in integration
#    # tests in test_grammar.

#class ParseTests(TestCase):
#    """Tests for the ``parse()`` method"""
#
#    def test_parse_success(self):
#        """Make sure ``parse()`` returns the tree on success.
#
#        There's not much more than that to test that we haven't already vetted
#        above.
#
#        """
expr = OneOrMore(Literal("a", name="lit"), name="more")
text = "aa"
isequal(parse(expr, text), Node("more", text, 1, 2, (Node("lit", text, 1, 1), Node("lit", text, 2, 2),)))
#
#
#class ErrorReportingTests(TestCase):
#    """Tests for reporting parse errors"""
#
#    def test_inner_rule_succeeding(self):
#        """Make sure ``parse()`` fails and blames the
#        rightward-progressing-most named Expression when an Expression isn"t
#        satisfied.
#
#        Make sure ParseErrors have nice Unicode representations.
#
#        """

# Try examples that will work on both Boot Grammar and Rule Grammar on both
boot_gr, rule_gr = Parsimonious.boot_grammar(), Grammar()
for (rules, file) in zip((boot_gr, rule_gr), ("boot_expr", "rule_expr"))
    print(IOString(), rules.default_rule)  # print no infinite loop
end

# parse / match don't crash
parse(lookup(rule_gr, "rules"), Parsimonious.rule_syntax)
match(lookup(rule_gr, "rules"), Parsimonious.rule_syntax)

#for g in [boot_gr, rule_gr]
for g in [rule_gr]
    grammar = Grammar(g, "
                bold_text = open_parens text close_parens
                open_parens = '(('
                text = ~'[a-zA-Z]+'
                close_parens = '))'
                ")

    text = "((fred))"
    parse(grammar, text)
    text = "((fred!!"
    try
        parse(grammar, text)
    catch e
        @test e.pos == 7
        @test e.expr == lookup(grammar, "close_parens")
        @test e.text == text
        @test unicode(e) == "Rule 'close_parens' didn't match at '!!' (line 1, column 7)."
    end

#
#    def test_rewinding(self):
#        """Make sure rewinding the stack and trying an alternative (which
#        progresses farther) from a higher-level rule can blame an expression
#        within the alternative on failure.
#
#        There"s no particular reason I suspect this wouldn"t work, but it"s a
#        more real-world example than the no-alternative cases already tested.
#
#        """
    grammar = Grammar(g, """
        formatted_text = bold_text / weird_text
        bold_text = open_parens text close_parens
        weird_text = open_parens text '!!' bork
        bork = 'bork'
        open_parens = '(('
        text = ~'[a-zA-Z]+'
        close_parens = '))'
        """)

    text = "((fred))"
    parse(grammar, text)
    text = "((fred!!"
    try
        parse(grammar, text)
    catch error
        @test error.pos == 9
        @test error.expr == lookup(grammar, "bork")
        @test error.text == text
    end
#
#    def test_no_named_rule_succeeding(self):
#        """Make sure ParseErrors have sane printable representations even if we
#        never succeeded in matching any named expressions."""
    grammar = Grammar(g, "bork = 'bork'")
    try
        parse(grammar, "snork")
    catch error
        @test error.pos == 1
        @test error.expr == lookup(grammar, "bork")
        @test error.text == "snork"
    end
end  # for g in ...

#    def test_parse_with_leftovers(self):
#        """Make sure ``parse()`` reports where we started failing to match,
#        even if a partial match was successful."""

# TODO: crap, this one won't compile -- first time using parenthesis
# ERROR: Rule 'equals' didn't match at ' = 'chitty' (' ' 'ban' (line 1, column 9).
grammar = Grammar(rule_gr, """sequence = 'chitty' (' ' 'bang')+""")
try
    parse(grammar, "chitty bangbang")
catch error
    @test unicode(error) == "Rule 'sequence' matched in its entirety, but it didn't consume all the text. The non-matching portion of the text begins with 'bang' (line 1, column 11)."
end

#    def test_favoring_named_rules(self):
#        """Named rules should be used in error messages in favor of anonymous
#        ones, even if those are rightward-progressing-more, and even if the
#        failure starts at position 0."""

# TODO: Another fail. Something very wrong.
grammar = Grammar(rule_gr, """starts_with_a = &'a' ~'[a-z]+'""")
try
    parse(grammar, "burp")
catch error
    @test unicode(error) == "Rule 'starts_with_a' didn't match at 'burp' (line 1, column 1)."
end

#    def test_line_and_column(self):
#        """Make sure we got the line and column computation right."""

# TODO: This is fucked. I'm not escaping literals which is good for
# parsing the rule syntax but it makes a problem for texts like this.
# Check how python interprets these strings.
# Python escapes \ so you have \\n
# Julia does this and also escapes quotes so you have \"
# but when you print it \" prints correctly. This may be a subtle bug affecting
# Regex and other Julia string processing.
# TODO: Also the unicode error messages quadrouple escape everythig
# TODO: Document the exact difference between p", r", p""", r""", " and """

grammar = Grammar(rule_gr, p"""
    whee_lah = whee "\n" lah "\n"
    whee = "whee"
    lah = "lah"
    """)
print(grammar.default_rule)
# TODO: This text does parse which is good. Need to revisit escaping issues
# To parse JSON you definitely need to escape quotes.
parse(grammar, p"whee\nlah\n")

try
    parse(grammar, p"whee\nlahGOO")
catch error
    # TODO: Right now, this says "Rule <Literal "\n" at 0x4368250432>
    # didn"t match". That"s not the greatest. Fix that, then fix this.
    #ok_(unicode(error).endswith(ur"""didn"t match at "GOO" (line 2, column 4)."""))
    @show unicode(error)
# TODO: Thinks line 1, column 1 -- but also, parsing the proper text fails
#    @test endswith(unicode(error), """didn"t match at "GOO" (line 2, column 4).""")
end

#class RepresentationTests(TestCase):
#    """Tests for str(), unicode(), and repr() of expressions"""
#
#    def test_unicode_crash(self):
#        """Make sure matched unicode strings don"t crash ``__str__``."""
grammar = Grammar(rule_gr, p"""string = ~"\S+" """)
@show grammar.default_rule
println(parse(grammar, "中文"))

grammar = Grammar(rule_gr, p"""string = ~"\S\S" """)
@show grammar.default_rule
println(parse(grammar, "中文"))

grammar = Grammar(rule_gr, s"""string = ~"xx" """)
@show grammar.default_rule
println(parse(grammar, "xx"))

grammar = Grammar(rule_gr, s"""string = ~"中文" """)
@show grammar.default_rule
println(parse(grammar, "中文"))

grammar = Grammar(rule_gr, p"""string = "中文" """)
@show grammar.default_rule
# TODO: Failing parse grammar.
println(parse(grammar, "中文"))

# TODO: mix character width like as中文df

#
#    def test_unicode(self):
#        """Smoke-test the conversion of expressions to bits of rules.
#
#        A slightly more comprehensive test of the actual values is in
#        ``GrammarTests.test_unicode``.
#
#        """
#        unicode(rule_grammar)

mye = ParseError("foo\nfoo\n", TestExpression("tst"), 5)
@test Parsimonious.line(mye) == 2
@test Parsimonious.column(mye) == 1
mye = ParseError("foo\nfoo\n", TestExpression("tst"), 4)
@test Parsimonious.line(mye) == 1
@test Parsimonious.column(mye) == 4
myf = ParseError("foo\nfoo\n", TestExpression("tst"), 1)
@test Parsimonious.line(myf) == 1
@test Parsimonious.column(myf) == 1
myio = IOString()
showerror(myio, myf)
seekstart(myio)
# TODO: Fucked up because of not escaping strings:
# "Rule 'tst' didn't match at 'foo\n"
@test readline(myio) == "Rule 'tst' didn't match at 'foo\\nfoo\\n' (line 1, column 1)."

# Test

l = Literal("foo", name="foo")
t = parse(l, "foo", 1)
# see error messages:
@test_throws parse(l, "fooo", 1)
@test_throws parse(l, "bar", 1)
try
    parse(l, "foos", 1)
catch e
    @test typeof(e) == IncompleteParseError
end
try
    parse(l, "bar", 1)
catch e
    @test typeof(e) == ParseError
end

@test parse(Literal("foo"), "foo") == Node("", "foo", 1, 3)
@test parse(Literal("foo", "foorule"), "foo") == Node("foorule", "foo", 1, 3)

@test parse(Regex("fo.*", options="", name="myregex"), "fooooo", 1) == Node("myregex", "fooooo", 1, 6)  #, match=match(r"fo.*", "fooooo"))
@test_throws parse(Regex("fo.*", options="", name="myregex"), "FOOOOO", 1)
@test parse(Regex("fo.*", options="i", name="myregex"), "FOOOOO", 1) == Node("myregex", "FOOOOO", 1, 6)  #, match=match(r"fo.*"i, "FOOOOO"))
@test parse(Sequence("seq", l, l), "foofoo", 1) == Node("seq", "foofoo", 1, 6, (Node("foo", "foofoo", 1, 3), Node("foo", "foofoo", 4, 6)))
@test parse(OneOf(Literal("foo"), Literal("bar")), "bar", 1) == Node("", "bar", 1, 3, (Node("", "bar", 1, 3),))
@test parse(Sequence(Lookahead(Literal("fo")), Literal("foo")), "foo") == Node("", "foo", 1, 3, (Node("", "foo", 1, 0), Node("", "foo", 1, 3)))
@test parse(Sequence(Not(Literal("bar")), Literal("foo")), "foo") == Node("", "foo", 1, 3, (Node("", "foo", 1, 0), Node("", "foo", 1, 3)))
@test parse(Sequence(Optional(Literal("bar")), Literal("foo")), "foo") == Node("", "foo", 1, 3, (Node("", "foo", 1, 0), Node("", "foo", 1, 3)))
@test parse(Sequence(Optional(Literal("bar")), Literal("foo")), "barfoo") == Node("", "barfoo", 1, 6, (Node("", "barfoo", 1, 3, (Node("", "barfoo", 1, 3),)), Node("", "barfoo", 4, 6)))
# TODO: mixed UTF8 for all these tests
@show parse(ZeroOrMore(Literal("bar")), "")
@test parse(ZeroOrMore(Literal("bar")), "") == Node("", "", 1, 0) # Again, not sure if it should have children
@test parse(ZeroOrMore(Literal("bar")), "bar") == Node("", "bar", 1, 3, (Node("", "bar", 1, 3),))
@test parse(ZeroOrMore(Literal("bar")), "barbar") == Node("", "barbar", 1, 6, (Node("", "barbar", 1, 3), Node("", "barbar", 4, 6)))
@test_throws parse(OneOrMore(Literal("bar")), "")
@test parse(OneOrMore(Literal("bar")), "barbar") == Node("", "barbar", 1, 6, (Node("", "barbar", 1, 3), Node("", "barbar", 4, 6)))
@test parse(OneOrMore(Literal("bar")), "bar") == Node("", "bar", 1, 3, (Node("", "bar", 1, 3),))
@test_throws parse(OneOrMore(Literal("bar")), "bas")
te = TestExpression("tst")
@test Sequence("", te, te) == Sequence(te, te, name="") == Sequence(te, te)

# end  # function go()
# go()


text:
function readgamefile(filename)
    open(filename) do f
        for line in enumerate(eachline(f))
            splitline = split(line[2])
            println(splitline)
            if splitline[1] == "#"
                continue;
            elseif !isa(parse(splitline[1], raise=false), Number)
                # tree = ReadGameInfoLine(split_line)
            else
                node_id = splitline[1]
                # NodeSetGeneralInformation(split_line, tree)
                # if split_line.size() == 3 || split_line.size() == 5
                #   # CreateLeafNode(split_line, tree)
                # elseif node_id < tree->num_chance_histories()
                #   # CreateNatureNode(split_line, tree)
                # else
                #   # CreatePlayerNode(split_line, tree, game)
                # end
            end
        end
    end
end

function readgameinfoline(line)
    num_chance_histories_ = parse(split_line[1]);
    num_terminal_histories_ = parse(split_line[3]);
    num_nodes_ =  (parse(split_line[5]) + 1);
    num_infosets0 = parse(split_line[7]);
    num_infosets1 = parse(split_line[8]);
    return std::make_shared<GameTree>(
    num_chance_histories_,
    num_terminal_histories_, num_nodes_,
    num_infosets0, num_infosets1)
end


text:
# module SplineRaceWay

# using AutomotiveDrivingModels
# using NearestNeighbors
# using SplineUtils

# export Raceway

type Raceway
    roadway::AutomotiveDrivingModels.Roadway
    tree::NearestNeighbors.KDTree
    obstacleMap::Dict
    models::Dict{Int,AutomotiveDrivingModels.DriverModel}
    Δs::Float64
    x::Array{Float64,1}
    y::Array{Float64,1}
    θ::Array{Float64,1}
    k::Array{Float64,1}
    s::Array{Float64,1}

    function Raceway(Pts::Array,degree::Int,num_points::Int,num_samples::Int,lane_width::Float64)
        retval = new()

        T, tt, rx, ry = ClosedB_Spline(Pts, degree, num_points)
        ṙx, ṙy = B_SplineDerivative(T,tt,Pts,degree)
        θ = atan2(ṙy,ṙx) # unit tangent vector
        s = zeros(size(rx))
        s[2:end] = cumsum(sqrt(diff(rx).^2 + diff(ry).^2))
        k = diff(θ)./diff(s) # curvature
        x,y,θ,s,k = ResampleSplineEven(rx,ry,θ,s,k,num_samples)
        retval.roadway, retval.tree = GenSplineRoadway(x,y,θ,s,k,lane_width)
        retval.x = x
        retval.y = y
        retval.θ = θ
        retval.s = s
        retval.k = k
        retval.Δs = s[2] - s[1]
        retval.obstacleMap = Dict()
        retval.models = Dict{Int, DriverModel}()

        retval
    end
end # type

# end # module


text:
using CounterfactualRegret: IIEMatrixGame, Kuhn

function CFRMatrixTest(sol_type, N::Int)
    ## Rock Paper Scissors
    game = IIEMatrixGame([
        (0,0) (-1,1) (1,-1);
        (1,-1) (0,0) (-1,1);
        (-1,1) (1,-1) (0,0)
    ])
    trainer = sol_type(game)
    train!(trainer, N)
    s1 = trainer.I[0].s
    s1 ./= sum(s1)
        @test ≈(s1, fill(1/3,3), atol=0.01)
    s2 = trainer.I[1].s
    s2 ./= sum(s2)
        @test ≈(s2, fill(1/3,3), atol=0.01)
    F_eval = evaluate(trainer)
        @test all( .≈(F_eval,(0,0), atol=0.01))
    MC_eval = MonteCarloEvaluate(trainer,1)
        @test all( .≈(MC_eval,(0,0), atol=0.01))

    ## Prisoners Dilemma
    game = IIEMatrixGame([
        (-1,-1) (-3,0);
        (0,-3) (-2,-2)
    ])
    trainer = sol_type(game)
    train!(trainer, N)
    s1 = trainer.I[0].s
    s1 ./= sum(s1)
        @test ≈(s1, [0,1], atol=0.01)
    s2 = trainer.I[1].s
    s2 ./= sum(s2)
        @test ≈(s2, [0,1], atol=0.01)
    F_eval = evaluate(trainer)
        @test all( .≈(F_eval,(-2,-2), atol=0.01))
    MC_eval = MonteCarloEvaluate(trainer,1)
        @test all( .≈(MC_eval,(-2,-2), atol=0.01))

    # https://sites.math.northwestern.edu/~clark/364/handouts/bimatrix-mixed.pdf
    game = CounterfactualRegret.IIEMatrixGame([
        (1,1) (0,0) (0,0);
        (0,0) (0,2) (3,0);
        (0,0) (2,0) (0,3);
    ])
    trainer = sol_type(game)
    train!(trainer, N)
    NEs = [[6/11,3/11,2/11], [0,3/5,2/5], [1,0,0]]
    s1 = trainer.I[0].s
    s1 ./= sum(s1)
        @test begin
            ≈(s1, NEs[1], atol=0.01) ||
            ≈(s1, NEs[2], atol=0.01) ||
            ≈(s1, NEs[3], atol=0.01)
        end
    s2 = trainer.I[1].s
    s2 ./= sum(s2)
        @test begin
            ≈(s2, NEs[1], atol=0.01) ||
            ≈(s2, NEs[2], atol=0.01) ||
            ≈(s2, NEs[3], atol=0.01)
        end
    F_eval = evaluate(trainer)
    @test begin
        all( .≈(F_eval,(6/5,6/5), atol=0.01)) ||
        all( .≈(F_eval,(6/11,6/11), atol=0.01)) ||
        all( .≈(F_eval,(1,1), atol=0.01))
    end
    MC_eval = MonteCarloEvaluate(trainer,1)
    @test begin
        all( .≈(F_eval,(6/5,6/5), atol=0.01)) ||
        all( .≈(F_eval,(6/11,6/11), atol=0.01)) ||
        all( .≈(F_eval,(1,1), atol=0.01))
    end

    sol = sol_type(game; debug=true)
    train!(sol, 100_000)

    @test CFR.infokeytype(sol) == Int
end

function CFRKuhnTest(sol_type, N::Int, atol::Float64)
    game = Kuhn()
    trainer = sol_type(game)
    train!(trainer, N)

    s11__ = trainer.I[(1,1,SA[-1,-1,-1])].s
    s11__ ./= sum(s11__)
    α = s11__[2]
    @test 0 ≤ α ≤ 1/3
    s1200 = trainer.I[(1,2,SA[-1,-1,-1])].s
    s1200 ./= sum(s1200)
    @test ≈(s1200, [1,0], atol=atol)
    s1300 = trainer.I[(1,3,SA[-1,-1,-1])].s
    s1300 ./= sum(s1300)
    @test ≈(s1300, [1-3α,3α], atol=atol)

    s210_ = trainer.I[(2,1,SA[0,-1,-1])].s
    s210_ ./= sum(s210_)
    @test ≈(s210_, [2/3,1/3], atol=atol)
    s220_ = trainer.I[(2,2,SA[0,-1,-1])].s
    s220_ ./= sum(s220_)
    @test ≈(s220_, [1,0], atol=atol)
    s230_ = trainer.I[(2,3,SA[0,-1,-1])].s
    s230_ ./= sum(s230_)
    @test ≈(s230_, [0,1], atol=atol)

    s211_ = trainer.I[(2,1,SA[1,-1,-1])].s
    s211_ ./= sum(s211_)
    @test ≈(s211_, [1,0], atol=atol)
    s221_ = trainer.I[(2,2,SA[1,-1,-1])].s
    s221_ ./= sum(s221_)
    @test ≈(s221_, [2/3,1/3], atol=atol)
    s231_ = trainer.I[(2,3,SA[1,-1,-1])].s
    s231_ ./= sum(s231_)
    @test ≈(s231_, [0,1], atol=atol)

    s1101 = trainer.I[(1,1,SA[0,1,-1])].s
    s1101 ./= sum(s1101)
    @test ≈(s1101, [1,0], atol=atol)
    s1201 = trainer.I[(1,2,SA[0,1,-1])].s
    s1201 ./= sum(s1201)
    @test ≈(s1201, [2/3-α,1/3+α], atol=atol)
    s1301 = trainer.I[(1,3,SA[0,1,-1])].s
    s1301 ./= sum(s1301)
    @test ≈(s1301, [0,1], atol=atol)
end

@testset "IIE Solvers" begin
    @testset "CFR Matrix" begin CFRMatrixTest(CFRSolver, 100_000) end
    @testset "CFR Kuhn" begin CFRKuhnTest(CFRSolver, 100_000, 0.03) end

    @testset "CSCFR Matrix" begin CFRMatrixTest(CSCFRSolver, 100_000) end
    @testset "CSCFR Kuhn" begin CFRKuhnTest(CSCFRSolver, 1_000_000, 0.03) end

    @testset "DCFR Matrix" begin CFRMatrixTest(DCFRSolver, 100_000) end
    @testset "DCFR Kuhn" begin CFRKuhnTest(DCFRSolver, 200_000, 0.03) end

    @testset "ESCFR Matrix" begin CFRMatrixTest(ESCFRSolver, 500_000) end
    @testset "ESCFR Kuhn" begin CFRKuhnTest(ESCFRSolver, 1_000_000, 0.03) end
end

max_stars_repo_name: WhiffleFish/HelloCFR

max_issues_repo_name: WhiffleFish/HelloCFR

max_forks_repo_name: WhiffleFish/HelloCFR


text:
module GeometricFlux

using Core.Intrinsics: llvmcall
using Base.Threads
using Statistics: mean
using SparseArrays: SparseMatrixCSC
using LinearAlgebra: I, issymmetric, diagm, eigmax

using Requires
using DataStructures: DefaultDict
using Flux
using Flux: glorot_uniform, leakyrelu, GRUCell
using Flux: @functor
using ZygoteRules

import Base: identity
import Base.Threads: atomictypes, llvmtypes, inttype, ArithmeticTypes, FloatTypes,
       atomic_cas!, atomic_xchg!,
       atomic_add!, atomic_sub!, atomic_max!, atomic_min!,
       atomic_and!, atomic_nand!, atomic_or!, atomic_xor!
import Base.Sys: ARCH, WORD_SIZE

export

    # layers/meta
    Meta,
    adjlist,
    update_edge,
    update_vertex,
    update_global,
    aggregate_neighbors,
    aggregate_edges,
    aggregate_vertices,
    all_vertices_data,
    all_edges_data,
    adjacent_vertices_data,
    incident_edges_data,
    propagate,
    generate_cluster,

    # layers/msgpass
    MessagePassing,

    # layers/conv
    GCNConv,
    ChebConv,
    GraphConv,
    GATConv,
    GatedGraphConv,
    EdgeConv,
    message,
    update,
    propagate,

    # layer/pool
    GlobalPool,
    LocalPool,
    sumpool,
    subpool,
    prodpool,
    divpool,
    maxpool,
    minpool,
    meanpool,
    pool,

    # models
    GAE,
    VGAE,
    InnerProductDecoder,
    VariationalEncoder,

    # linalg
    degree_matrix,
    laplacian_matrix,
    normalized_laplacian,
    neighbors,

    # scatter
    scatter_add!,
    scatter_sub!,
    scatter_max!,
    scatter_min!,
    scatter_mul!,
    scatter_div!,
    scatter_mean!,
    scatter!,

    # graph/utils
    adjlist,

    # utils
    gather,
    identity,
    GraphInfo,
    edge_index_table

using CUDAapi
if has_cuda()
    try
        using CuArrays
        @eval has_cuarrays() = true
    catch ex
        @warn "CUDA is installed, but CuArrays.jl fails to load" exception=(ex,catch_backtrace())
        @eval has_cuarrays() = false
    end
else
    has_cuarrays() = false
end

const IntOrTuple = Union{Integer,Tuple}

include("scatter.jl")
include("linalg.jl")
include("utils.jl")
include("layers/meta.jl")
include("layers/msgpass.jl")
include("layers/conv.jl")
include("layers/pool.jl")
include("models.jl")

function __init__()
    @require CuArrays = "3a865a2d-5b23-5a0f-bc46-62713ec82fae" begin
        using CUDAnative
        using CuArrays: CuArray, CuMatrix, CuVector
        import CuArrays: cu
        include("cuda/scatter.jl")
        include("cuda/msgpass.jl")
        include("cuda/pool.jl")
        include("cuda/utils.jl")
        CuArrays.cu(x::Array{<:Integer}) = CuArray(x)
    end
    @require LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d" begin
        include("graph/simplegraphs.jl")
    end
    @require SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622" begin
        include("graph/weightedgraphs.jl")
        include("graph/utils.jl")
    end
    @require MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5" begin
        include("graph/metagraphs.jl")
    end
end

end


text:
# -*- coding: utf-8 -*-
module CategoryTheory

using LightGraphs
using SemanticModels
import Catlab.Doctrines: dom, codom
using MacroTools: prewalk, postwalk
using ModelingToolkit

export ⊔, AbstractMorph, FinSetMorph, dom, codom, verify, func, GraphMorph, Decorated, decoration, undecorate, AbstractSpan, leftob, rightob, apexob, Span, left, right, DoublePushout, AbstractCospan, Cospan, pushout

# +
import MacroTools.walk
walk(x::Operation, inner, outer) = outer(Operation(x.op, map(inner, x.args)))

⊔(a::UnitRange, b::UnitRange) = 1:(length(a)+length(b))
⊔(a::AbstractVector{Int}, b::AbstractVector{Int}) = vcat(a,b)
⊔(g::AbstractGraph, h::AbstractGraph) = blockdiag(g,h)

# -

"""    AbstractMorph

an abstract type for representing morphisms. The essential API for subtypes of AbstractMorph are

1. dom(m)::T
2. codom(m)::T
3. func(m)::Function

where T is the type of the objects in the category. See FinSetMorph for an example.
"""
abstract type AbstractMorph end

# +
"""    FinSetMorph{T,F}

morphisms in the category of Finite Sets. The objects are of type UnitRange{Int}.
func(m::FinSetMorph) is a function that takes `Int -> Int`. FinSetMorphs can be constructed
from a list of numbers. For example, `FinSetMorph([1,3,2,3])` is the morphism that takes
`1->1, 2->3, 3->2, 4->3` on domain `1:4` with codomain `1:3`. When you define a morphism from
a list of integers, the codomain is inferred from the largest element of the list. The domain
must always be the `1:l` where `l` is the length of the input list.

```(f::FinSetMorph)(g::G) where G <: AbstractGraph```

lift a finite set morphism (list of integers) to a graph homomorphism by its action on the vertex
set. The graph `h = f(g)` is defined by taking the edges of `g` and relabeling their src and dst
according to the function of `f`.

This method computes a valid graph homomorphism by definition.
"""
struct FinSetMorph{T,F} <: AbstractMorph
    codom::T
    fun::F
end

FinSetMorph(v::AbstractVector{Int}) = FinSetMorph(1:maximum(v), v)

dom(m::FinSetMorph) = 1:length(m.fun)
codom(m::FinSetMorph) = m.codom
func(m::FinSetMorph) = i->m.fun[i]

"""    ⊔(f::FinSetMorph, g::FinSetMorph)

the union of two morphisms in a finite set.
"""
function ⊔(f::FinSetMorph, g::FinSetMorph)
    Y = codom(f) ⊔ codom(g)
    h = f.fun ⊔ g.fun
    FinSetMorph(Y, h)
end

function (f::FinSetMorph)(g::G) where G <: AbstractGraph
    dom(f) == vertices(g) || throw(DomainError(vertices(g), "dom(f) = $(dom(f)) but nv(g) = $(nv(g))"))
    ϕ = func(f)
    map(edges(g)) do e
        s,t = e.src, e.dst
        Edge(ϕ(s), ϕ(t))
    end |> Graph
end

# +
"""    GraphMorph{T,F} <: Morph

morphisms in the category of Finite Graphs. The objects must be a subtype of AbstractGraph.

You can take a `FinSetMorph` and lift it to a graph homomorphism. This is the functor that
takes the finite set `1:n`, to the empty graph with `n` vertices.

"""
struct GraphMorph{T, F} <: AbstractMorph
    dom::T
    codom::T
    fun::F
end

"""    GraphMorph(g::AbstractGraph, f::FinSetMorph)

is defined to be the graph homomorphism you get by functorially lifting `f`.
That is, `f` acts on the vertex set of `g` as an `Int->Int` function, and then
must act on the edges consistently.
"""
GraphMorph(g::AbstractGraph, f::FinSetMorph) = GraphMorph(g, f(g), f)

dom(m::GraphMorph) = m.dom
codom(m::GraphMorph) = m.codom
func(m::GraphMorph) = begin
    f = func(m.fun)
    return i->f(i)
end

"""    verify(m::GraphMorph)

validate a graph homomorphism by checking that all the edges in `dom(m)` and map to edges in `codom(m)`.
"""
verify(m::GraphMorph) = begin
    dom(m.fun) == vertices(dom(m)) || return false
    codom(m.fun) == vertices(codom(m)) || return false
    E = Set(edges(codom(m)))
    f = func(m)
    map(edges(dom(m))) do e
        u,v = f(e.src), f(e.dst)
        if u > v
            u,v = v,u
        end
        Edge(u,v) in E
    end |> all
end

# +
"""    Decorated{M,T}

a decoration applied to the objects of a morphism, where M is a type of morphism and
type T is the category of the decoration
"""
struct Decorated{M,T}
    f::M
    d::T
end

# Get the domain or codomain of a decorated morphism
dom(m::Decorated) = dom(m.f)
codom(m::Decorated) = codom(m.f)
# Get the decoration of a decorated morphism
decoration(m::Decorated) = m.d
# Remove the decoration of a decorated morphism, and return the original morphism
undecorate(m::Decorated) = m.f

# +
"""    AbstractSpan

an abstract type for representing spans. The essential API for subtypes of AbstractSpan are

1. left(s)::M
2. right(s)::M
3. pushout(s)::C

where M is the type of morphisms in the span, and C is the type of cospan that solves
a pushout of span s. See Span for an example.
"""
abstract type AbstractSpan end

leftob(s::AbstractSpan) = codom(left(s))

rightob(s::AbstractSpan) = codom(right(s))

function apexob(s::AbstractSpan)
    a = dom(left(s))
    b = dom(right(s))
    a == b || error("Inconsistent span")
    return a
end

# +
"""    Span{F,G} <: AbstractSpan

a general span type where types F and G are types of morphisms in the span
"""
struct Span{F,G} <: AbstractSpan
    f::F
    g::G
end

function left(s::Span)
    return s.f
end

function right(s::Span)
    return s.g
end

"""    undecorate(s::Span{T,T}) where T <: Decorated

remove decorations of a span of decorated morphisms
"""
function undecorate(s::Span{T,T}) where T <: Decorated
    return Span(undecorate(left(s)), undecorate(right(s)))
end

# +
struct DoublePushout{S<:AbstractSpan, T<:NTuple{3,AbstractMorph}}
    rule::S
    morphs::T
    application::S
end

# TODO DPO CONSTRUCTOR TO SOLVE UNKNOWN DOUBLEPUSHOUT

# +
"""    AbstractCospan

an abstract type for representing cospans. The essential API for subtypes of AbstractCospan are

1. left(c)::M
2. right(c)::M
3. pullback(c)::S

where M is the type of morphisms in the cospan, and S is the type of span that solves the
pullback defined by c. See Cospan for an example.
"""
abstract type AbstractCospan end

leftob(c::AbstractCospan) = codom(left(c))

rightob(c::AbstractCospan) = codom(right(c))

function apexob(c::AbstractCospan)
    a = dom(left(c))
    b = dom(right(c))
    a == b || error("Inconsistent cospan")
    return a
end

# +
"""    Cospan{F,G} <: AbstractCospan

a general cospan type where types F and G are types of morphisms in the cospan
"""
struct Cospan{F,G} <: AbstractCospan
    f::F
    g::G
end

function left(c::Cospan)
    return c.f
end

function right(c::Cospan)
    return c.g
end

"""    undecorate(s::Copan{T,T}) where T <: Decorated

remove decorations of a cospan of decorated morphisms
"""
function undecorate(c::Cospan{T,T}) where T <: Decorated
    return Cospan(undecorate(left(c)), undecorate(right(c)))
end

# +
"""    pushout(s::Span{T,T}) where T <: FinSetMorph

treat f,g as a span and compute the pushout that is, the cospan of f=(f⊔g) and g=(a⊔b)
"""
function pushout(s::Span{T,T}) where T <: FinSetMorph
    f_dict = Dict(a=>i for (i, a) in enumerate(left(s).fun))
    g′ = map(n->n in keys(f_dict) ? func(right(s))(f_dict[n]) : n+length(rightob(s)), leftob(s))

    g_dict = Dict(a=>i for (i, a) in enumerate(right(s).fun))
    f′ = map(n->n in keys(g_dict) ? g′[func(left(s))(g_dict[n])] : n, rightob(s))

    u = union(f′, g′)
    u_dict = Dict(a=>i for (i, a) in enumerate(u))
    f′ = FinSetMorph(1:length(u), map(n->u_dict[n], f′))
    g′ = FinSetMorph(1:length(u), map(n->u_dict[n], g′))

    return Cospan(f′, g′)
end

"""    pushout(s::Span{T, T}) where T <: Decorated

treat f,g as a decorated span and compute the pushout that is, the cospan of f=(f⊔g) and g=(a⊔b),
with the decoration of (f⊔g)(d)
"""
function pushout(s::Span{T, T}) where T <: Decorated
    cs = pushout(undecorate(s))
    D = decoration(left(s)) ⊔ decoration(right(s))
    return Decorated(cs, (right(cs) ⊔ left(cs))(D))
end

end


text:
using Pkg
Pkg.activate(".")
Pkg.add(PackageSpec(;name="Catalyst", version="10.0.0"))
Pkg.add(PackageSpec(;name="AlgebraicPetri", rev="Catalyst_v10"))
Pkg.instantiate()


text:
module TestKDTrees
    using FactCheck
    using Devectorize
    using Base.Test
    using Base.Collections

    using KDTrees

    FactCheck.onlystats(true)

    # Test helper to see that two arrays are equal not counting order.
    # This is probably done in a better way and having this function
    # here makes me look dumb.
    function allin(idxs, data)
        return length(symdiff(Set(idxs), Set(data))) == 0
    end

    include("test_knn.jl")
    include("test_build.jl")
    include("test_inball.jl")
    include("test_monkey.jl")

    FactCheck.exitstatus()

end #module


text:
struct SimpleCovariance <: CovarianceEstimator end

simple() = SimpleCovariance()

function S_hat(x::RegressionModel, ::SimpleCovariance)
	rmul!(crossmodelmatrix(x), sum(abs2, residuals(x)))
end

function StatsBase.vcov(x::RegressionModel, ::SimpleCovariance)
    invcrossmodelmatrix = Matrix(inv(crossmodelmatrix(x)))
    rmul!(invcrossmodelmatrix, sum(abs2, residuals(x)) /  dof_residual(x))
    Symmetric(invcrossmodelmatrix)
end


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: panorama
using AWS.Compat
using AWS.UUIDs

"""
    create_application_instance(default_runtime_context_device, manifest_payload)
    create_application_instance(default_runtime_context_device, manifest_payload, params::Dict{String,<:Any})

Creates an application instance and deploys it to a device.

# Arguments
- `default_runtime_context_device`: A device's ID.
- `manifest_payload`: The application's manifest document.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ApplicationInstanceIdToReplace"`: The ID of an application instance to replace with the
  new instance.
- `"Description"`: A description for the application instance.
- `"ManifestOverridesPayload"`: Setting overrides for the application manifest.
- `"Name"`: A name for the application instance.
- `"RuntimeRoleArn"`: The ARN of a runtime role for the application instance.
- `"Tags"`: Tags for the application instance.
"""
function create_application_instance(
    DefaultRuntimeContextDevice,
    ManifestPayload;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/application-instances",
        Dict{String,Any}(
            "DefaultRuntimeContextDevice" => DefaultRuntimeContextDevice,
            "ManifestPayload" => ManifestPayload,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_application_instance(
    DefaultRuntimeContextDevice,
    ManifestPayload,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/application-instances",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DefaultRuntimeContextDevice" => DefaultRuntimeContextDevice,
                    "ManifestPayload" => ManifestPayload,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_job_for_devices(device_ids, device_job_config, job_type)
    create_job_for_devices(device_ids, device_job_config, job_type, params::Dict{String,<:Any})

Creates a job to run on one or more devices.

# Arguments
- `device_ids`: IDs of target devices.
- `device_job_config`: Configuration settings for the job.
- `job_type`: The type of job to run.

"""
function create_job_for_devices(
    DeviceIds, DeviceJobConfig, JobType; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "POST",
        "/jobs",
        Dict{String,Any}(
            "DeviceIds" => DeviceIds,
            "DeviceJobConfig" => DeviceJobConfig,
            "JobType" => JobType,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_job_for_devices(
    DeviceIds,
    DeviceJobConfig,
    JobType,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/jobs",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeviceIds" => DeviceIds,
                    "DeviceJobConfig" => DeviceJobConfig,
                    "JobType" => JobType,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_node_from_template_job(node_name, output_package_name, output_package_version, template_parameters, template_type)
    create_node_from_template_job(node_name, output_package_name, output_package_version, template_parameters, template_type, params::Dict{String,<:Any})

Creates a camera stream node.

# Arguments
- `node_name`: A name for the node.
- `output_package_name`: An output package name for the node.
- `output_package_version`: An output package version for the node.
- `template_parameters`: Template parameters for the node.
- `template_type`: The type of node.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobTags"`: Tags for the job.
- `"NodeDescription"`: A description for the node.
"""
function create_node_from_template_job(
    NodeName,
    OutputPackageName,
    OutputPackageVersion,
    TemplateParameters,
    TemplateType;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/packages/template-job",
        Dict{String,Any}(
            "NodeName" => NodeName,
            "OutputPackageName" => OutputPackageName,
            "OutputPackageVersion" => OutputPackageVersion,
            "TemplateParameters" => TemplateParameters,
            "TemplateType" => TemplateType,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_node_from_template_job(
    NodeName,
    OutputPackageName,
    OutputPackageVersion,
    TemplateParameters,
    TemplateType,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/packages/template-job",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "NodeName" => NodeName,
                    "OutputPackageName" => OutputPackageName,
                    "OutputPackageVersion" => OutputPackageVersion,
                    "TemplateParameters" => TemplateParameters,
                    "TemplateType" => TemplateType,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_package(package_name)
    create_package(package_name, params::Dict{String,<:Any})

Creates a package and storage location in an Amazon S3 access point.

# Arguments
- `package_name`: A name for the package.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Tags"`: Tags for the package.
"""
function create_package(PackageName; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "POST",
        "/packages",
        Dict{String,Any}("PackageName" => PackageName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_package(
    PackageName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/packages",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("PackageName" => PackageName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_package_import_job(client_token, input_config, job_type, output_config)
    create_package_import_job(client_token, input_config, job_type, output_config, params::Dict{String,<:Any})

Imports a node package.

# Arguments
- `client_token`: A client token for the package import job.
- `input_config`: An input config for the package import job.
- `job_type`: A job type for the package import job.
- `output_config`: An output config for the package import job.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobTags"`: Tags for the package import job.
"""
function create_package_import_job(
    ClientToken,
    InputConfig,
    JobType,
    OutputConfig;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/packages/import-jobs",
        Dict{String,Any}(
            "ClientToken" => ClientToken,
            "InputConfig" => InputConfig,
            "JobType" => JobType,
            "OutputConfig" => OutputConfig,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_package_import_job(
    ClientToken,
    InputConfig,
    JobType,
    OutputConfig,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/packages/import-jobs",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ClientToken" => ClientToken,
                    "InputConfig" => InputConfig,
                    "JobType" => JobType,
                    "OutputConfig" => OutputConfig,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_device(device_id)
    delete_device(device_id, params::Dict{String,<:Any})

Deletes a device.

# Arguments
- `device_id`: The device's ID.

"""
function delete_device(DeviceId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "DELETE",
        "/devices/$(DeviceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_device(
    DeviceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "DELETE",
        "/devices/$(DeviceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_package(package_id)
    delete_package(package_id, params::Dict{String,<:Any})

Deletes a package.

# Arguments
- `package_id`: The package's ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ForceDelete"`: Delete the package even if it has artifacts stored in its access point.
  Deletes the package's artifacts from Amazon S3.
"""
function delete_package(PackageId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "DELETE",
        "/packages/$(PackageId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_package(
    PackageId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "DELETE",
        "/packages/$(PackageId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    deregister_package_version(package_id, package_version, patch_version)
    deregister_package_version(package_id, package_version, patch_version, params::Dict{String,<:Any})

Deregisters a package version.

# Arguments
- `package_id`: A package ID.
- `package_version`: A package version.
- `patch_version`: A patch version.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"OwnerAccount"`: An owner account.
- `"UpdatedLatestPatchVersion"`: If the version was marked latest, the new version to maker
  as latest.
"""
function deregister_package_version(
    PackageId,
    PackageVersion,
    PatchVersion;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "DELETE",
        "/packages/$(PackageId)/versions/$(PackageVersion)/patch/$(PatchVersion)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function deregister_package_version(
    PackageId,
    PackageVersion,
    PatchVersion,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "DELETE",
        "/packages/$(PackageId)/versions/$(PackageVersion)/patch/$(PatchVersion)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_application_instance(application_instance_id)
    describe_application_instance(application_instance_id, params::Dict{String,<:Any})

Returns information about an application instance on a device.

# Arguments
- `application_instance_id`: The application instance's ID.

"""
function describe_application_instance(
    applicationInstanceId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_application_instance(
    applicationInstanceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_application_instance_details(application_instance_id)
    describe_application_instance_details(application_instance_id, params::Dict{String,<:Any})

Returns information about an application instance's configuration manifest.

# Arguments
- `application_instance_id`: The application instance's ID.

"""
function describe_application_instance_details(
    applicationInstanceId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)/details";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_application_instance_details(
    applicationInstanceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)/details",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_device(device_id)
    describe_device(device_id, params::Dict{String,<:Any})

Returns information about a device.

# Arguments
- `device_id`: The device's ID.

"""
function describe_device(DeviceId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET",
        "/devices/$(DeviceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_device(
    DeviceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/devices/$(DeviceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_device_job(job_id)
    describe_device_job(job_id, params::Dict{String,<:Any})

Returns information about a device job.

# Arguments
- `job_id`: The job's ID.

"""
function describe_device_job(JobId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET", "/jobs/$(JobId)"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_device_job(
    JobId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/jobs/$(JobId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_node(node_id)
    describe_node(node_id, params::Dict{String,<:Any})

Returns information about a node.

# Arguments
- `node_id`: The node's ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"OwnerAccount"`: The account ID of the node's owner.
"""
function describe_node(NodeId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET", "/nodes/$(NodeId)"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_node(
    NodeId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/nodes/$(NodeId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_node_from_template_job(job_id)
    describe_node_from_template_job(job_id, params::Dict{String,<:Any})

Returns information about a job to create a camera stream node.

# Arguments
- `job_id`: The job's ID.

"""
function describe_node_from_template_job(
    JobId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/template-job/$(JobId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_node_from_template_job(
    JobId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/template-job/$(JobId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_package(package_id)
    describe_package(package_id, params::Dict{String,<:Any})

Returns information about a package.

# Arguments
- `package_id`: The package's ID.

"""
function describe_package(PackageId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET",
        "/packages/metadata/$(PackageId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_package(
    PackageId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/packages/metadata/$(PackageId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_package_import_job(job_id)
    describe_package_import_job(job_id, params::Dict{String,<:Any})

Returns information about a package import job.

# Arguments
- `job_id`: The job's ID.

"""
function describe_package_import_job(
    JobId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/import-jobs/$(JobId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_package_import_job(
    JobId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/import-jobs/$(JobId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_package_version(package_id, package_version)
    describe_package_version(package_id, package_version, params::Dict{String,<:Any})

Returns information about a package version.

# Arguments
- `package_id`: The version's ID.
- `package_version`: The version's version.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"OwnerAccount"`: The version's owner account.
- `"PatchVersion"`: The version's patch version.
"""
function describe_package_version(
    PackageId, PackageVersion; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/metadata/$(PackageId)/versions/$(PackageVersion)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_package_version(
    PackageId,
    PackageVersion,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/packages/metadata/$(PackageId)/versions/$(PackageVersion)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_application_instance_dependencies(application_instance_id)
    list_application_instance_dependencies(application_instance_id, params::Dict{String,<:Any})

Returns a list of application instance dependencies.

# Arguments
- `application_instance_id`: The application instance's ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of application instance dependencies to return in one
  page of results.
- `"nextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_application_instance_dependencies(
    applicationInstanceId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)/package-dependencies";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_application_instance_dependencies(
    applicationInstanceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)/package-dependencies",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_application_instance_node_instances(application_instance_id)
    list_application_instance_node_instances(application_instance_id, params::Dict{String,<:Any})

Returns a list of application node instances.

# Arguments
- `application_instance_id`: The node instances' application instance ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of node instances to return in one page of results.
- `"nextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_application_instance_node_instances(
    applicationInstanceId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)/node-instances";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_application_instance_node_instances(
    applicationInstanceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/application-instances/$(applicationInstanceId)/node-instances",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_application_instances()
    list_application_instances(params::Dict{String,<:Any})

Returns a list of application instances.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"deviceId"`: The application instances' device ID.
- `"maxResults"`: The maximum number of application instances to return in one page of
  results.
- `"nextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
- `"statusFilter"`: Only include instances with a specific status.
"""
function list_application_instances(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET",
        "/application-instances";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_application_instances(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/application-instances",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_devices()
    list_devices(params::Dict{String,<:Any})

Returns a list of devices.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of devices to return in one page of results.
- `"NextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_devices(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET", "/devices"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_devices(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET", "/devices", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_devices_jobs()
    list_devices_jobs(params::Dict{String,<:Any})

Returns a list of jobs.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeviceId"`: Filter results by the job's target device ID.
- `"MaxResults"`: The maximum number of device jobs to return in one page of results.
- `"NextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_devices_jobs(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama("GET", "/jobs"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET)
end
function list_devices_jobs(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET", "/jobs", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_node_from_template_jobs()
    list_node_from_template_jobs(params::Dict{String,<:Any})

Returns a list of camera stream node jobs.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of node from template jobs to return in one page of
  results.
- `"NextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_node_from_template_jobs(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET",
        "/packages/template-job";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_node_from_template_jobs(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/template-job",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_nodes()
    list_nodes(params::Dict{String,<:Any})

Returns a list of nodes.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"category"`: Search for nodes by category.
- `"maxResults"`: The maximum number of nodes to return in one page of results.
- `"nextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
- `"ownerAccount"`: Search for nodes by the account ID of the nodes' owner.
- `"packageName"`: Search for nodes by name.
- `"packageVersion"`: Search for nodes by version.
- `"patchVersion"`: Search for nodes by patch version.
"""
function list_nodes(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama("GET", "/nodes"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET)
end
function list_nodes(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET", "/nodes", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_package_import_jobs()
    list_package_import_jobs(params::Dict{String,<:Any})

Returns a list of package import jobs.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The maximum number of package import jobs to return in one page of
  results.
- `"NextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_package_import_jobs(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET",
        "/packages/import-jobs";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_package_import_jobs(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/packages/import-jobs",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_packages()
    list_packages(params::Dict{String,<:Any})

Returns a list of packages.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of packages to return in one page of results.
- `"nextToken"`: Specify the pagination token from a previous request to retrieve the next
  page of results.
"""
function list_packages(; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "GET", "/packages"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_packages(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET", "/packages", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_tags_for_resource(resource_arn)
    list_tags_for_resource(resource_arn, params::Dict{String,<:Any})

Returns a list of tags for a resource.

# Arguments
- `resource_arn`: The resource's ARN.

"""
function list_tags_for_resource(
    ResourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "GET",
        "/tags/$(ResourceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "GET",
        "/tags/$(ResourceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    provision_device(name)
    provision_device(name, params::Dict{String,<:Any})

Creates a device and returns a configuration archive. The configuration archive is a ZIP
file that contains a provisioning certificate that is valid for 5 minutes. Transfer the
configuration archive to the device with the included USB storage device within 5 minutes.

# Arguments
- `name`: A name for the device.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description for the device.
- `"NetworkingConfiguration"`: A networking configuration for the device.
- `"Tags"`: Tags for the device.
"""
function provision_device(Name; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "POST",
        "/devices",
        Dict{String,Any}("Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function provision_device(
    Name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "POST",
        "/devices",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Name" => Name), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    register_package_version(package_id, package_version, patch_version)
    register_package_version(package_id, package_version, patch_version, params::Dict{String,<:Any})

Registers a package version.

# Arguments
- `package_id`: A package ID.
- `package_version`: A package version.
- `patch_version`: A patch version.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MarkLatest"`: Whether to mark the new version as the latest version.
- `"OwnerAccount"`: An owner account.
"""
function register_package_version(
    PackageId,
    PackageVersion,
    PatchVersion;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "PUT",
        "/packages/$(PackageId)/versions/$(PackageVersion)/patch/$(PatchVersion)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function register_package_version(
    PackageId,
    PackageVersion,
    PatchVersion,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "PUT",
        "/packages/$(PackageId)/versions/$(PackageVersion)/patch/$(PatchVersion)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    remove_application_instance(application_instance_id)
    remove_application_instance(application_instance_id, params::Dict{String,<:Any})

Removes an application instance.

# Arguments
- `application_instance_id`: An application instance ID.

"""
function remove_application_instance(
    applicationInstanceId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "DELETE",
        "/application-instances/$(applicationInstanceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function remove_application_instance(
    applicationInstanceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "DELETE",
        "/application-instances/$(applicationInstanceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    tag_resource(resource_arn, tags)
    tag_resource(resource_arn, tags, params::Dict{String,<:Any})

Tags a resource.

# Arguments
- `resource_arn`: The resource's ARN.
- `tags`: Tags for the resource.

"""
function tag_resource(ResourceArn, Tags; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "POST",
        "/tags/$(ResourceArn)",
        Dict{String,Any}("Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    ResourceArn,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "POST",
        "/tags/$(ResourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Tags" => Tags), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    untag_resource(resource_arn, tag_keys)
    untag_resource(resource_arn, tag_keys, params::Dict{String,<:Any})

Removes tags from a resource.

# Arguments
- `resource_arn`: The resource's ARN.
- `tag_keys`: Tag keys to remove.

"""
function untag_resource(
    ResourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return panorama(
        "DELETE",
        "/tags/$(ResourceArn)",
        Dict{String,Any}("tagKeys" => tagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    ResourceArn,
    tagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "DELETE",
        "/tags/$(ResourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("tagKeys" => tagKeys), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_device_metadata(device_id)
    update_device_metadata(device_id, params::Dict{String,<:Any})

Updates a device's metadata.

# Arguments
- `device_id`: The device's ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description for the device.
"""
function update_device_metadata(DeviceId; aws_config::AbstractAWSConfig=global_aws_config())
    return panorama(
        "PUT",
        "/devices/$(DeviceId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_device_metadata(
    DeviceId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return panorama(
        "PUT",
        "/devices/$(DeviceId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
using Unmarked, DataFrames

λ_formula = @formula(λ~elev+forest);
p_formula = @formula(p~precip+wind);
β_truth = [0, -0.3, 0.7, -0.2, 0, 0.7];

umd = simulate(Nmix, λ_formula, p_formula, (1000, 5), β_truth);

fit = nmix(λ_formula, p_formula, umd);

hcat(coef(fit), β_truth)

#Prediction
pr_df = DataFrame(elev=[0.5, -0.3], forest=[1,-1]);

predict(abundance(fit), pr_df, interval=true)

#Goodness-of-fit
gof(fit)

#Fit all subsets of covariates
fit_all = nmix(allsub(λ_formula), allsub(p_formula), umd);

#Model selection table
fit_all

#Missing values
yna = Array{Union{Int,Missing}}(deepcopy(umd.y))
yna[1,:] = fill(missing, 5)
yna[2,1] = missing

inp2 = UmData(yna, umd.site_covs, umd.obs_covs);

fit2 = nmix(@formula(psi~elev+forest), @formula(p~precip+wind), inp2)

gof(fit2)

max_stars_repo_name: kenkellner/julia-hme

max_issues_repo_name: kenkellner/julia-hme

max_forks_repo_name: kenkellner/julia-hme


text:
using Distributed
if nprocs() == 1
    addprocs(3)
end

module MyModule
  using Distributed
  mat = Dict()
  println("Module MyModule loaded on $(myid())")
  mat["5x5x5"] = [1, 2, 3]
  mat["15x15x15"] = [5, 6]

  matice = @spawn mat;
# mat[1] = 7
# matice = @spawn mat;

  @everywhere function used_in_smallfunc(par1, par2)
    println(par1, par2, " ")
    return par1 + par2
  end

  function bigfunc(iterator)
    localvar = 2.5
    pars = (1, 15)

    smallfunc = function (elm)
      # hypothetical (expensive) operation with elm
      # (in my code this is solving a linear system)
      vysledek = localvar * elm
      global matice

      # println("$localvar * $elm = $vysledek ")
      # matice
      # print("pred fetch $matice")
      # fetch(matice);
      # print("po fetch $matice")
      local_matrix_storage = fetch(matice)
      print(local_matrix_storage)
      if local_matrix_storage["5x5x5"][1] == 1
        local_matrix_storage["5x5x5"][1] = 2
        matice = @spawn local_matrix_storage
      else
        print(" else...")
      end
      # used_in_smallfunc(pars...)  # LoadError
      used_in_smallfunc(pars[1], pars[2])
    end

    pmap(smallfunc, iterator)
  end
end

# using MyModule

MyModule.bigfunc(1:10)


text:
module PlutoLab

include("Pluto.jl")

function run(; kwargs...)
    Pluto.run(; kwargs...)
end
end


text:
function preprocess(s::String, doc::Document; config::Dict = Dict())
    # order matters for the following string transformations!

    abs_src = joinpath(doc.root, doc.rel_path)
    rel_base = first(splitext(doc.rel_path))
    repo_root = get(config, "repo_root_path", REPO_DIR)
    repo_root_url = get(config, "repo_root_url", "<unknown>")
    filename = Literate.filename(abs_src)
    relrepo_path = relpath(abs_src, repo_root)

    s = replace(s, "@__FILE_URL__" => "@__REPO_ROOT_URL__/$(relrepo_path)")
    s = replace(s, "@__FILE__" => relrepo_path)
    exfile = joinpath(REPO_DIR, PATHS.examples_tarfile)
    rel_exfile = relpath(exfile, abs_src)
    #error(rel_exfile)
    #s = replace(s, "@__EXAMPLES__" => PATHS.examples_tarfile)
    s = replace(s, "@__EXAMPLES__" => rel_exfile)
    s = replace(
        s,
        "@__EXAMPLES_README__" => read(joinpath(EXAMPLE_DIR, "README.md"), String),
    )

    if doc.kind === :documenter
        s = parse_documenter(s).body
        s = add_documenter_title(s, doc.config[:title])
        # Since we are doing an out-of-source build
        # we need to add correct EditURL for Documenter
        s = add_documenter_editurl(s)
    elseif doc.kind === :literate
        s = parse_literate(s).body
        s = add_literate_title(s, doc.config[:title])
        if :script in doc.config[:builds] || :notebook in doc.config[:builds]
            # If we are building executable scripts/notebooks, add admonition at top of file
            # linking to examples
            s = add_literate_examples_header(s, repo_root, abs_src)
        end
    else
        error("Unknown document kind: $(doc.kind)")
    end

    s
end

function add_documenter_editurl(content::String)
    if occursin("EditURL", content)
        print(content)
        error("$path already contains an EditURL")
    end
    """
    ```@meta
    EditURL = "@__FILE_URL__"
    ```
    """ * content
end

function add_literate_examples_header(content::String, repo_root, abs_src)
    #examplehowto = joinpath(SRC_DIR, "examples/example_howto.md")
    #@assert isfile(examplehowto)
    #path = relpath(examplehowto, abs_src)
    # TODO
    examplehowto = "https://docs.lyceum.ml/dev/examples/example_howto/"
    """
    #md # !!! note "Running examples locally"
    #md #     This example and more are also available as Julia scripts and Jupyter notebooks.
    #md #
    #md #     See [the how-to page]($(examplehowto)) for more information.
    #md #
    """ * content
end

function add_literate_title(content::String, title::String)
    """
    #md # # $title

    """ * content
end
function add_documenter_title(content::String, title::String)
    """
    # $title

    """ * content
end


text:
# sbf_read.jl
#
#	Read Stoffflüsse Binary Format data from CLD - Gill R3.vi
#
# Jeremy Rüffer
# Thünen Institut
# Institut für Agrarklimaschutz
# Junior Research Group NITROSPHERE
# Julia 1.4.0
# 19.12.2017
# Last Edit: 02.04.2020

# A Labview string will have a four byte dimension array before the actual data
# s = String(read(fid,UInt8,5))
# a = Int64(read(fid,UInt16)) # Read little-endian UInt16 and convert it to Int64
# p_val = read(fid,Float64) # Read little-endian Float64

using DataFrames

"""# sbf_read

Load a single SBF (Stoffflüsse Binary Format) file

---

### Examples

`header_info, data = sbf_read(\"K:\\\\Data\\\\Site_20171222_125500.sbf\")` Load he data from the file into a dataframe, data, converting the first column to DateTime. header_info is an array of strings representing the \'description\' of the file, the \'column names\', \'units\' for each column (m/s, K, °C, etc.), \'data types\' for each column (Float64, UInt16, etc.), and each column\'s \'conversion equations\'.

---

`sbf_read(F)`\n
* **F**::String = File name, including path
"""
function sbf_read(src::String)
	#################
	##  Functions  ##
	#################
	function timestamp_aerodyne(t::Float64)::DateTime
		return DateTime(1904,1,1,1,0,0) + Dates.Second(floor(t)) + Dates.Millisecond(floor(1000(t - floor(t))))
	end

	#################
	##  Constants  ##
	#################
	fid = open(src,"r")
	magicbytes = read!(fid,Array{Int8}(undef,2))
	if magicbytes != Int8[63,42]
		close(fid)
		error(src * " is not a CLD/QCL/Gill binary file")
	end

	########################
	##  Read Header Info  ##
	########################
	fid,description,cols,units,types,conversions,lsize = sbf_header(fid)

	############################
	##  Calculate line count  ##
	############################
	pos_datastart = position(fid)
	fsize = filesize(src)
	lcount = Int(floor((fsize - pos_datastart)/lsize))

	#################
	##  Load Data  ##
	#################
	d = DataFrame(types,Symbol[Symbol(i) for i in cols],lcount,makeunique=true)
	colcount = length(cols)
	for i=1:lcount
		d[i,1] = timestamp_aerodyne(read(fid,Float64))
		for j=2:colcount
			d[i,j] = read(fid,types[j])
		end
		read(fid,Int8)
	end

	# Completeness Check
	if !eof(fid)
		warn("\t" * src * " was closed early")
	end

	close(fid)

	return [description,cols,units,types,conversions], d
end


text:
using Primes
function makeConstraint(data, numVars, bernO, priceIndex, whichShare, own)
# Data: matrix of non-interacted bernstein polynomials
# numVars: number of variables when priceIndex==1, number of variables per
# product otherwise
# priceIndex: whether price is in index or not
# which market share we care abou
p = primes(10000);
pdata = zeros(1,size(data,2));
for i = 1:size(data,2)
    pdata[1,i] = p[i]; # list of primes the size of the data
end

pdata2 = fullInteraction(convert.(Integer,pdata), convert.(Integer, numVars), convert.(Integer,bernO)); # interact primes into design matrix

s1p = pdata[(bernO+1)*(whichShare-1)+1:(bernO+1)*whichShare]; # locations of univariate bernstein polynomial in s1

# monotonicity
if numVars>1
    constMat = [];
    for i = 1:length(s1p)-1
        t1 = pdata2./s1p[i]; # divide data by primes corresponding to terms in polynomial of s
        t2 = pdata2./s1p[i+1];
        for j = 1:1:size(t1,2)
            if ~isempty(findall(x->x==t1[j], t2)) # & (t1[j]==floor(t1[j])))
                z = zeros(1,size(pdata2,2));
                    # assumes s increasing in delta
                    z[j] = 1; # add a 1 at the current location
                    z[findall(x->x==t1[j], t2)] .= -1; # add a -1 where the order of s is one higher
                if i==1
                    constMat = z;
                else
                    constMat = [constMat; z];
                end
            end
        end
    end
else
    constMat = [];
    i1 = 1;
    i2 = 2;
    for i = 1:size(data,2)-1
        z = zeros(1,size(data,2));
        z[i1] = 1;
        z[i2] = -1;
        if i==1
            constMat = z;
        else
            constMat = [constMat;z];
        end
        i1 = i1+1;
        i2 = i2+1;
    end
end

return constMat
end