text:
"""
    est_dΣdθ(glo, kernel_hyperparameters; return_est=true, return_anal=false, return_dif=false, return_bool=false, dif=1e-6, print_stuff=true)

Estimate the covariance derivatives of `glo` (a GLOM model) at
`kernel_hyperparameters`with forward differences

# Keyword Arguments
- `return_est::Bool=true`: Add the numerically estimated dΣdθs to the output
- `return_anal::Bool=false`: Add the analytical dΣdθs to the output
- `return_dif::Bool=false`: Add the difference between the dΣdθs to the output
- `return_bool::Bool=false`: Return just a similarity Boolean
- `dif::Real=1e-6`: The step size used in the forward difference method
- `print_stuff::Bool=true`: Whether to print things

# Output
- If `return_bool==true`, returns a Boolean for whether the analytical and
numerical estimates for the dΣdθs are approximately the same.
- Else returns a vector with some combination of the numerically estimated
dΣdθs, analytical dΣdθs, and differences between them
"""
function est_dΣdθ(glo::GLO, kernel_hyperparameters::Vector{T}; return_est::Bool=true, return_anal::Bool=false, return_dif::Bool=false, return_bool::Bool=false, dif::Real=1e-6, print_stuff::Bool=true) where {T<:Real}

    total_hyperparameters = append!(collect(Iterators.flatten(glo.a)), kernel_hyperparameters)

    if print_stuff
        println()
        println("est_dΣdθ: Check that our analytical dΣdθ are close to numerical estimates")
        println("hyperparameters: ", total_hyperparameters)
    end

    x = glo.x_obs
    return_vec = []
    coeff_orders = coefficient_orders(glo.n_out, glo.n_dif, a=glo.a)

    # construct estimated dΣdθs
    if return_est || return_dif || return_bool
        val = covariance(glo, total_hyperparameters)
        est_dΣdθs = zeros(length(total_hyperparameters), glo.n_out * length(x), glo.n_out * length(x))
        for i in 1:length(total_hyperparameters)
            if total_hyperparameters[i]!=0
                hold = copy(total_hyperparameters)
                hold[i] += dif
                est_dΣdθs[i, :, :] =  (covariance(glo, hold) - val) / dif
            end
        end
        if return_est; append!(return_vec, [est_dΣdθs]) end
    end

    # construct analytical dΣdθs
    if return_anal || return_dif || return_bool
        anal_dΣdθs = zeros(length(total_hyperparameters), glo.n_out * length(x), glo.n_out * length(x))
        for i in 1:length(total_hyperparameters)
            anal_dΣdθs[i, :, :] =  covariance(glo, total_hyperparameters; dΣdθs_total=[i])
        end
        if return_anal; append!(return_vec, [anal_dΣdθs]) end
    end

    if return_dif || return_bool
        difs = est_dΣdθs - anal_dΣdθs
        append!(return_vec, [difs])
    end

    # find whether the analytical and estimated dΣdθs are approximately the same
    if return_bool
        no_differences = true
        min_thres = 5e-3
        max_val = 0
        for ind in 1:length(total_hyperparameters)
            est = est_dΣdθs[ind,:,:]
            est[est .== 0] .= 1e-8
            val = mean(abs.(difs[ind,:,:] ./ est))
            max_val = maximum([max_val, val])
            if val>min_thres
                no_differences = false
                if print_stuff; println("dΣ/dθ$ind has a average ratioed difference of: ", val) end
            end
        end
        if print_stuff; println("Maximum average dΣ/dθ ratioed difference of: ", max_val) end
        return no_differences
    end

    return return_vec

end

"""
    est_∇(f::Function, inputs::Vector{<:Real}; dif=1e-7, ignore_0_inputs=false)

Estimate the gradient of `f` at `inputs` with forward differences

# Examples
```jldoctest
julia> f(x) = x[1] + x[2]^2;

julia> isapprox([1,4], GPLinearODEMaker.est_∇(f, [2, 2.]); rtol=1e-5)
true
```
"""
function est_∇(f::Function, inputs::Vector{<:Real}; dif::Real=1e-7, ignore_0_inputs::Bool=false)
    # original value
    val = f(inputs)

    #estimate gradient
    j = 1
    if ignore_0_inputs
        grad = zeros(length(remove_zeros(inputs)))
    else
        grad = zeros(length(inputs))
    end
    for i in 1:length(inputs)
        if !ignore_0_inputs || inputs[i]!=0
            hold = copy(inputs)
            hold[i] += dif
            grad[j] =  (f(hold) - val) / dif
            j += 1
        end
    end
    return grad
end

"""
    est_∇nlogL_GLOM(glo, total_hyperparameters; dif=1e-7)

Estimate the gradient of `nlogL_GLOM(glo, total_hyperparameters)` with forward
differences
"""
function est_∇nlogL_GLOM(glo::GLO, total_hyperparameters::Vector{T}; dif::Real=1e-7) where {T<:Real}
    f(inputs) = nlogL_GLOM(glo, inputs)
    return est_∇(f, total_hyperparameters; dif=dif, ignore_0_inputs=true)
end

"""
    test_∇(est_G, G; print_stuff=true, function_name="function")

Check if two gradient vectors (`est_G` and `G`) are approximately the same

# Examples
```jldoctest
julia> GPLinearODEMaker.test_∇([1., 2, 3], [1.0001, 2, 3]; print_stuff=false)
true
julia> GPLinearODEMaker.test_∇([1., 2, 3], [0., 2, 3]; print_stuff=false)
false
```
"""
function test_∇(est_G::Vector{T}, G::Vector{T}; print_stuff::Bool=true, function_name::String="function") where {T<:Real}

    if print_stuff
        println()
        println("test_∇: Check that our analytical $function_name is close to numerical estimates")
        # println("only values for non-zero hyperparameters are shown!")
        # println("hyperparameters: ", total_hyperparameters)
        println("analytical: ", G)
        println("numerical : ", est_G)
    end

    no_mismatch = true
    for i in 1:length(G)
        if !isapprox(G[i], est_G[i], rtol=5e-2)
            if print_stuff
                println("mismatch df/dθ" * string(i))
            end
            no_mismatch = false
        end
    end

    return no_mismatch
end

"""
    test_∇nlogL_GLOM(glo, kernel_hyperparameters; dif=1e-4, print_stuff=true)

Check if `∇nlogL_GLOM(glo, total_hyperparameters)` is close to numerical
estimates provided by `est_∇nlogL_GLOM(glo, total_hyperparameters)`
"""
function test_∇nlogL_GLOM(glo::GLO, kernel_hyperparameters::Vector{T}; dif::Real=1e-4, print_stuff::Bool=true) where {T<:Real}
    total_hyperparameters = append!(collect(Iterators.flatten(glo.a)), kernel_hyperparameters)
    return test_∇(est_∇nlogL_GLOM(glo, total_hyperparameters; dif=dif),
        ∇nlogL_GLOM(glo, total_hyperparameters);
        print_stuff=print_stuff, function_name="∇nlogL_GLOM")
end

"""
    est_∇∇(g::Function, inputs::Vector{<:Real}; dif=1e-7, ignore_0_inputs=false)

Estimate the Hessian of a function whose gradients are provided by `g` at
`inputs` with forward differences

# Examples
```jldoctest
julia> g(x) = [(x[2] ^ 2) / 2, x[1] * x[2]];  # h(x) = [0 x[2], x[2] x[1]]

julia> isapprox([0. 9; 9 4], GPLinearODEMaker.est_∇∇(g, [4., 9]); rtol=1e-5)
true
```
"""
function est_∇∇(g::Function, inputs::Vector{<:Real}; dif::Real=1e-7, ignore_0_inputs::Bool=false)

    val = g(inputs)

    #estimate hessian
    j = 1
    dim = length(remove_zeros(inputs))
    hess = zeros(dim, dim)
    for i in 1:length(inputs)
        if !ignore_0_inputs || inputs[i]!=0
            hold = copy(inputs)
            hold[i] += dif
            hess[j, :] =  (g(hold) - val) / dif
            j += 1
        end
    end
    return symmetric_A(hess)

end

"""
    est_∇∇_from_f(f::Function, inputs::Vector{<:Real}; dif=1e-7, ignore_0_inputs=false)

Estimate the Hessian of `f` at `inputs` with forward differences. WARNING: The
result is very sensitive to `dif`

# Examples
```jldoctest
julia> f(x) = (x[1] * x[2] ^ 2) / 2;  # h(x) = [0 x[2], x[2] x[1]]

julia> isapprox([0. 9; 9 4], GPLinearODEMaker.est_∇∇_from_f(f, [4., 9]; dif=1e-4); rtol=1e-3)
true
```
"""
function est_∇∇_from_f(f::Function, inputs::Vector{<:Real}; dif::Real=1e-7, ignore_0_inputs::Bool=false)

    val = est_∇(f, inputs; dif=dif, ignore_0_inputs=ignore_0_inputs)

    #estimate hessian
    j = 1
    hess = zeros(length(inputs), length(inputs))
    for i in 1:length(inputs)
        if !ignore_0_inputs || inputs[i]!=0
            hold = copy(inputs)
            hold[i] += dif
            hess[j, :] =  (est_∇(f, hold; dif=dif, ignore_0_inputs=ignore_0_inputs) - val) / dif
            j += 1
        end
    end
    return symmetric_A(hess)

end

"""
    est_∇∇nlogL_GLOM(glo, total_hyperparameters; dif=1e-4)

Estimate the Hessian of `nlogL_GLOM(glo, total_hyperparameters)` with forward
differences
"""
function est_∇∇nlogL_GLOM(glo::GLO, total_hyperparameters::Vector{T}; dif::Real=1e-4) where {T<:Real}

    g(inputs) = ∇nlogL_GLOM(glo, inputs)
    return est_∇∇(g, total_hyperparameters; ignore_0_inputs=true)

end

"""
    test_∇∇(est_H, H; print_stuff=true, function_name="function", rtol=1e-3)

Check if two Hessian matrices (`est_H` and `H`) are approximately the same

# Examples
```jldoctest
julia> GPLinearODEMaker.test_∇∇([1. 2; 3 4], [1.0001 2; 3 4]; print_stuff=false)
true
julia> GPLinearODEMaker.test_∇∇([1. 2; 3 4], [0. 2; 3 4]; print_stuff=false)
false
```
"""
function test_∇∇(est_H::Union{Symmetric,Matrix{T}}, H::Union{Symmetric,Matrix{T}}; print_stuff::Bool=true, function_name::String="function", rtol::Real=1e-3) where {T<:Real}

    if print_stuff
        println()
        println("test_∇∇: Check that our analytical $function_name is close to numerical estimates")
        # println("only values for non-zero hyperparameters are shown!")
        # println("hyperparameters: ", total_hyperparameters)
        println("analytical:")
        for i in 1:size(H, 1)
            println(H[i, :])
        end
        println("numerical:")
        for i in 1:size(est_H, 1)
            println(est_H[i, :])
        end
    end

    no_mismatch = true
    matches = fill(1, size(H))
    for i in 1:size(H, 1)
        for j in 1:size(H, 2)
            if !(isapprox(H[i, j], est_H[i, j], rtol=rtol))
                # println("mismatch d2nlogL/dθ" * string(i) * "dθ" * string(j))
                matches[i, j] = 0
                no_mismatch = false
            end
        end
    end

    if !no_mismatch && print_stuff
        println("mismatches at 0s")
        for i in 1:size(H, 1)
            println(matches[i, :])
        end
    end

    return no_mismatch
end

"""
    test_∇∇nlogL_GLOM(glo, kernel_hyperparameters; dif=1e-4, print_stuff=true)

Check if `∇∇nlogL_GLOM(glo, total_hyperparameters)` is close to numerical
estimates provided by `est_∇∇nlogL_GLOM(glo, total_hyperparameters)`
"""
function test_∇∇nlogL_GLOM(glo::GLO, kernel_hyperparameters::Vector{T}; dif::Real=1e-4, print_stuff::Bool=true) where {T<:Real}

    total_hyperparameters = append!(collect(Iterators.flatten(glo.a)), kernel_hyperparameters)
    H = ∇∇nlogL_GLOM(glo, total_hyperparameters)
    est_H = est_∇∇nlogL_GLOM(glo, total_hyperparameters; dif=dif)

    return test_∇∇(est_H, H; print_stuff=print_stuff, function_name="∇∇nlogL_GLOM")

end

max_stars_repo_name: christiangil/GPLinearODEMaker

max_issues_repo_name: christiangil/GPLinearODEMaker

max_forks_repo_name: christiangil/GPLinearODEMaker


text:
##################################
## Internal operations
##################################

"""
    add_to_dict!(dict::Dict{Any, V}, key, val::V) where V<:Number

    *Sparsely* add `val` to the `key` entry of `dict` dictionnary. That is creates
    the entry if needed, deletes it if the resulting value is null.
"""
function add_to_dict!(dict::Dict{U, Number}, key::U, val::T) where T<:Number where U
    if !haskey(dict, key)
        dict[key] = 0
    end
    dict[key] += val
    if dict[key] == 0
        delete!(dict, key)
    end
end

function setindex!(pt::Point, val::Number, var::Variable)
    pt.coords[var] = val
end

## Empty constructors
Point() = Point(Dict{Variable, Number}())
Exponent() = Exponent(Dict{Variable, Degree}())
Polynomial() = Polynomial(Dict{Exponent, Number}())

Exponent(x::Variable) = Exponent(Dict{Variable, Degree}(x=>Degree(1,0)))
function Point(vars::Array{Variable}, vals::Array{<:Number})
  if length(vars) != length(vals)
    error("Point(): input arrays must have same size.")
  end

  pt = Point()
  for i=1:length(vars)
    var, val = vars[i], vals[i]
    if isreal(var) val = real(val) end
    if isbool(var) && val != 0
      val = Int((val/abs(val)+1)/2)
    end
    add_coord!(pt, vars[i], val)
  end
  return pt
end

## copy methods
function copy(p::Polynomial)
    pdict = copy(p.poly)
    return Polynomial(pdict)
end

## convert functions
function convert(::Type{Polynomial}, expo::Exponent)
    return Polynomial(Dict{Exponent, Number}(expo=>1.0))
end

function convert(::Type{Polynomial}, x::Variable)
    return Polynomial(Dict{Exponent, Number}(Exponent(Dict(x=>Degree(1,0)))=>1.0))
end

function convert(::Type{Polynomial}, λ::Number)
    return Polynomial(Dict{Exponent, Number}(Exponent()=>λ))
end

function convert(::Type{Point}, pt::Dict{Variable, T}) where T
    return Point(convert(Dict{Variable, Number}, pt))
end

##
isreal(x::Variable) = x.kind <: Real
isbool(x::Variable) = x.kind <: Bool
iscomplex(x::Variable) = x.kind <: Complex

function conj(d::Degree)
    return Degree(d.conjvar, d.explvar)
end

function conj(x::Variable)
    if x.kind<:Complex
        return Polynomial(Dict{Exponent, Number}(Exponent(Dict(x=>Degree(0,1)))=>1.0))
    else
        return Polynomial(Dict{Exponent, Number}(Exponent(Dict(x=>Degree(1,0)))=>1.0))
    end
end

function conj(expo::Exponent)
    expodict = Dict{Variable, Degree}()
    for (var, deg) in expo.expo
        expodict[var] = conj(deg)
    end
    return Exponent(expodict)
end

function conj(p::Polynomial)
    pdict = Dict{Exponent, Number}()
    for (expo, λ) in p
        pdict[conj(expo)] = conj(λ)
    end
    return Polynomial(pdict)
end

## real, imag
real(p::Polynomial) = (p + conj(p))/2
imag(p::Polynomial) = (p - conj(p))/(2im)

function real(p::T) where T<:AbstractPolynomial
    return (p + conj(p))/2
end

function imag(p::T) where T<:AbstractPolynomial
    return (p + -conj(p))/(2im)
end

function abs2(p::T) where T<:AbstractPolynomial
    return p*conj(p)
end

## Evaluate
function evaluate(p::Polynomial, pt::Point)
    res=0
    for (expo, λ) in p
        res += λ*evaluate(expo, pt)
    end
    return res
end

function evaluate(expo::Exponent, pt::Point)
    res=1
    for (var, deg) in expo.expo
        if haskey(pt, var)
            res *= (evaluate(var, pt)^deg.explvar) * (conj(evaluate(var, pt))^deg.conjvar)
        end
    end
    return res
end

function evaluate(x::Variable, pt::Point)
    if !haskey(pt, x)
        return 0
    elseif x.kind<:Complex
        return pt[x]
    else
        return real(pt[x])
    end
end

ext: jl

max_stars_repo_name: Gimaju/GOC

max_issues_repo_name: Gimaju/GOC

max_forks_repo_name: Gimaju/GOC


text:
using Documenter, SparseMatricesCSR

makedocs(;
    modules=[SparseMatricesCSR],
    format=Documenter.HTML(),
    pages=[
        "Home" => "index.md",
    ],
    repo="https://github.com/gridap/SparseMatricesCSR.jl/blob/{commit}{path}#L{line}",
    sitename="SparseMatricesCSR.jl",
    authors="Víctor Sande <vsande@cimne.upc.edu>",
    assets=String[],
)

deploydocs(;
    repo="github.com/gridap/SparseMatricesCSR.jl",
)


text:
"""
    signature(m::Method) -> Dict{Symbol,Any}

Finds the expression for a method's signature as broken up into its various components
including:

- `:name`: Name of the function
- `:params`: Parametric types defined on constructors
- `:args`: Positional arguments of the function
- `:whereparams`: Where parameters

All components listed above may not be present in the returned dictionary if they are
not in the function definition.

Limited support for:
- `:kwargs`: Keyword arguments of the function.
  Only the names will be included, not the default values or type constraints.

Unsupported:
- `:rtype`: Return type of the function
- `:body`: Function body0
- `:head`: Expression head of the function definition (`:function`, `:(=)`, `:(->)`)

For more complete coverage, consider using [`splitdef`](@ref)
with [`CodeTracking.definition`](https://github.com/timholy/CodeTracking.jl).

The dictionary of components returned by `signature` match those returned by
[`splitdef`](@ref) and include all that are required by [`combinedef`](@ref), except for
the `:body` component.

# keywords

 - `extra_hygiene=false`: if set to `true` this forces name-hygiene on the `TypeVar`s in
   `UnionAll`s, regenerating each with a unique name via `gensym`. This shouldn't actually
   be required as they are scoped such that they are not supposed to leak. However, there is
   a long-standing [julia bug](https://github.com/JuliaLang/julia/issues/39876) that means
   they do leak if they clash with function type-vars.
"""
function signature(m::Method; extra_hygiene=false)
    sig = extra_hygiene ? _truly_rename_unionall(m.sig) : m.sig

    def = Dict{Symbol, Any}()
    def[:name] = m.name

    def[:args] = arguments(m, sig)
    def[:whereparams] = where_parameters(sig)
    def[:params] = type_parameters(m)
    def[:kwargs] = kwargs(m)

    return filter!(kv->last(kv)!==nothing, def)  # filter out nonfields.
end

"""
    signature(sig::Type{<:Tuple})

Like `ExprTools.signature(::Method)` but on the underlying signature type-tuple, rather than
the Method`.
For `sig` being a tuple-type representing a methods type signature, this generates a
dictionary that can be passes to `ExprTools.combinedef` to define that function,
Provided that you assign the `:body` key on the dictionary first.

The quality of the output, in terms of matching names etc is not as high as for the
`signature(::Method)`, but all the key information is present; and the type-tuple is for
other purposes generally easier to manipulate.

Examples
```julia
julia> signature(Tuple{typeof(identity), Any})
Dict{Symbol, Any} with 2 entries:
  :name => :(op::typeof(identity))
  :args => Expr[:(x1::Any)]

julia> signature(Tuple{typeof(+), Vector{T}, Vector{T}} where T<:Number)
Dict{Symbol, Any} with 3 entries:
  :name        => :(op::typeof(+))
  :args        => Expr[:(x1::Array{var"##T#5492", 1}), :(x2::Array{var"##T#5492", 1})]
  :whereparams => Any[:(var"##T#5492" <: Number)]
```

# keywords

 - `extra_hygiene=false`: if set to `true` this forces name-hygine on the `TypeVar`s in
   `UnionAll`s, regenerating each with a unique name via `gensym`. This shouldn't actually
   be required as they are scoped such that they are not supposed to leak. However, there is
   a long-standing [julia bug](https://github.com/JuliaLang/julia/issues/39876) that means
   they do leak if they clash with function type-vars.
"""
function signature(orig_sig::Type{<:Tuple}; extra_hygiene=false)
    sig = extra_hygiene ? _truly_rename_unionall(orig_sig) : orig_sig
    def = Dict{Symbol, Any}()

    opT = parameters(sig)[1]
    def[:name] = :(op::$opT)

    arg_types = name_of_type.(argument_types(sig))
    arg_names = [Symbol(:x, ii) for ii in eachindex(arg_types)]
    def[:args] = Expr.(:(::), arg_names, arg_types)
    def[:whereparams] = where_parameters(sig)

    filter!(kv->last(kv)!==nothing, def)  # filter out nonfields.
    return def
end

function slot_names(m::Method)
    ci = Base.uncompressed_ast(m)
    return ci.slotnames
end

function argument_names(m::Method)
    slot_syms = slot_names(m)
    # nargs includes 1 for `#self#` or the function object name;
    arg_names = slot_syms[2:m.nargs]
    return arg_names
end

argument_types(m::Method) = argument_types(m.sig)
function argument_types(sig)
    # First parameter of `sig` is the type of the function itself
    return parameters(sig)[2:end]
end

module DummyThatHasOnlyDefaultImports end  # for working out visibility

function name_of_module(m::Module)
    if Base.is_root_module(m)
        return nameof(m)
    else
        return :($(name_of_module(parentmodule(m))).$(nameof(m)))
    end
end
function name_of_type(x::Core.TypeName)
    # TODO: could let user pass this in, then we could be using what is inscope for them
    # but this is not important as we will give a correct (if overly verbose) output as is.
    from = DummyThatHasOnlyDefaultImports
    if Base.isvisible(x.name, x.module, from)  # avoid qualifying things that are in scope
        return x.name
    else
        return :($(name_of_module(x.module)).$(x.name))
    end
end

name_of_type(x::Symbol) = QuoteNode(x)  # Literal type-param e.g. `Val{:foo}`
function name_of_type(x::T) where T  # Literal type-param e.g. `Val{1}`
    # If this error is thrown, there is an issue with out implementation
    isbits(x) || throw(DomainError((x, T), "not a valid type-param"))
    return x
end
name_of_type(tv::TypeVar) = tv.name
function name_of_type(x::DataType)
    name = name_of_type(x.name)
    # because tuples are varadic in number of type parameters having no parameters does not
    # mean you should not write the `{}`, so we special case them here.
    if isempty(x.parameters) && x != Tuple{}
        return name
    else
        parameter_names = map(name_of_type, x.parameters)
        return :($(name){$(parameter_names...)})
    end
end

function name_of_type(x::UnionAll)
    # we do nested union all unwrapping so we can make the more compact:
    # `foo{T,A} where {T, A}`` rather than the longer: `(foo{T,A} where T) where A`
    where_params = []
    while x isa UnionAll
        push!(where_params, where_constraint(x.var))
        x = x.body
    end

    name = name_of_type(x)
    return :($name where {$(where_params...)})
end

function name_of_type(x::Union)
    parameter_names = map(name_of_type, Base.uniontypes(x))
    return :(Union{$(parameter_names...)})
end

function arguments(m::Method, sig=m.sig)
    arg_names = argument_names(m)
    arg_types = argument_types(sig)
    map(arg_names, arg_types) do name, type
        has_name = name !== Symbol("#unused#")
        type_name = name_of_type(type)
        if type === Any && has_name
            name
        elseif has_name
            :($name::$type_name)
        else
            :(::$type_name)
        end
    end
end

function where_constraint(x::TypeVar)
    if x.lb === Union{} && x.ub === Any
        return x.name
    elseif x.lb === Union{}
        return :($(x.name) <: $(name_of_type(x.ub)))
    elseif x.ub === Any
        return :($(x.name) >: $(name_of_type(x.lb)))
    else
        return :($(name_of_type(x.lb)) <: $(x.name) <: $(name_of_type(x.ub)))
    end
end

where_parameters(m::Method) = where_parameters(m.sig)
where_parameters(sig) = nothing
function where_parameters(sig::UnionAll)
    whereparams = []
    while sig isa UnionAll
        push!(whereparams, where_constraint(sig.var))
        sig = sig.body
    end
    return whereparams
end

type_parameters(m::Method) = type_parameters(m.sig)
function type_parameters(sig)
    typeof_type = first(parameters(sig))  # will be e.g Type{Foo{P}} if it has any parameters
    typeof_type <: Type{<:Any} || return nothing

    function_type = first(parameters(typeof_type))  # will be e.g. Foo{P}
    parameter_types = parameters(function_type)
    return map(name_of_type, parameter_types)
end

function kwargs(m::Method)
    names = kwarg_names(m)
    isempty(names) && return nothing  # we know it has no keywords.
    # TODO: Enhance this to support more than just their names
    # see https://github.com/invenia/ExprTools.jl/issues/6
    return names
end

function kwarg_names(m::Method)
    mt = Base.get_methodtable(m)
    !isdefined(mt, :kwsorter) && return []  # no kwsorter means no keywords for sure.
    return Base.kwarg_decl(m, typeof(mt.kwsorter))
end

"""
    _truly_rename_unionall(@nospecialize(u))

For `u` being a `UnionAll` this replaces every `TypeVar` with  a new one with a `gensym`ed
names.
This shouldn't actually be required as they are scoped such that they are not supposed to leak. However, there is
a long standing [julia bug](https://github.com/JuliaLang/julia/issues/39876) that means
they do leak if they clash with function type-vars.

Example:
```julia
julia> _truly_rename_unionall(Array{T, N} where {T<:Number, N})
Array{var"##T#2881", var"##N#2880"} where var"##N#2880" where var"##T#2881"<:Number
```

Note that the similar `Base.rename_unionall`, though `Base.rename_unionall` does not
`gensym` the names just replaces the instances with new instances with identical names.
"""
function _truly_rename_unionall(@nospecialize(u))
    # This works by recursively unwrapping UnionAlls to seperate the TypeVars from body
    # changing the name in the TypeVar, and then  rewrapping it back up.
    # The code is basically the same as `Base.rename_unionall`, but with gensym added
    isa(u, UnionAll) || return u
    body = _truly_rename_unionall(u.body)
    if body === u.body
        body = u
    else
        body = UnionAll(u.var, body)
    end
    var = u.var::TypeVar
    nv = TypeVar(gensym(var.name), var.lb, var.ub)
    return UnionAll(nv, body{nv})
end


text:
# ---
# title: Custom Title
# description: |
#   this is a multi line
#   destiption that spans
#   over multiple lines
# ---

# This is the content


text:
using Test, Printf

t1 = @elapsed using Tullio
@info @sprintf("Loading Tullio took %.1f seconds", t1)

is_buildkite = parse(Bool, get(ENV, "BUILDKITE", "false"))
if is_buildkite
    test_group = "2" # only run group 2 on the GPU servers
else
    test_group = get(ENV, "TULLIO_TEST_GROUP", "all")
end

@info "Testing flags" Threads.nthreads() test_group is_buildkite

if Threads.nthreads() > 1 # use threading even on small arrays
    Tullio.BLOCK[] = 32
    Tullio.TILE[] = 32
end

if test_group in ["all", "1"]
    include("group-1.jl")
end

if test_group in ["all", "2"] && VERSION <= v"1.6" # KA testing time-out https://github.com/JuliaGPU/KernelAbstractions.jl/issues/155
    include("group-2.jl")
end

if test_group in ["all", "3"]
    include("group-3.jl")
end


text:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Description
# ==============================================================================
#
#   Definition of constans.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# Definition of exponents.
const _EXPONENTS = ("⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹")
const _SUBNUMBERS = ("₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉")


text:
"""
rnn_lonely_integer.jl: (c) Ozan Arkan Can, 2016

This example learns to find the lonely integer in an array where all
but one of the integers occur in pairs.  The problem is taken from
[hackerrank](https://www.hackerrank.com/challenges/lonely-integer).
We feed elements into a recurrent neural network one by one, and we
get a prediction from the model after the final element.

To run the demo, simply `include("rnn_lonely_integer.jl")` and run
`LonelyInteger.train()`.  You can provide the initial weights as an
optional argument to `train`, which should have the form
[Whx,Whh,bh,Woh,bo] where first three elements are the parameters of
the rnn and the last two are the parameters of the softmax classifier.
The function `LonelyInteger.weights(;h, vocab)` can be used to create
random starting weights for a recurrent neural network with hidden
size and vocab size.  `train` also accepts the following keyword
arguments: `lr` specifies the learning rate, `N` gives the number of
instances that are used to train the model.  `seqlength` specifies the
length of the input sequences and `limit` defines the range of the
elements (from 1 to limit).  The running average cross entropy loss
and accuracy for the seen data will be printed after every 10k
instances and optimized parameters will be returned.

Data instances are created using `gendata`. It generates one instance
for a given sequence length and the limit parameter.

`test_example` takes trained weights and optional sequence length and
limit (must be same as the limit parameter used in the training)
parameters.  It shows a generated sequence and prediction of the model
for that instance.  You can test the performance of the model on
shorter or longer sequences than sequences used in training.

You can see an example experiment log at the end of the file.
"""

module LonelyInteger

using AutoGrad

function rnn_sequence(w, X; hidden=256)
    h = zeros(Float32, hidden, 1)

    for x in X
	preh = w[1] * x + w[2] * h .+ w[3]
	h = tanh(preh)
    end

    return h
end

function gendata(;seqlength=5, limit=20)
    rnums = randperm(limit)
    seq = rnums[1:(seqlength-1)]
    append!(seq, rnums[1:(seqlength-1)])
    push!(seq, rnums[seqlength])

    function onehot(indx)
	rep = zeros(Float32, limit, 1)
	rep[indx, 1] = 1.0
	return rep
    end

    onehotseq = map(onehot, seq)
    y = onehotseq[end]
    shuffle!(onehotseq)
    return (onehotseq, y)
end

function predict(w, h)
    return w[1] * h .+ w[2]
end

function loss(w, X, ygold)
    hT = rnn_sequence(w[1:3], X)
    ypred = predict(w[4:5], hT)
    ynorm = ypred .- log(sum(exp(ypred),1))
    -sum(ygold .* ynorm) / size(ygold,2)
end

function accuracy(w, X, ygold)
    hT = rnn_sequence(w[1:3], X)
    ypred = predict(w[4:5], hT)
    acc = indmax(ypred) == indmax(ygold) ? 1 : 0
end

function train(; lr=.001, N=2000000, seqlength=7, limit=50, w=weights(;vocab=limit))
    gradfun = grad(loss)
    nextn = 1000

    lss_avg = 0.0
    acc_avg = 0.0

    for n=1:N
	seq, ygold = gendata(;seqlength=seqlength, limit=limit)
	g = gradfun(w, seq, ygold)

	sloss = loss(w, seq, ygold)
	acc = accuracy(w, seq, ygold)

	#update
	for i=1:length(w); w[i] -= lr * g[i]; end

	lss_avg = (n==1 ? sloss : 0.99 * lss_avg + 0.01 * sloss)
	acc_avg = (n==1 ? acc : 0.99 * acc_avg + 0.01 * acc)

	if acc_avg > 0.999
	    println((n, lss_avg, acc_avg))
	    break
	end

	(n == nextn) && (println((n,lss_avg, acc_avg)); nextn+=1000)
    end

    return w
end

function timing(; lr=.001, N=10, seqlength=15, limit=100, w=weights(;vocab=limit))
    gradfun = grad(loss)

    function onestep()
	seq, ygold = gendata(;seqlength=seqlength, limit=limit);
	g = gradfun(w, seq, ygold);

	#update
	for i=1:length(w); w[i] -= lr * g[i]; end
    end

    for n=1:N
	gc_enable(false)
	@time onestep()
	gc_enable(true)
    end
end

function gendata(;seqlength=5, limit=20)
    rnums = randperm(limit)
    dup = round(Int32, (seqlength - 1) / 2)
    seq = rnums[1:dup]
    append!(seq, rnums[1:dup])
    push!(seq, rnums[dup+1])

    function onehot(indx)
	rep = zeros(Float32, limit, 1)
	rep[indx, 1] = 1.0
	return rep
    end

    onehotseq = map(onehot, seq)
    y = onehotseq[end]
    shuffle!(onehotseq)
    return (onehotseq, y)
end

function weights(; hidden=256, vocab=50)
    w = Any[]
    push!(w, 0.1*randn(hidden,vocab))#W_hx
    push!(w, 0.1*randn(hidden, hidden))#W_hh
    push!(w, zeros(hidden))#b
    push!(w, 0.1*randn(vocab, hidden))#W_oh
    push!(w, zeros(vocab))#b
    return w
end

function test_example(w; seqlength=5, limit=20)
    seq, ygold = gendata(;seqlength=seqlength, limit=limit)
    println("Sequence: $(map(indmax, seq))")

    hT = rnn_sequence(w[1:3], seq)
    ypred = predict(w[4:5], hT)

    println("Gold: $(indmax(ygold)), Prediction: $(indmax(ypred))")
    println("")
end

end # module

#=
RNNEXAMPLE.train(;N=2000000, lr=0.001, seqlength=7, limit=50)
(5000,3.9254147954712932,0.05823546687711375)
(15000,3.8347783596082543,0.09440094359889017)
(25000,3.865838169439587,0.09101518162802844)
(35000,3.5961692054202,0.10177853251884963)
(45000,3.591958750481305,0.09625938583918481)
(55000,3.7078481570065813,0.057749964095921785)
(65000,3.662092935174832,0.10045160364338919)
(75000,3.4344444000257703,0.09762542191839016)
(85000,3.5202308574219323,0.1188191211181203)
(95000,3.495033362578429,0.06985608240709526)
(105000,3.545338465844032,0.049726255706462094)
(115000,3.401478302792871,0.08356034017466722)
(125000,3.43937704044176,0.048482881898495034)
(135000,3.373317482719548,0.0783790938216713)
(145000,3.3968358146064377,0.07863264401047583)
(155000,3.3998308365038072,0.058176810199577385)
(165000,3.3854203953236857,0.05996116207383239)
(175000,3.294610861214726,0.05980160736874341)
(185000,3.2955473125602834,0.06772983554593635)
(195000,3.205354931780993,0.044352347305207276)
(205000,3.138209614524962,0.07289154771175269)
(215000,3.136330274070695,0.05219027884713963)
(225000,3.1472323635944233,0.06585019067290764)
(235000,3.2339883176082926,0.037801909117437194)
(245000,3.2237514125329967,0.03909466540726603)
(255000,3.1034637040730155,0.03435449802546742)
(265000,3.01630489214593,0.039125509093287075)
(275000,2.9492079233763255,0.0775554075728231)
(285000,2.924373808650891,0.08119570489621394)
(295000,2.88330660833433,0.045520738204591744)
(305000,2.9801444551347966,0.0720059839435539)
(315000,2.8292788451926425,0.07489431795574271)
(325000,2.9260810182274057,0.06557384998708969)
(335000,2.796193694704773,0.05329545095793585)
(345000,2.8496901913169976,0.07897440943301519)
(355000,2.712887418984886,0.07291655722670402)
(365000,2.765270516987435,0.06446038584847386)
(375000,2.6747343086015882,0.06450701435593854)
(385000,2.593207560038882,0.08195785777298324)
(395000,2.5675051588390225,0.12403903722792178)
(405000,2.440873241602668,0.1286490186185866)
(415000,2.3607913906119866,0.13948185811566524)
(425000,2.422550846616807,0.14491854838751542)
(435000,2.33712184509847,0.1649838142989618)
(445000,2.1728366766728686,0.23305804728313556)
(455000,2.294092413720761,0.21370029615442018)
(465000,2.229902398980125,0.22693723287288248)
(475000,2.0843636254568456,0.29324882274028424)
(485000,2.1690135942845052,0.25494423904147795)
(495000,2.029575497169476,0.2817808574311234)
(505000,2.018764740911919,0.31172281466469576)
(515000,2.0521509456935756,0.32065625642771545)
(525000,1.9296962540268194,0.30291575580380437)
(535000,1.6879912814679325,0.3863069741736311)
(545000,1.8727704996555408,0.4136314713530327)
(555000,1.6648456097910216,0.4396605024667052)
(565000,1.5993401016268955,0.4265090719822417)
(575000,1.5941809610605726,0.4890625982526005)
(585000,1.5426872216335419,0.4989237198379696)
(595000,1.4157123752145753,0.557825588505984)
(605000,1.3032705734715309,0.5745242115672833)
(615000,1.517168941868977,0.5324376419982895)
(625000,1.3540932023513743,0.5474682036807306)
(635000,1.4108951279140025,0.52847086791496)
(645000,1.267215396068737,0.6162706256838634)
(655000,1.1798962320204538,0.6393352147003012)
(665000,1.2345061003259454,0.6481249939547733)
(675000,1.1281916840470916,0.6672409938073572)
(685000,1.0237858905118933,0.7145218218525072)
(695000,0.8914707716711384,0.7717530434428677)
(705000,0.952238328309364,0.72362933798709)
(715000,0.9405367513063994,0.7123276652101643)
(725000,0.8679144953733816,0.7744618942551058)
(735000,0.8550557011342603,0.7791783970643467)
(745000,0.9217059218784082,0.706177293999402)
(755000,0.715502505327051,0.8481938405802976)
(765000,0.8175346060709452,0.7572529026629168)
(775000,0.720734279350491,0.8383031603696639)
(785000,0.7204544240456311,0.7612595785013923)
(795000,0.6605850089156964,0.8389512861661527)
(805000,0.6718430138799281,0.8148635994701189)
(815000,0.6393106793073713,0.8361706527781337)
(825000,0.6666348198783135,0.8435147218150201)
(835000,0.5988272323855302,0.8605988641604053)
(845000,0.6191561860954037,0.8627608081668675)
(855000,0.5832566163231246,0.9016319453333244)
(865000,0.47324692338257224,0.8717556367836733)
(875000,0.45557475839702866,0.9365387594669428)
(885000,0.4668700088971958,0.9122769421372297)
(895000,0.5099601910275182,0.8982515886371417)
(905000,0.47793319039539783,0.927460059393786)
(915000,0.45992243845045605,0.8965995653073635)
(925000,0.4391425468584585,0.8793919722708446)
(935000,0.3897434968359376,0.9662436695667682)
(945000,0.3646875053312192,0.9602772286117665)
(955000,0.3718836517601025,0.9254588034778205)
(965000,0.3392730076665364,0.9510296136308011)
(975000,0.3696439743946451,0.9440675204831033)
(985000,0.37081730190116174,0.9331734713781792)
(995000,0.32695742816382656,0.9343670214841164)
(1005000,0.3299650774822141,0.968463584172169)
(1015000,0.29854385060520233,0.96746614445897)
(1025000,0.3234709585760939,0.9681262164427642)
(1035000,0.2978435021693024,0.9771662701659236)
(1045000,0.2739979235617187,0.9861928981448081)
(1055000,0.26884869311505255,0.9657797242822954)
(1063063,0.24595387097191207,0.9990003244074369)
=#


text:
notebook = Notebook([
    Cell("md\"This is **markdown**\"")
])
html, nb = notebook2html_helper(notebook; use_distributed=false)
lines = split(html, '\n')
@test contains(lines[2], "<strong>")

notebook = Notebook([
    Cell("""("pluto", "tree", "object")"""),
    Cell("""["pluto", "tree", "object"]"""),
    Cell("""[1, (2, (3, 4))]"""),
    Cell("(; a=(1, 2), b=(3, 4))")
])
html, nb = notebook2html_helper(notebook; use_distributed=false);
lines = split(html, '\n')
@test contains(lines[2], "(\"pluto\", \"tree\", \"object\")")
@test contains(lines[2], "<pre")
@test contains(lines[6], "pluto")
@test contains(lines[7], "tree")
@test contains(lines[8], "object")
@test contains(lines[11], "2-element Vector")
@test contains(lines[16], "(a = (1, 2), b = (3, 4))")


text:
using Trebuchet

function Base.run(ws, r)
    t = TrebuchetState(;wind_speed=ws, release_angle=deg2rad(r))
    simulate(t)
    scope = visualise(t)

    if @isdefined(IJulia) || @isdefined(Juno)
      return scope
    elseif @isdefined(Blink)
      body!(Blink.Window(), scope)
    else
      println("to visualise, use Blink or run this on Juno or IJulia")
    end
end

run(-10.0, 33.2)


text:
#=
//===--------------------------------------------------------------------===//
// Table Function Bind
//===--------------------------------------------------------------------===//
=#
struct BindInfo
    handle::duckdb_bind_info
    main_function::Any

    function BindInfo(handle::duckdb_bind_info, main_function)
        result = new(handle, main_function)
        return result
    end
end

mutable struct InfoWrapper
    main_function::Any
    info::Any

    function InfoWrapper(main_function, info)
        return new(main_function, info)
    end
end

function parameter_count(bind_info::BindInfo)
    return duckdb_bind_get_parameter_count(bind_info.handle)
end

function get_parameter(bind_info::BindInfo, index::Int64)
    return Value(duckdb_bind_get_parameter(bind_info.handle, index))
end

function add_result_column(bind_info::BindInfo, name::AbstractString, type::DataType)
    return add_result_column(bind_info, name, create_logical_type(type))
end

function add_result_column(bind_info::BindInfo, name::AbstractString, type::LogicalType)
    return duckdb_bind_add_result_column(bind_info.handle, name, type.handle)
end

function get_extra_data(bind_info::BindInfo)
    return bind_info.main_function.extra_data
end

function _add_global_object(main_function, object)
    lock(main_function.global_lock)
    push!(main_function.global_objects, object)
    unlock(main_function.global_lock)
    return
end

function _remove_global_object(main_function, object)
    lock(main_function.global_lock)
    delete!(main_function.global_objects, object)
    unlock(main_function.global_lock)
    return
end

function _table_bind_cleanup(data::Ptr{Cvoid})
    info::InfoWrapper = unsafe_pointer_to_objref(data)
    _remove_global_object(info.main_function, info)
    return
end

function _table_bind_function(info::duckdb_bind_info)
    try
        main_function = unsafe_pointer_to_objref(duckdb_bind_get_extra_info(info))
        binfo = BindInfo(info, main_function)
        bind_data = InfoWrapper(main_function, main_function.bind_func(binfo))
        bind_data_pointer = pointer_from_objref(bind_data)
        _add_global_object(main_function, bind_data)
        duckdb_bind_set_bind_data(info, bind_data_pointer, @cfunction(_table_bind_cleanup, Cvoid, (Ptr{Cvoid},)))
    catch ex
        duckdb_bind_set_error(info, sprint(showerror, ex))
        return
    end
    return
end

#=
//===--------------------------------------------------------------------===//
// Table Function Init
//===--------------------------------------------------------------------===//
=#
struct InitInfo
    handle::duckdb_init_info
    main_function::Any

    function InitInfo(handle::duckdb_init_info, main_function)
        result = new(handle, main_function)
        return result
    end
end

function _table_init_function_generic(info::duckdb_init_info, init_fun::Function)
    try
        main_function = unsafe_pointer_to_objref(duckdb_init_get_extra_info(info))
        binfo = InitInfo(info, main_function)
        init_data = InfoWrapper(main_function, init_fun(binfo))
        init_data_pointer = pointer_from_objref(init_data)
        _add_global_object(main_function, init_data)
        duckdb_init_set_init_data(info, init_data_pointer, @cfunction(_table_bind_cleanup, Cvoid, (Ptr{Cvoid},)))
    catch ex
        duckdb_init_set_error(info, sprint(showerror, ex))
        return
    end
    return
end

function _table_init_function(info::duckdb_init_info)
    main_function = unsafe_pointer_to_objref(duckdb_init_get_extra_info(info))
    return _table_init_function_generic(info, main_function.init_func)
end

function _table_local_init_function(info::duckdb_init_info)
    main_function = unsafe_pointer_to_objref(duckdb_init_get_extra_info(info))
    return _table_init_function_generic(info, main_function.init_local_func)
end

function get_bind_info(info::InitInfo, ::Type{T})::T where {T}
    return unsafe_pointer_to_objref(duckdb_init_get_bind_data(info.handle)).info
end

function get_extra_data(info::InitInfo)
    return info.main_function.extra_data
end

function set_max_threads(info::InitInfo, max_threads)
    return duckdb_init_set_max_threads(info.handle, max_threads)
end

function get_projected_columns(info::InitInfo)::Vector{Int64}
    result::Vector{Int64} = Vector()
    column_count = duckdb_init_get_column_count(info.handle)
    for i in 1:column_count
        push!(result, duckdb_init_get_column_index(info.handle, i))
    end
    return result
end

function _empty_init_info(info::DuckDB.InitInfo)
    return missing
end

#=
//===--------------------------------------------------------------------===//
// Main Table Function
//===--------------------------------------------------------------------===//
=#
struct FunctionInfo
    handle::duckdb_function_info
    main_function::Any

    function FunctionInfo(handle::duckdb_function_info, main_function)
        result = new(handle, main_function)
        return result
    end
end

function get_bind_info(info::FunctionInfo, ::Type{T})::T where {T}
    return unsafe_pointer_to_objref(duckdb_function_get_bind_data(info.handle)).info
end

function get_init_info(info::FunctionInfo, ::Type{T})::T where {T}
    return unsafe_pointer_to_objref(duckdb_function_get_init_data(info.handle)).info
end

function get_local_info(info::FunctionInfo, ::Type{T})::T where {T}
    return unsafe_pointer_to_objref(duckdb_function_get_local_init_data(info.handle)).info
end

function _table_main_function(info::duckdb_function_info, chunk::duckdb_data_chunk)
    main_function::TableFunction = unsafe_pointer_to_objref(duckdb_function_get_extra_info(info))
    binfo::FunctionInfo = FunctionInfo(info, main_function)
    try
        main_function.main_func(binfo, DataChunk(chunk, false))
    catch ex
        duckdb_function_set_error(info, sprint(showerror, ex))
    end
    return
end

#=
//===--------------------------------------------------------------------===//
// Table Function
//===--------------------------------------------------------------------===//
=#
"""
DuckDB table function
"""
mutable struct TableFunction
    handle::duckdb_table_function
    bind_func::Function
    init_func::Function
    init_local_func::Function
    main_func::Function
    extra_data::Any
    global_objects::Set{Any}
    global_lock::ReentrantLock

    function TableFunction(
        name::AbstractString,
        parameters::Vector{LogicalType},
        bind_func::Function,
        init_func::Function,
        init_local_func::Function,
        main_func::Function,
        extra_data::Any,
        projection_pushdown::Bool
    )
        handle = duckdb_create_table_function()
        duckdb_table_function_set_name(handle, name)
        for param in parameters
            duckdb_table_function_add_parameter(handle, param.handle)
        end
        result = new(handle, bind_func, init_func, init_local_func, main_func, extra_data, Set(), ReentrantLock())
        finalizer(_destroy_table_function, result)

        duckdb_table_function_set_extra_info(handle, pointer_from_objref(result))
        duckdb_table_function_set_bind(handle, @cfunction(_table_bind_function, Cvoid, (duckdb_bind_info,)))
        duckdb_table_function_set_init(handle, @cfunction(_table_init_function, Cvoid, (duckdb_init_info,)))
        duckdb_table_function_set_local_init(handle, @cfunction(_table_local_init_function, Cvoid, (duckdb_init_info,)))
        duckdb_table_function_set_function(
            handle,
            @cfunction(_table_main_function, Cvoid, (duckdb_function_info, duckdb_data_chunk))
        )
        duckdb_table_function_supports_projection_pushdown(handle, projection_pushdown)

        return result
    end
end

function _destroy_table_function(func::TableFunction)
    # disconnect from DB
    if func.handle != C_NULL
        duckdb_destroy_table_function(func.handle)
    end
    return func.handle = C_NULL
end

function create_table_function(
    con::Connection,
    name::AbstractString,
    parameters::Vector{LogicalType},
    bind_func::Function,
    init_func::Function,
    main_func::Function,
    extra_data::Any = missing,
    projection_pushdown::Bool = false,
    init_local_func::Union{Missing, Function} = missing
)
    if init_local_func === missing
        init_local_func = _empty_init_info
    end
    fun = TableFunction(
        name,
        parameters,
        bind_func,
        init_func,
        init_local_func,
        main_func,
        extra_data,
        projection_pushdown
    )
    if duckdb_register_table_function(con.handle, fun.handle) != DuckDBSuccess
        throw(QueryException(string("Failed to register table function \"", name, "\"")))
    end
    push!(con.db.functions, fun)
    return
end

function create_table_function(
    con::Connection,
    name::AbstractString,
    parameters::Vector{DataType},
    bind_func::Function,
    init_func::Function,
    main_func::Function,
    extra_data::Any = missing,
    projection_pushdown::Bool = false,
    init_local_func::Union{Missing, Function} = missing
)
    parameter_types::Vector{LogicalType} = Vector()
    for parameter_type in parameters
        push!(parameter_types, create_logical_type(parameter_type))
    end
    return create_table_function(
        con,
        name,
        parameter_types,
        bind_func,
        init_func,
        main_func,
        extra_data,
        projection_pushdown,
        init_local_func
    )
end

function create_table_function(
    db::DB,
    name::AbstractString,
    parameters::Vector{LogicalType},
    bind_func::Function,
    init_func::Function,
    main_func::Function,
    extra_data::Any = missing,
    projection_pushdown::Bool = false,
    init_local_func::Union{Missing, Function} = missing
)
    return create_table_function(
        db.main_connection,
        name,
        parameters,
        bind_func,
        init_func,
        main_func,
        extra_data,
        projection_pushdown,
        init_local_func
    )
end

function create_table_function(
    db::DB,
    name::AbstractString,
    parameters::Vector{DataType},
    bind_func::Function,
    init_func::Function,
    main_func::Function,
    extra_data::Any = missing,
    projection_pushdown::Bool = false,
    init_local_func::Union{Missing, Function} = missing
)
    return create_table_function(
        db.main_connection,
        name,
        parameters,
        bind_func,
        init_func,
        main_func,
        extra_data,
        projection_pushdown,
        init_local_func
    )
end

max_stars_repo_name: taniabogatsch/duckdb

max_issues_repo_name: taniabogatsch/duckdb

max_forks_repo_name: taniabogatsch/duckdb


text:
using RedPitayaDAQServer
using PyPlot

# obtain the URL of the RedPitaya
include("config.jl")

rp = RedPitayaCluster([URLs[1]])

dec = 64
modulus = 4800
base_frequency = 125000000
periods_per_step = 5
samples_per_period = div(modulus, dec)
periods_per_frame = 50 # about 0.5 s frame length
frame_period = dec*samples_per_period*periods_per_frame / base_frequency
slow_dac_periods_per_frame = div(50, periods_per_step)

stopADC(rp)
masterTrigger(rp, false)
clearSequence(rp)

decimation(rp, dec)
samplesPerPeriod(rp, samples_per_period)
periodsPerFrame(rp, periods_per_frame)
passPDMToFastDAC(master(rp), true)

modeDAC(rp, "STANDARD")
frequencyDAC(rp,1,1, base_frequency / modulus)

freq = frequencyDAC(rp,1,1)
println(" frequency = $(freq)")
signalTypeDAC(rp, 1 , "SINE")
amplitudeDAC(rp, 1, 1, 0.1)
phaseDAC(rp, 1, 1, 0.0 ) # Phase has to be given in between 0 and 1

ramWriterMode(rp, "TRIGGERED")
triggerMode(rp, "EXTERNAL")

# Sequence
slowDACStepsPerFrame(rp, slow_dac_periods_per_frame)
numSlowDACChan(master(rp), 1)
fig = figure(1)
clf()

# Constant Sequence
amplitudeDAC(rp, 1, 1, 0.1) # Amplitude is set to zero after a sequence
lut = [0.2]
seq = ConstantSequence(lut, nothing, slow_dac_periods_per_frame, 1, computeRamping(master(rp), frame_period * 2, 0.5), computeRamping(master(rp), frame_period * 3, 1.0))
appendSequence(master(rp), seq)
success = prepareSequence(master(rp))
startADC(rp)
masterTrigger(rp, true)
sleep(0.1)

uCurrentFrame = readFrames(rp, 0, 6)
stopADC(rp)
masterTrigger(rp, false)
clearSequence(rp)

plot(vec(uCurrentFrame[:, 1, :, :]))
title("Ramping")
fig

max_stars_repo_name: tknopp/RedPitayaDAQServer

max_issues_repo_name: tknopp/RedPitayaDAQServer

max_forks_repo_name: tknopp/RedPitayaDAQServer


text:
const Maybe{T} = Union{T,Nothing}
const MaybeString = Maybe{AbstractString}
"""
    ValidationException

Thrown when parameters to generate an IBAN fail validation. Reports the problematic value
and the matching BBAN attribute

## Example
```julia
julia> iban_random(CountryCode = "GR", BankCode = "xxx")
ERROR: ValidationException value: "xxx"
invalid characters [IbanGen.BankCode]
```
"""
struct ValidationException <: Exception
    val::MaybeString
    msg::AbstractString
end

Base.showerror(io::IO, ex::ValidationException) =
    print(io, """ValidationException value: "$(ex.val)"\n$(ex.msg)""")

ensure(condition, val, msg) =
    !condition && throw(ValidationException(val, msg))


text:
using PyPlot, ParticleScattering
import JLD, Optim, LineSearches
input_dir = homedir()
output_dir = homedir()
Ns = 100
k0 = 2π
kin = 3*k0
l0 = 2π/k0
a1=0.3*l0; a2=0.1*l0; a3=5;
dmin = R_multipole*2*(a1+a2)
θ_i = 0.5π; ui = PlaneWave(θ_i)

width = 21l0
height = 7l0
myshapefun(N) = rounded_star(a1,a2,a3,N)
points = [range(0.0, stop=width, length=20) height*ones(20)]

if !isfile(joinpath(input_dir), "sim3data.jld")
    centers = randpoints(Ns, dmin, width, height, points)
else
    JLD.@load(joinpath(input_dir, "sim3data.jld"), centers)
end

N,errN = (934, 9.97040926753751e-7) #
#N,errN = minimumN(k0,kin,myshapefun, tol = 1e-6, N_points = 20_000)
shapes = [myshapefun(N)]
P,errP = (12, 8.538711552646218e-7)#
#P,errP = minimumP(k0, kin, shapes[1], tol = 1e-6, N_points = 20_000, P_min = 1, P_max = 120)

φs = zeros(Float64,Ns)
ids = ones(Int, Ns)

fmm_options = FMMoptions(true, acc = 6, nx = 9, method="pre")

divideSpace(centers, fmm_options; drawGroups = false)

draw_fig = true

# verify and draw
begin
    @assert verify_min_distance(shapes, centers, ids, points)
    if draw_fig
        figure()
        #draw shapes and points
        draw_shapes(shapes, ids, centers, φs)
        plot(points[:,1], points[:,2], "r*")
        tight_layout()
        axis("equal")
    end
end

border = shapes[1].R*[-1;1;-1;1] + [0.0; width; 0.0; height]

optim_options =  Optim.Options(f_tol = 1e-6,
                                iterations = 150,
                                store_trace = true,
                                extended_trace = false,
                                show_trace = true,
                                allow_f_increases = true)

optim_method = Optim.BFGS(;linesearch = LineSearches.BackTracking())

#precompile
test_max = optimize_φ(φs, points, P, ui, k0, kin, shapes, centers, ids,
            fmm_options, optim_options, optim_method; minimize = false)
optim_time = @elapsed begin
    test_max = optimize_φ(φs, points, P, ui, k0, kin, shapes, centers, ids,
                fmm_options, optim_options, optim_method; minimize = false)
end

sp_before = ScatteringProblem(shapes, ids, centers, φs)
Ez1 = plot_near_field(k0, kin, P, sp_before, ui,
                x_points = 600, y_points = 200, border = border, method="recurrence")
colorbar()
clim([0;5])

sp_after = ScatteringProblem(shapes, ids, centers, test_max.minimizer)
Ez2 = plot_near_field(k0, kin, P, sp_after, ui,
                x_points = 600, y_points = 200, border = border, method="recurrence")
colorbar()
clim([0;5])

inner_iters = length(test_max.trace)
fobj = -[test_max.trace[i].value for i=1:inner_iters]
gobj = [test_max.trace[i].g_norm for i=1:inner_iters]

figure()
plot(0:inner_iters-1, fobj, label="\$f_{\\mathrm{obj}}\$")
plot(0:inner_iters-1, gobj, label="\$\\Vert\\mathbf{g}_{\\mathrm{obj}}\\Vert\$")
legend(loc="best")
xlabel("Iterations")

JLD.@save joinpath(output_dir, "angle_optim2.jld")


text:
@with_kw mutable struct RewardModPOMDP{S, A, O} <: POMDP{S,A,O}
    pomdp::POMDP{S,A,O}
    reward_model
end

POMDPs.initialstate(mdp::RewardModPOMDP) = initialstate(mdp.pomdp)
POMDPs.initialobs(mdp::RewardModPOMDP, s) = initialobs(mdp.pomdp, s)

POMDPs.actions(mdp::RewardModPOMDP) = actions(mdp.pomdp)
POMDPs.actionindex(mdp::RewardModPOMDP, a) = actionindex(mdp.pomdp)

POMDPs.isterminal(mdp::RewardModPOMDP, s) = isterminal(mdp.pomdp, s)
POMDPs.discount(mdp::RewardModPOMDP) = discount(mdp.pomdp)

POMDPs.observation(mdp::RewardModPOMDP, s) = observation(mdp.pomdp, s)

render(mdp::RewardModPOMDP, s, a = nothing; kwargs...) = render(mdp.pomdp, s, a; kwargs...)
render(mdp::RewardModPOMDP; kwargs...) = render(mdp.pomdp; kwargs...)

function POMDPs.gen(mdp::RewardModPOMDP, s, a, rng::AbstractRNG = Random.GLOBAL_RNG; info=Dict())
    sp, o, r = gen(mdp.pomdp, s, a, rng, info=info)
    rnew = mdp.reward_model((s=s, a=a, sp=sp, o=o, r=r, info=info))
    return (sp=sp, o=o, r=rnew)
end

@with_kw mutable struct RewardModMDP{S, A} <: MDP{S,A}
    mdp::MDP{S,A}
    reward_model
end

POMDPs.initialstate(mdp::RewardModMDP) = initialstate(mdp.mdp)

POMDPs.actions(mdp::RewardModMDP) = actions(mdp.mdp)
POMDPs.actionindex(mdp::RewardModMDP, a) = actionindex(mdp.mdp)

POMDPs.isterminal(mdp::RewardModMDP, s) = isterminal(mdp.mdp, s)
POMDPs.discount(mdp::RewardModMDP) = discount(mdp.mdp)

render(mdp::RewardModMDP, s, a = nothing; kwargs...) = render(mdp.mdp, s, a; kwargs...)
render(mdp::RewardModMDP; kwargs...) = render(mdp.mdp; kwargs...)

function POMDPs.gen(mdp::RewardModMDP, s, a, rng::AbstractRNG = Random.GLOBAL_RNG; info=Dict())
    sp, r = gen(mdp.mdp, s, a, rng, info=info)
    rnew = mdp.reward_model((s=s, a=a, sp=sp, r=r, info=info))
    return (sp=sp, r=rnew)
end

RewardMod(mdp::T, reward_model) where T <: MDP = RewardModMDP(mdp, reward_model)
RewardMod(pomdp::T, reward_model) where T <: POMDP = RewardModPOMDP(pomdp, reward_model)


text:
using CSV, DataFrames, HDF5

"""
This function read the orginal porto taxi csv file and save all trips into
hdf5 file, each trip will be a 2xn matrix where n is the length of the trip.
The first row is longitude and second row is latitude.

The output hdf5 file will be used to construct vocabulary by `makeVocab!()` in
`SpatialRegionTools.jl`.
"""
function porto2h5(csvfile::String)
    df = CSV.read(csvfile)
    #df[:TIMESTAMP] = convert(Array{String}, df[:TIMESTAMP])
    #df[:MISSING_DATA] = convert(Array{String}, df[:MISSING_DATA])
    #df[:POLYLINE] = convert(Array{String, 1}, df[:POLYLINE])

    df = df[df.MISSING_DATA .== "False", :]
    sort!(df, [:TIMESTAMP])

    println("Processing $(size(df, 1)) trips...")
    ## writing in pure text
    #CSV.write("porto.csv", df[:POLYLINE])
    #open("porto.csv", "w") do f
    #    for s in df[:POLYLINE]
    #        write(f, s * "\n")
    #    end
    #end
    ## writing in numeric matrix with hdf5
    h5open("../data/porto.h5", "w") do f
        num = 0
        for trip in df.POLYLINE
            trip = Meta.parse(trip) |> eval
            tripLength = length(trip)
            tripLength == 0 && continue
            trip = hcat(trip...)
            num += 1
            f["/trips/$num"] = trip
            f["/timestamps/$num"] = collect(0:tripLength-1) * 15.0
            num % 100_000 == 0 && println("$num")
        end
        attrs(f)["num"] = num
        println("Saved $num trips.")
    end
end

function porto2standardcsv(portocsv::String)
    df = CSV.read(portocsv)
    df = df[df.MISSING_DATA .== "False", :]
    #sort!(df, [:TIMESTAMP])
    trips = DataFrame[]
    for (i, row) in enumerate(eachrow(df))
        gps = Meta.parse(row.POLYLINE) |> eval
        tripLength = length(gps)
        tripLength == 0 && continue
        gps = hcat(gps...)
        trip = DataFrame(tripid=fill(row.TRIP_ID, tripLength),
                         timestamps=row.TIMESTAMP .+ collect(0:tripLength-1) * 15,
                         lon=gps[1, :],
                         lat=gps[2, :])
        push!(trips, trip)
        i % 100_000 == 0 && println("Processed $i rows.")
    end
    #CSV.write("../data/porto-trips.csv", trips, header=[:tripid, :timestamps, :lon, :lat])
    trips
end

"""
Distorting a trip using Gaussian noise
"""
function distort(trip::Matrix{Float64}, rate::Float64; radius=50.0)
    noisetrip = copy(trip)
    for i = 1:size(noisetrip, 2)
        if rand() <= rate
            x, y = lonlat2meters(noisetrip[:, i]...)
            xnoise, ynoise = 2rand()-1, 2rand()-1
            normz = hypot(xnoise, ynoise)
            xnoise, ynoise = xnoise*radius/normz, ynoise*radius/normz
            noisetrip[:, i] = meters2lonlat(x + xnoise, y + ynoise) |> collect
        end
    end
    noisetrip
end

"""
Accepting one trip and producing its 10 different noise rate distorted variants
"""
function distort(trip::Matrix{Float64}, nsplit::Int)
    noisetrips = Matrix{Float64}[]

    for rate in 0:0.1:0.9
        noisetrip = distort(trip, rate)
        push!(noisetrips, noisetrip)
    end
    noisetrips
end

"""
Downsampling one trip, rate is dropping rate
"""
function downsampling(trip::Matrix{Float64}, rate::Float64)
    keep_idx = Int[1]
    for i = 2:size(trip, 2)-1
        rand() > rate && push!(keep_idx, i)
    end
    push!(keep_idx, size(trip, 2))
    copy(trip[:, keep_idx])
end

"""
Accepting one trip and producing its 9 different lowsampling rate variants
"""
function downsampling(trip::Matrix{Float64}, nsplit::Int)
    noisetrips = Matrix{Float64}[]
    dropping_rates = 0:0.1:0.8
    for rate in dropping_rates
        noisetrip = downsampling(trip, rate)
        push!(noisetrips, noisetrip)
    end
    noisetrips
end

"""
First downsampling and then distorting the trip, producing its 20 different variants
"""
function downsamplingDistort(trip::Matrix, nsplit::Int)
    noisetrips = Matrix{Float64}[]
    dropping_rates = [0, 0.2, 0.4, 0.5, 0.6]
    distorting_rates = [0, 0.2, 0.4, 0.6]
    for dropping_rate in dropping_rates
        noisetrip1 = downsampling(trip, dropping_rate)
        for distorting_rate in distorting_rates
            noisetrip2 = distort(noisetrip1, distorting_rate)
            push!(noisetrips, noisetrip2)
        end
    end
    noisetrips
end

"""
longitude, latitude to Web Mercator coordinate
"""
function lonlat2meters(lon::Float64, lat::Float64)
    semimajoraxis = 6378137.0
    east = lon * 0.017453292519943295
    north = lat * 0.017453292519943295
    t = sin(north)
    semimajoraxis * east, 3189068.5 * log((1 + t) / (1 - t))
end

function meters2lonlat(x::Float64, y::Float64)
    semimajoraxis = 6378137.0
    lon = x / semimajoraxis / 0.017453292519943295
    t = exp(y / 3189068.5)
    lat = asin((t - 1) / (t + 1)) / 0.017453292519943295
    lon, lat
end

#x, y = lonlat2meters(-8.7351,40.95)
#meters2lonlat(x, y)

#h5open("/tmp/test.h5", "w") do f
#    f["/trips/1"] = rand(10,2)
#    f["/trips/2"] = rand(30,2)
#    attrs(f)["num"] = 2
#    attrs(f)["/trips/1"] = 10
#    attrs(f)["/trips/2"] = 11
#end
#
#h5open("/tmp/test.h5", "r") do f
#    read(f["/trips/1"])
#    attrs(f)["/trips/1"] |> read
#end

#porto2h5("/Users/fineday/Downloads/train.csv")

#tic()
#tripLengths = Int[]
#h5open("preprocessing/porto.h5", "r") do f
#    num = read(attrs(f)["num"])
#    for i = 1:num
#        trip = read(f["/trips/$i"])
#        push!(tripLengths, size(trip, 2))
#        #i % 100_000 == 0 && println(i)
#    end
#end
#toc()
#h5open("porto.h5", "r") do fread
#    num = attrs(fread)["num"] |> read
#    h5open("/tmp/porto.h5", "w") do fwrite
#        attrs(fwrite)["num"] = num
#        for i = 1:num
#            trip = read(fread["/trips/$i"])
#            fwrite["/trips/$i"] = trip
#            tripLength = size(trip, 2)
#            fwrite["/timestamps/$i"] = collect(0:tripLength-1) * 15.0
#            i % 100_000 == 0 && println(i)
#        end
#    end
#end
#toc()

#filter(x-> x >= 20 && x <= 100, tripLengths) |> length
#sum(tripLengths)
#
#sum(tripLengths[1:800_000])

#h5open("porto.h5", "r") do fread
#    h5open("/tmp/porto.h5", "w") do fwrite
#        num = attrs(fread)["num"] |> read
#        attrs(fwrite)["num"] = num
#        for i = 1:num
#            fwrite["/trips/$i"] = fread["/trips/$i"] |> read |> transpose
#            i % 100_000 == 0 && println(i)
#        end
#    end
#end

#tripLengths = Int[]
#h5open("../preprocessing/porto.h5", "r") do f
#    num = read(attrs(f)["num"])
#    for i = 1:num
#        trip = read(f["/trips/$i"])
#        tripLength = size(trip, 2)
#        push!(tripLengths, tripLength)
#        #tripLength >= 50 && push!(tripLengths, tripLength)
#        #i % 100_000 == 0 && println(i)
#    end
#end
#t = filter(x -> 30<=x, tripLengths)
#filter(x -> 60 <= x, t[800000:end])
#sum(tripLengths)
#mean(tripLengths)
#tripLengths
#maximum(tripLengths) #3881
#129_535_632 / 1527348
#trip = rand(40:50, 2, 40)*1.0
#distort(trip, 10)
#downsampling(trip, 10)
#sdownsamplingDistort(trip, 5)

#lonlat2meters(-8.735152,40.953673)

max_stars_repo_name: lzzppp/DERT

max_issues_repo_name: lzzppp/DERT

max_forks_repo_name: lzzppp/DERT


text:
struct Point{T}
	r::T
	θ::T
	ϕ::T
	x
 	y
	z
end

function(p::Point)()
	x = p.r * cos(p.θ) * cos(p.ϕ)
	y = p.r * cos(p.θ) * sin(p.ϕ)
	z = p.r * sin(p.θ)
	return Point(p.r, p.θ, p.ϕ, x, y, z)
end

function get_versor(p::Point{Float64})
	return Point{Float64}(1, p.θ, p.ϕ, p.x / p.r, p.y / p.r, p.z / p.r)
end

function diff_cart(p::Point{Float64}, p_2::Point{Float64})
	return Point{Float64}(0, 0, 0, p.x-p_2.x, p.y-p_2.y, p.z-p_2.z)
end

Base.:+(a::Point{Float64}, b::Point{Float64}) = Point{Float64}(a.r+b.r, a.θ+b.θ, a.ϕ+b.ϕ, undef, undef, undef)()
Base.:-(a::Point{Float64}, b::Point{Float64}) = Point{Float64}(a.r-b.r, a.θ-b.θ, a.ϕ-b.ϕ, undef, undef, undef)()
Base.:*(a::Point{Float64}, b::Point{Float64}) = Point{Float64}(a.r*b.r, a.θ*b.θ, a.ϕ*b.ϕ, undef, undef, undef)()

# Non e` una vera divisione e` quello che mi serve per ottenere il versore partendo dal vettore
# Forse e` meglio fare una funzione del tipo get_versor -> a.r / a.r , a.θ, a.ϕ, a.x / a.r, a.y /a.r , a.z / a.r
# in questo modo si ottiene direttamente il versore nella direzione di a

max_stars_repo_name: gruffoc/AFO

max_issues_repo_name: gruffoc/AFO

max_forks_repo_name: gruffoc/AFO


text:
using ArrayRotations
using ArrayRotations: _copy!, _left_shift!, _right_shift!, _interchange_left_large!, _interchange_right_large!
using Test

@testset "_copy!" begin
    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    B = zeros(Int, 20)
    _copy!(B, 1, A, 1, 10)
    @test B == [7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    reverse!(A)
    _copy!(B, 11, A, 1, 10)
    @test B == [7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 10, 9, 8, 7]

    B = zeros(Int, 100)
    A = collect(1:100)
    for i in 1:96
       _copy!(B, i, A, i, 5)
    end
    @test A == B
end

@testset "_*_shift!" begin
    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    @test _left_shift!(A, 5) == [1, 2, 3, 4, 5, 6, 3, 4, 5, 6]
    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    _right_shift!(A, 4) == [7, 8, 9, 10, 1, 2, 7, 8, 9, 10]
end

@testset "AuxRotation" begin
    aux = AuxRotation(Int[])
    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    @test rotate!(aux, A, 5) == collect(1:10)
    A = [7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6]
    @test rotate!(aux, A, 10) == collect(1:15)
end

@testset "RevRotation" begin
    aux = RevRotation()
    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    @test rotate!(aux, A, 5) == collect(1:10)
    A = [7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6]
    @test rotate!(aux, A, 10) == collect(1:15)
end

#=
@testset "Interchange_*_large" begin
    A = [4, 5, 6, 7, 8, 9, 1, 2, 3]
    @test _interchange_left_large!(copy(A), 7) == [1, 2, 3, 4, 5, 6, 1, 2, 3]
    A = [7, 8, 9, 1, 2, 3, 4, 5, 6]
    @test _interchange_right_large!(copy(A), 4) == [1, 2, 3, 1, 2, 3, 7, 8, 9]
end
=#

@testset "BridgeRotation" begin
    bridge = BridgeRotation(Int[])
    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    @test rotate!(bridge, A, 5) == collect(1:10)
    A = [7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6]
    @test rotate!(bridge, A, 10) == collect(1:15)
end

@testset "GriesMillsRotation" begin
    gm = GriesMillsRotation()

    A = [3, 1, 2]
    @test rotate!(gm, A, 2) == collect(1:3)
    A = [2, 3, 1]
    @test rotate!(gm, A, 3) == collect(1:3)

    A = [7, 8, 9, 10, 1, 2, 3, 4, 5, 6]
    @test rotate!(gm, A, 5) == collect(1:10)
    A = [7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6]
    @test rotate!(gm, A, 10) == collect(1:15)
end


text:
using TypeClasses

# we assume that Pair(a::A, b::B) always constructs the most specific Pair type Pair{A, B}
# TODO is this really the case? It could be, but is it true?

# MonoidAlternative
# =================

TypeClasses.neutral(::Type{Pair{F, S}}) where {F, S} = neutral(F) => neutral(S)
TypeClasses.combine(p1::Pair, p2::Pair) = combine(p1.first, p2.first) => combine(p1.second, p2.second)

# we don't implement orelse, as it is commonly meant on container level, but there is no obvious failure semantics here


text:
struct AAProperties <: AbstractVector{Float64}
    charge::Float64
    hydropathy::Float64   # from https://www.sciencedirect.com/science/article/pii/0022283682905150?via%3Dihub & https://en.wikipedia.org/wiki/Amino_acid
    volume::Float64       # from http://proteinsandproteomics.org/content/free/tables_1/table08.pdf (van der Waals volume in Å³)
end
Base.size(v::AAProperties) = (3,)
Base.getindex(v::AAProperties, i::Int) = i == 1 ? v.charge :
                                         i == 2 ? v.hydropathy :
                                         i == 3 ? v.volume : Base.throw_boundserror(v, i)
const aa_properties = Dict(
    Residue('A') => AAProperties(0, 1.8, 67),
    Residue('R') => AAProperties(1, -4.5, 148),
    Residue('N') => AAProperties(0, -3.5, 96),
    Residue('D') => AAProperties(-1, -3.5, 91),
    Residue('C') => AAProperties(0, 2.5, 86),
    Residue('Q') => AAProperties(0, -3.5, 114),
    Residue('E') => AAProperties(-1, -3.5, 109),
    Residue('G') => AAProperties(0, -0.4, 48),
    Residue('H') => AAProperties(0.1, -3.2, 118),   # at pH 7.4, His has a 10% chance of being charged
    Residue('I') => AAProperties(0, 4.5, 124),
    Residue('L') => AAProperties(0, 3.8, 124),
    Residue('K') => AAProperties(1, -3.9, 135),
    Residue('M') => AAProperties(0, 1.9, 124),
    Residue('F') => AAProperties(0, 2.8, 135),
    Residue('P') => AAProperties(0, -1.6, 90),
    Residue('S') => AAProperties(0, -0.8, 73),
    Residue('T') => AAProperties(0, -0.7, 93),
    Residue('W') => AAProperties(0, -0.9, 163),
    Residue('Y') => AAProperties(0, -1.3, 141),
    Residue('V') => AAProperties(0, 4.2, 105),
)

# z-score the amino acid features using the full covariance matrix
featμ = sum(p for (r, p) in aa_properties)/length(aa_properties)
featC = sum((Δp = p - featμ; Δp*Δp') for (r, p) in aa_properties)/(length(aa_properties)-1)
featChol = cholesky(featC)

const aa_properties_zscored = Dict(r => SVector{3}(featChol.L \ (p - featμ)) for (r, p) in aa_properties)


text:
export PlotShd

"""

    PlotShd(filename)

Plot transmission loss field

# Arguments
- `filename`: *.shd

# Output
- Heatmap transmission loss

# Example
```julia-repl
julia> PlotShd("Case1.shd")
```

"""
function PlotShd(filename::String;
        xlabs::String = "Distance, m",
        ylabs::String = "Transmission loss, dB",
        cblabs::String = "TL, dB",
        climb = (40,80))

        pressure, Pos_r_r, Pos_r_z = read_shd(filename)

        tlt = abs.(pressure)
        tlt = -20.0f0 * log10.(tlt)

        #seekend(io)

        heatmap(Pos_r_r[:,1],Pos_r_z[:,1],  tlt,
                clim= climb,
                c=cgrad(:Paired_6,rev = true),
                colorbartitle = cblabs,
                xlabel=xlabs, ylabel=ylabs)
        yflip!(true)

end # PlotShd function


text:
export @oneapi, zefunction, kernel_convert

macro oneapi(ex...)
    call = ex[end]
    kwargs = ex[1:end-1]

    # destructure the kernel call
    Meta.isexpr(call, :call) || throw(ArgumentError("second argument to @oneapi should be a function call"))
    f = call.args[1]
    args = call.args[2:end]

    code = quote end
    vars, var_exprs = assign_args!(code, args)

    # group keyword argument
    macro_kwargs, compiler_kwargs, call_kwargs, other_kwargs =
        split_kwargs(kwargs, [:launch], [:name], [:groups, :items, :queue])
    if !isempty(other_kwargs)
        key,val = first(other_kwargs).args
        throw(ArgumentError("Unsupported keyword argument '$key'"))
    end

    # handle keyword arguments that influence the macro's behavior
    launch = true
    for kwarg in macro_kwargs
        key,val = kwarg.args
        if key == :launch
            isa(val, Bool) || throw(ArgumentError("`launch` keyword argument to @cuda should be a constant value"))
            launch = val::Bool
        else
            throw(ArgumentError("Unsupported keyword argument '$key'"))
        end
    end
    if !launch && !isempty(call_kwargs)
        error("@oneapi with launch=false does not support launch-time keyword arguments; use them when calling the kernel")
    end

    # FIXME: macro hygiene wrt. escaping kwarg values (this broke with 1.5)
    #        we esc() the whole thing now, necessitating gensyms...
    @gensym f_var kernel_f kernel_args kernel_tt kernel

    # convert the arguments, call the compiler and launch the kernel
    # while keeping the original arguments alive
    push!(code.args,
        quote
            $f_var = $f
            GC.@preserve $(vars...) $f_var begin
                local $kernel_f = $kernel_convert($f_var)
                local $kernel_args = map($kernel_convert, ($(var_exprs...),))
                local $kernel_tt = Tuple{map(Core.Typeof, $kernel_args)...}
                local $kernel = $zefunction($kernel_f, $kernel_tt; $(compiler_kwargs...))
                if $launch
                    $kernel($(var_exprs...); $(call_kwargs...))
                end
                $kernel
            end
         end)
    return esc(code)
end

## argument conversion

struct KernelAdaptor end

# convert oneL0 host pointers to device pointers
Adapt.adapt_storage(to::KernelAdaptor, p::ZePtr{T}) where {T} = reinterpret(Ptr{T}, p)

# Base.RefValue isn't GPU compatible, so provide a compatible alternative
struct ZeRefValue{T} <: Ref{T}
  x::T
end
Base.getindex(r::ZeRefValue) = r.x
Adapt.adapt_structure(to::KernelAdaptor, r::Base.RefValue) = ZeRefValue(adapt(to, r[]))

"""
    kernel_convert(x)

This function is called for every argument to be passed to a kernel, allowing it to be
converted to a GPU-friendly format. By default, the function does nothing and returns the
input object `x` as-is.

Do not add methods to this function, but instead extend the underlying Adapt.jl package and
register methods for the the `oneAPI.KernelAdaptor` type.
"""
kernel_convert(arg) = adapt(KernelAdaptor(), arg)

## abstract kernel functionality

abstract type AbstractKernel{F,TT} end

@generated function call(kernel::AbstractKernel{F,TT}, args...; call_kwargs...) where {F,TT}
    sig = Base.signature_type(F, TT)
    args = (:F, (:( args[$i] ) for i in 1:length(args))...)

    # filter out ghost arguments that shouldn't be passed
    predicate = if VERSION >= v"1.5.0-DEV.581"
        dt -> isghosttype(dt) || Core.Compiler.isconstType(dt)
    else
        dt -> isghosttype(dt)
    end
    to_pass = map(!predicate, sig.parameters)
    call_t =                  Type[x[1] for x in zip(sig.parameters,  to_pass) if x[2]]
    call_args = Union{Expr,Symbol}[x[1] for x in zip(args, to_pass)            if x[2]]

    # replace non-isbits arguments (they should be unused, or compilation would have failed)
    for (i,dt) in enumerate(call_t)
        if !isbitstype(dt)
            call_t[i] = Ptr{Any}
            call_args[i] = :C_NULL
        end
    end

    # finalize types
    call_tt = Base.to_tuple_type(call_t)

    quote
        Base.@_inline_meta

        onecall(kernel.fun, $call_tt, $(call_args...); call_kwargs...)
    end
end

## host-side kernels

struct HostKernel{F,TT} <: AbstractKernel{F,TT}
    fun::ZeKernel
end

## host-side API

function zefunction(f::Core.Function, tt::Type=Tuple{}; name=nothing, kwargs...)
    dev = device()
    cache = get!(()->Dict{UInt,Any}(), zefunction_cache, dev)
    source = FunctionSpec(f, tt, true, name)
    target = SPIRVCompilerTarget(; kwargs...)
    params = oneAPICompilerParams()
    job = CompilerJob(target, source, params)
    GPUCompiler.cached_compilation(cache, job,
                                   zefunction_compile, zefunction_link)::HostKernel{f,tt}
end

const zefunction_cache = Dict{Any,Any}()

function zefunction_compile(@nospecialize(job::CompilerJob))
    mi, mi_meta = GPUCompiler.emit_julia(job)
    ir, ir_meta = GPUCompiler.emit_llvm(job, mi)
    asm, asm_meta = GPUCompiler.emit_asm(job, ir; format=LLVM.API.LLVMObjectFile)

    return (image=asm, entry=LLVM.name(ir_meta.entry))
end

# JIT into an executable kernel object
function zefunction_link(@nospecialize(job::CompilerJob), compiled)
    ctx = context()
    dev = device()
    mod = ZeModule(ctx, dev, compiled.image)
    kernel = kernels(mod)[compiled.entry]

    return HostKernel{job.source.f,job.source.tt}(kernel)
end

@inline function onecall(kernel::ZeKernel, tt, args...; groups::ZeDim=1, items::ZeDim=1,
                         queue::ZeCommandQueue=global_queue(context(), device()))
    for (i, arg) in enumerate(args)
        arguments(kernel)[i] = arg
    end

    groupsize!(kernel, items)
    execute!(queue) do list
        append_launch!(list, kernel, groups)
    end
end

function (kernel::HostKernel)(args...; kwargs...)
    call(kernel, map(kernel_convert, args)...; kwargs...)
end

## TODO: device-side kernels


text:
#### GitHubDockerCI

"""
    GitHubDockerCI

# Fields
$(DocStringExtensions.FIELDS)
"""
Base.@kwdef struct GitHubDockerCI
    "Organization name"
    org::String
    "Package name (with extension)"
    pkg_name::String
    "Package name (no extension)"
    pkg_name_no_ext::String = replace(pkg_name, ".jl" => "")
    "Julia version"
    julia_version::String
    "Build name"
    build_name::String="dockerci"
    "Checks"
    checks::String="$(build_name) ($(julia_version), ubuntu-latest)"
end

function yaml(ci::GitHubDockerCI)
    return "name: DockerCI

on:
  push:
    branches: [ master, staging, trying ]
  pull_request:
    branches: [ master, staging, trying ]

jobs:
  $(ci.build_name):
    runs-on: \${{ matrix.os }}
    strategy:
      matrix:
        julia-version: [$(ci.julia_version)]
        os: [ubuntu-latest]

    steps:
    - uses: actions/checkout@v2
    - name: Build and test
      run: |
        docker build . --file Dockerfile --tag $(ci.pkg_name_no_ext):PR
        docker images
        docker run $(ci.pkg_name_no_ext):PR
"
end

max_stars_repo_name: charleskawczynski/PkgTemplates

max_issues_repo_name: charleskawczynski/PkgTemplates

max_forks_repo_name: charleskawczynski/PkgTemplates


text:
const INTERACT_OUTPUT_KEYWORDS = """
- `ruleset::Ruleset`: the ruleset to run in the interface simulations.

#### `DynamicGrids.Extent` keywords:

$(DynamicGrids.EXTENT_KEYWORDS)

An `Extent` object can be also passed to the `extent` keyword, and other keywords will be ignored.

#### `DynamicGrids.GraphicConfig` keywords:

$(DynamicGrids.GRAPHICCONFIG_KEYWORDS)

A `GraphicConfig` object can be also passed to the `graphicconfig` keyword, and other keywords will be ignored.

#### `DynamicGrids.ImageConfig` keywords:

$(DynamicGrids.IMAGECONFIG_KEYWORDS)

(See DynamicGrids.jl docs for more details)
"""

"""
    AbstractInteractOutput

Abstract supertype of Interact outputs including `InteractOuput` and `ElectronOutput`
"""
abstract type AbstractInteractOutput{T,F} <: ImageOutput{T,F} end

const output_css = Asset(joinpath(@__DIR__, "..", "assets", "style.css"))

"""
    InteractOutput <: AbstractInteractOutput

    InteractOutput(init; ruleset, kw...)

An `Output` for Atom/Juno and Jupyter notebooks,
and the back-end for [`ElectronOutput`](@ref) and [`ServerOutput`](@ref).

# Arguments:

- `init`: initialisation `AbstractArray` or `NamedTuple` of `AbstractArray`.

# Keywords

$INTERACT_OUTPUT_KEYWORDS
"""
mutable struct InteractOutput{T,F<:AbstractVector{T},E,GC,IC,RS<:Ruleset,Pa,IM,TI} <: AbstractInteractOutput{T,F}
    frames::F
    running::Bool
    extent::E
    graphicconfig::GC
    imageconfig::IC
    ruleset::RS
    page::Pa
    image_obs::IM
    t_obs::TI
end
# Most defaults are passed in from the generic ImageOutput constructor
function InteractOutput(;
    frames, running, extent, graphicconfig, imageconfig, ruleset,
    extrainit=Dict(), throttle=0.1, interactive=true, kw...
)
    # Observables that update during the simulation
    image_obs = Observable{Any}(dom"div"())
    t_obs = Observable{Int}(1)

    # Page and output construction
    page = Scope()
    output = InteractOutput(
        frames, running, extent, graphicconfig, imageconfig, ruleset, page, image_obs, t_obs
    )

    # Widgets
    timedisplay = _time_text(t_obs)
    controls = _control_widgets(output, ruleset, extrainit)
    sliders = _rule_sliders(ruleset, throttle, interactive)

    # Put it all together into a web page
    output.page = Scope(
        imports=[output_css],
        dom=vbox(
            dom"div.resizable"(output.image_obs; title="Drag bottom right to resize"),
            timedisplay,
            controls,
            sliders
        ),
    )

    # Initialise image Observable simdata = DynamicGrids.SimData(extent, ruleset) image_obs[] = _webimage(DG.render!(output, simdata))

    return output
end

# Base interface
Base.display(o::InteractOutput) = display(o.page)
Base.show(o::InteractOutput) = show(o.page)

# DynamicGrids interface
DynamicGrids.isasync(o::InteractOutput) = true
function DynamicGrids.showimage(image::AbstractArray, o::InteractOutput, data::AbstractSimData)
    # Update simulation image, makeing sure any errors are printed in the REPL
    try
        o.t_obs[] = currentframe(data)
        o.image_obs[] = _webimage(image)
    catch e
        println(e)
    end
    return nothing
end

_webimage(image) = dom"div"(image)

# Widget buliding

function _time_text(t_obs::Observable)
    timedisplay = Observable{Any}(dom"div"("0"))
    map!(timedisplay, t_obs) do t
        dom"div"(string(t))
    end
    return timedisplay
end

function _rule_sliders(ruleset, throttle, interactive)
    if interactive
        return InteractModels.attach_sliders!(ruleset; throttle=throttle, submodel=Rule)
    else
        return dom"div"()
    end
end

function _control_widgets(o::InteractOutput, ruleset, extrainit)
    # We use the init dropdown for the simulation init, even if we don't
    # show the dropdown because it only has 1 option.
    extrainit[:init] = deepcopy(init(o))
    init_dropdown = dropdown(extrainit, value=extrainit[:init], label="Init")
    maybe_init_dropdown = length(extrainit) > 1 ? (init_dropdown,) : ()

    fps_slider = slider(1:200, value=fps(o), label="FPS")

    # Buttons
    sim = button("sim")
    resume = button("resume")
    stop = button("stop")

    # Control mappings. Make errors visible in the console.
    on(observe(sim)) do _
        try
            !DG.isrunning(o) && sim!(o, ruleset; init=init_dropdown[])
        catch e
            println(e)
        end
    end
    on(observe(resume)) do _
        try
            !DG.isrunning(o) && resume!(o, ruleset; tstop=last(tspan(o)))
        catch e
            println(e)
        end
    end
    on(observe(stop)) do _
        try
            DG.setrunning!(o, false)
        catch e
            println(e)
        end
    end
    on(observe(fps_slider)) do fps
        try
            DG.setfps!(o, fps)
            DG.settimestamp!(o, o.t_obs[])
        catch e
            println(e)
        end
    end

    return hbox(sim, resume, stop, fps_slider, maybe_init_dropdown...)
end


text:
# # unfished
# add_theme(:ggplot2_base,
#     bglegend = _invisible,
#     fg       = :white,
#     fglegend = _invisible,
#     fgguide  = :black)
#
# add_theme(:ggplot2,
#           base = :ggplot2_base,
#           bginside = :lightgray,
#           fg       = :lightgray,
#           fgtext   = :gray,
#           fglegend = :gray,
#           fgguide  = :black)
#
# add_theme(:ggplot2_grey, base = :ggplot2)
#
# add_theme(:ggplot2_bw,
#           base = :ggplot2_base,
#           bginside = :white,
#           fg       = :black,
#           fgtext   = :lightgray,
#           fglegend = :lightgray,
#           fgguide  = :black)

const _ggplot_colors = Dict(
    :gray92 => RGB(fill(0.92, 3)...),
    :gray20 => RGB(fill(0.2, 3)...),
    :gray30 => RGB(fill(0.3, 3)...)
    )

_themes[:ggplot2] = PlotTheme(
    ## Background etc
    bg = :white,
    bginside = _ggplot_colors[:gray92],
    bglegend = _ggplot_colors[:gray92],
    fglegend = :white,
    fgguide = :black,
    widen=true,
    ## Axes / Ticks
    #framestyle = :grid,
    #foreground_color_tick = _ggplot_colors[:gray20], # tick color not yet implemented
    foreground_color_axis = _ggplot_colors[:gray20], # tick color
    tick_direction=:out,
    foreground_color_border =:white, # axis color
    foreground_color_text = _ggplot_colors[:gray30], # tick labels
    gridlinewidth = 1,
    #tick label size = *0.8,
    ### Grid
    foreground_color_grid = :white,
    gridalpha = 1,
    ### Minor Grid
    minorgrid = true,
    minorgridalpha = 1,
    minorgridlinewidth=0.5, # * 0.5
    foreground_color_minor_grid=:white,
    #foreground_color_minortick=:white, ## not yet implemented
    minorticks = 2,
    ## Lines and markers
    markerstrokealpha = 0,
    markerstrokewidth = 0
    #showaxis= :false
    )


text:
"""
For observed data without missings.

# Constructor

    SemObservedData(;
        specification,
        data,
        meanstructure = false,
        obs_colnames = nothing,
        kwargs...)

# Arguments
- `specification`: either a `RAMMatrices` or `ParameterTable` object (1)
- `data`: observed data
- `meanstructure::Bool`: does the model have a meanstructure?
- `obs_colnames::Vector{Symbol}`: column names of the data (if the object passed as data does not have column names, i.e. is not a data frame)

# Extended help
## Interfaces
- `n_obs(::SemObservedData)` -> number of observed data points
- `n_man(::SemObservedData)` -> number of manifest variables

- `get_data(::SemObservedData)` -> observed data
- `obs_cov(::SemObservedData)` -> observed.obs_cov
- `obs_mean(::SemObservedData)` -> observed.obs_mean
- `data_rowwise(::SemObservedData)` -> observed data, stored as vectors per observation

## Implementation
Subtype of `SemObserved`

## Remarks
(1) the `specification` argument can also be `nothing`, but this turns of checking whether
the observed data/covariance columns are in the correct order! As a result, you should only
use this if you are shure your observed data is in the right format.

## Additional keyword arguments:
- `spec_colnames::Vector{Symbol} = nothing`: overwrites column names of the specification object
- `compute_covariance::Bool ) = true`: should the covariance of `data` be computed and stored?
- `rowwise::Bool = false`: should the data be stored also as vectors per observation
"""
struct SemObservedData{A, B, C, D, O, R} <: SemObserved
    data::A
    obs_cov::B
    obs_mean::C
    n_man::D
    n_obs::O
    data_rowwise::R
end

# error checks
function check_arguments_SemObservedData(kwargs...)
    # data is a data frame,

end

function SemObservedData(;
        specification,
        data,

        obs_colnames = nothing,
        spec_colnames = nothing,

        meanstructure = false,
        compute_covariance = true,

        rowwise = false,

        kwargs...)

    if isnothing(spec_colnames) spec_colnames = get_colnames(specification) end

    if !isnothing(spec_colnames)
        if isnothing(obs_colnames)
            try
                data = data[:, spec_colnames]
            catch
                throw(ArgumentError(
                    "Your `data` can not be indexed by symbols. "*
                    "Maybe you forgot to provide column names via the `obs_colnames = ...` argument.")
                    )
            end
        else
            if data isa DataFrame
                throw(ArgumentError(
                    "You passed your data as a `DataFrame`, but also specified `obs_colnames`. "*
                    "Please make shure the column names of your data frame indicate the correct variables "*
                    "or pass your data in a different format.")
                    )
            end

            if !(eltype(obs_colnames) <: Symbol)
                throw(ArgumentError("please specify `obs_colnames` as a vector of Symbols"))
            end

            data = reorder_data(data, spec_colnames, obs_colnames)
        end
    end

    if data isa DataFrame
        data = Matrix(data)
    end

    n_obs, n_man = Float64.(size(data))

    if compute_covariance
        obs_cov = Statistics.cov(data)
    else
        obs_cov = nothing
    end

    # if a meanstructure is needed, compute observed means
    if meanstructure
        obs_mean = vcat(Statistics.mean(data, dims = 1)...)
    else
        obs_mean = nothing
    end

    if rowwise
        data_rowwise = [data[i, :] for i = 1:convert(Int64, n_obs)]
    else
        data_rowwise = nothing
    end

    return SemObservedData(data, obs_cov, obs_mean, n_man, n_obs, data_rowwise)
end

############################################################################################
### Recommended methods
############################################################################################

n_obs(observed::SemObservedData) = observed.n_obs
n_man(observed::SemObservedData) = observed.n_man

############################################################################################
### additional methods
############################################################################################

get_data(observed::SemObservedData) = observed.data
obs_cov(observed::SemObservedData) = observed.obs_cov
obs_mean(observed::SemObservedData) = observed.obs_mean
data_rowwise(observed::SemObservedData) = observed.data_rowwise

############################################################################################
### Additional functions
############################################################################################

# reorder data -----------------------------------------------------------------------------
function reorder_data(data::AbstractArray, spec_colnames, obs_colnames)
    if spec_colnames == obs_colnames
        return data
    else
        new_position = [findall(x .== obs_colnames)[1] for x in spec_colnames]
        data = data[:, new_position]
        return data
    end
end


text:
"""
Transformation associated with a Generalized Linear Model (GLM). Maps an input
`x` to a random variable with expected value equal to `g(x)` where g(⋅) is a
inverse link function.
"""
immutable GeneralizedLinearTransformation{ST<:StochasticTransformation}
    noise::ST
    invlink::Transformation
end

# constructors
function GeneralizedLinearTransformation{D<:Distribution}(d::D)
    tfm = StochasticTransformation(d)
    invlink = cannonical_inv_link(d)
    GeneralizedLinearTransformation(tfm,invlink)
end
function GeneralizedLinearTransformation{D<:Distribution, T<:Transformation}(d::D, invlink::T)
    tfm = StochasticTransformation(d)
    GeneralizedLinearTransformation(tfm,invlink)
end

# functions
function transform(tfm::GeneralizedLinearTransformation, x)
    transform(tfm.noise, transform(tfm.invlink, x))
end
function rand(tfm::GeneralizedLinearTransformation, x)
    rand(tfm.noise, transform(tfm.invlink, x))
end

# cannonical inverse link functions
cannonical_inv_link(::Normal) = IdentityTransformation()
cannonical_inv_link(::Bernoulli) = LogisticTransformation()
cannonical_inv_link(::Poisson) = ExpTransformation()
cannonical_inv_link(::NegativeBinomial) = ExpTransformation()


text:
function are_natural_followers(::Type{EdgeWithNodeInfo}, arg0::EdgeWithNodeInfo, arg1::EdgeWithNodeInfo)
    return jcall(EdgeWithNodeInfo, "areNaturalFollowers", jboolean, (EdgeWithNodeInfo, EdgeWithNodeInfo), arg0, arg1)
end

function is_processed(obj::EdgeWithNodeInfo)
    return jcall(obj, "isProcessed", jboolean, ())
end

function result_from_a_split(::Type{EdgeWithNodeInfo}, arg0::EdgeWithNodeInfo, arg1::EdgeWithNodeInfo)
    return jcall(EdgeWithNodeInfo, "resultFromASplit", jboolean, (EdgeWithNodeInfo, EdgeWithNodeInfo), arg0, arg1)
end

function set_processed(obj::EdgeWithNodeInfo, arg0::jboolean)
    return jcall(obj, "setProcessed", void, (jboolean,), arg0)
end


text:
using DashHtmlComponents

function Chapter(name::String, href::Union{Nothing, String}, caption::Union{Nothing, String})
    divTitle = html_a(
        name,
        href = href,
        id = href,
        className = "toc--chapter-link"
    )
    divCaption = html_small(
        className = "toc--chapter-content",
        children = dcc_markdown(caption),
        style = (display = "block", marginTop = "3px",)
    )
    if !isnothing(caption)
        return(
            html_div(
                className = "toc--chapter",
                style = (marginTop = "10px",),
                children = (divTitle, divCaption,)
            )
        )
    else
        return(
            html_div(
                className = "toc--chapter",
                style = (marginTop = "10px",),
                children = (divTitle)
            )
        )
    end
end

max_stars_repo_name: joelostblom/dash-docs

max_issues_repo_name: joelostblom/dash-docs

max_forks_repo_name: joelostblom/dash-docs


text:
# This file was generated, do not modify it. # hide
mcr = misclassification_rate(predict_mode(mach, Xtrain), ytrain)
println(rpad("MNC mcr:", 10), round(mcr, sigdigits=3))


text:
abstract type AbstractPoint end
abstract type CartesianPoint <: AbstractPoint end
abstract type WGS84Point <: AbstractPoint  end

Base.:(==)(x::AbstractPoint, y::AbstractPoint) = x.srid == y.srid && x.coords == y.coords

mutable struct Point <: AbstractPoint
    srid::Integer
    coords::Array{Float64}
end

mutable struct CartesianPoint2 <: CartesianPoint
    srid::Integer
    coords::Array{Float64}
    x::Float64
    y::Float64
end

mutable struct CartesianPoint3 <: CartesianPoint
    srid::Integer
    coords::Array{Float64}
    x::Float64
    y::Float64
    z::Float64
end

CartesianPoint(x::Number,y::Number) = CartesianPoint2(7203, [x,y], x, y)
CartesianPoint(x::Number,y::Number,z::Number) = CartesianPoint3(9157, [x,y,z], x, y, z)
CartesianPoint(p::Tuple{Number,Number}) = CartesianPoint(p[1], p[2])
CartesianPoint(p::Tuple{Number,Number, Number}) = CartesianPoint(p[1], p[2], p[3])

mutable struct WGS84Point2 <: WGS84Point
    srid::Integer
    coords::Array{Float64}
    longitude::Float64
    latitude::Float64
    x::Float64
    y::Float64
end

mutable struct WGS84Point3 <: WGS84Point
    srid::Integer
    coords::Array{Float64}
    longitude::Float64
    latitude::Float64
    height::Float64
    x::Float64
    y::Float64
    z::Float64
end

WGS84Point(longitude::Number,latitude::Number) = WGS84Point2(4326, [longitude,latitude], longitude, latitude, longitude, latitude)
WGS84Point(longitude::Number,latitude::Number,height::Number) = WGS84Point3(4979, [longitude,latitude,height], longitude, latitude, height, longitude, latitude, height)
WGS84Point(p::Tuple{Number,Number}) = WGS84Point(p[1], p[2])
WGS84Point(p::Tuple{Number,Number, Number}) = WGS84Point(p[1], p[2], p[3])

function hydrate_point(srid::Integer, coordinates::Vector)
    if srid == 7203
        CartesianPoint2(srid, coordinates, coordinates[1], coordinates[2])
    elseif srid == 9157
        CartesianPoint3(srid, coordinates, coordinates[1], coordinates[2], coordinates[3])
    elseif srid == 4326
        WGS84Point2(srid, coordinates, coordinates[1], coordinates[2], coordinates[1], coordinates[2])
    elseif srid == 4979
        WGS84Point3(srid, coordinates, coordinates[1], coordinates[2], coordinates[3], coordinates[1], coordinates[2], coordinates[3])
    else
        Point(srid, coordinates)
    end
end

hydrate_point(srid::Integer, a, b...) = hydrate_point(srid, [a, b...])

function dehydrate_point(value::AbstractPoint)
    dim = length(value.coords)
    if dim == 2
        return Structure(UInt8('X'), Any[value.srid, value.coords...])
    elseif dim == 3
        return Structure(UInt8('Y'), Any[value.srid, value.coords...])
    else
        throw(ValueError("Cannot dehydrate Point with $(dim) dimensions"))
    end
end

dehydrate(value::AbstractPoint) = dehydrate_point(value)

function spatial_hydration_functions()
    return Dict(
        UInt8('X') => (psh, values)->hydrate_point(values...),
        UInt8('Y') => (psh, values)->hydrate_point(values...)
    )
end