text:
"""
    couple_models(aero::Wagner, stru::LiftingLineSection, flap::SimpleFlap,
        ctrl::LiftingLineSectionControl)

Create an aerostructural model using an unsteady aerodynamic model based
on Wagner's function, a lifting line aerodynamic model, and a linear steady-state
control surface model.  The existence of this coupling allows [`Wagner`](@ref)
and [`SimpleFlap`](@ref) to be used with [`LiftingLine`](@ref) and
[`LiftingLineFlaps`](@ref).  This model introduces the freestream air density
``\\rho`` as an additional parameter.
"""
function couple_models(aero::Wagner, stru::LiftingLineSection, flap::SimpleFlap,
    ctrl::LiftingLineSectionControl)

    return (aero, stru, flap, ctrl)
end

# --- Traits --- #

function number_of_additional_parameters(::Type{<:Wagner}, ::Type{LiftingLineSection},
    ::Type{SimpleFlap}, ::Type{LiftingLineSectionControl})

    return 1
end

function coupling_inplaceness(::Type{<:Wagner}, ::Type{LiftingLineSection},
    ::Type{SimpleFlap}, ::Type{LiftingLineSectionControl})

    return OutOfPlace()
end

function coupling_rate_jacobian_type(::Type{<:Wagner}, ::Type{LiftingLineSection},
    ::Type{SimpleFlap}, ::Type{LiftingLineSectionControl})

    return Linear()
end

function coupling_state_jacobian_type(::Type{<:Wagner}, ::Type{LiftingLineSection},
    ::Type{SimpleFlap}, ::Type{LiftingLineSectionControl})

    return Nonlinear()
end

function coupling_parameter_jacobian_type(::Type{<:Wagner}, ::Type{LiftingLineSection},
    ::Type{SimpleFlap}, ::Type{LiftingLineSectionControl})

    return Nonlinear()
end

function coupling_time_gradient_type(::Type{<:Wagner}, ::Type{LiftingLineSection},
    ::Type{SimpleFlap}, ::Type{LiftingLineSectionControl})

    return Zeros()
end

# --- Methods --- #

function get_coupling_inputs(aero::Wagner, stru::LiftingLineSection,
    flap::SimpleFlap, ctrl::LiftingLineSectionControl, dx, x, p, t)
    # extract state variables
    dλ1, dλ2, dvx, dvy, dvz, dωx, dωy, dωz, dδ = dx
    # extract state variables
    λ1, λ2, vx, vy, vz, ωx, ωy, ωz, δ = x
    # extract parameters
    a, b, a0, α0, cd0, cm0, cnδ, caδ, cmδ, ρ = p
    # extract model constants
    C1 = aero.C1
    C2 = aero.C2
    # freestream velocity components
    u, v, ω = liftingline_velocities(vx, vz, ωy)
    udot, vdot, ωdot = liftingline_accelerations(dvx, dvz, dωy)
    # aerodynamic loads
    Na, Aa, Ma = wagner_loads(a, b, ρ, a0, α0, cd0, cm0, C1, C2, u, v, ω, vdot, ωdot, λ1, λ2)
    # loads due to flap deflections
    Nf, Af, Mf = simpleflap_loads(b, u, ρ, cnδ, caδ, cmδ, δ)
    # loads per unit span
    f = SVector(Aa + Af, 0, Na + Nf)
    m = SVector(0, Ma + Mf, 0)
    # return portion of inputs that is not dependent on the state rates
    return SVector(u, v, ω, f..., m...)
end

# --- Performance Overloads --- #

function get_coupling_rate_jacobian(aero::Wagner, stru::LiftingLineSection,
    flap::SimpleFlap, ctrl::LiftingLineSectionControl, dx, x, p, t)
    # extract state variables
    dλ1, dλ2, dvx, dvy, dvz, dωx, dωy, dωz, dδ = dx
    # extract state variables
    λ1, λ2, vx, vy, vz, ωx, ωy, ωz, δ = x
    # extract parameters
    a, b, a0, α0, cd0, cm0, cnδ, caδ, cmδ, ρ = p
    # calculate loads
    N_dvx, A_dvx, M_dvx = wagner_loads_udot()
    N_dvz, A_dvz, M_dvz = wagner_loads_vdot(a, b, ρ)
    N_dωy, A_dωy, M_dωy = wagner_loads_ωdot(a, b, ρ)
    # assemble mass matrix
    return @SMatrix [
        0 0      0   0    0   0   0    0   0;
        0 0      0   0    0   0   0    0   0;
        0 0      0   0    0   0   0    0   0;
        0 0    A_dvx 0  A_dvz 0  A_dωy 0   0;
        0 0      0   0    0   0   0    0   0;
        0 0    N_dvx 0  N_dvz 0  N_dωy 0   0;
        0 0      0   0    0   0   0    0   0;
        0 0    M_dvx 0  M_dvz 0  M_dωy 0   0;
        0 0      0   0    0   0   0    0   0
        ]
end

# TODO: State and parameter jacobians

# --- Convenience Methods --- #

function set_additional_parameters!(padd, aero::Wagner, stru::LiftingLineSection,
    flap::SimpleFlap, ctrl::LiftingLineSectionControl; rho)

    padd[1] = rho

    return padd
end

function separate_additional_parameters(aero::Wagner, stru::LiftingLineSection,
    flap::SimpleFlap, ctrl::LiftingLineSectionControl, padd)

    return (rho = padd[1],)
end


text:
using BinaryBuilder

# These are the platforms built inside the wizard
platforms = [
    BinaryProvider.Linux(:i686, :glibc),
  BinaryProvider.Linux(:x86_64, :glibc),
  BinaryProvider.Linux(:aarch64, :glibc),
  BinaryProvider.Linux(:armv7l, :glibc),
  BinaryProvider.Linux(:powerpc64le, :glibc),
  BinaryProvider.MacOS(),
  BinaryProvider.Windows(:i686),
  BinaryProvider.Windows(:x86_64)
]

# If the user passed in a platform (or a few, comma-separated) on the
# command-line, use that instead of our default platforms
if length(ARGS) > 0
    platforms = platform_key.(split(ARGS[1], ","))
end
info("Building for $(join(triplet.(platforms), ", "))")

# Collection of sources required to build SundialsKLUBuilder2
sources = [
    "https://computation.llnl.gov/projects/sundials/download/sundials-3.1.0.tar.gz" =>
    "18d52f8f329626f77b99b8bf91e05b7d16b49fde2483d3a0ea55496ce4cdd43a",
    "http://faculty.cse.tamu.edu/davis/SuiteSparse/SuiteSparse-5.0.0.tar.gz" =>
    "7162e3a9fda729b3d46183307e93326e3cde726c72b1ec79a973060b16e6b3be"
]

script = raw"""
cd $WORKSPACE/srcdir
cd SuiteSparse/SuiteSparse_config/
cat > mk.patch <<'END'
--- SuiteSparse_config.mk
+++ SuiteSparse_config.mk.new
@@ -432,12 +432,13 @@

 SO_OPTS = $(LDFLAGS)

-ifeq ($(UNAME),Windows)
+ifeq ($(UNAME),MSYS_NT-6.3)
     # Cygwin Make on Windows (untested)
     AR_TARGET = $(LIBRARY).lib
     SO_PLAIN  = $(LIBRARY).dll
     SO_MAIN   = $(LIBRARY).$(SO_VERSION).dll
     SO_TARGET = $(LIBRARY).$(VERSION).dll
+    SO_OPTS  += -shared -Wl,-soname -Wl,$(SO_MAIN) -Wl,--no-undefined
     SO_INSTALL_NAME = echo
 else
     # Mac or Linux/Unix
END

patch -l SuiteSparse_config.mk < mk.patch
make library
INSTALL=$WORKSPACE/destdir/ make install
cd ../AMD
make library
INSTALL=$WORKSPACE/destdir/ make install
cd ../COLAMD
make library
INSTALL=$WORKSPACE/destdir/ make install
cd ../BTF
make library
INSTALL=$WORKSPACE/destdir/ make install
cd ../KLU
make library
INSTALL=$WORKSPACE/destdir/ make install
cd $WORKSPACE/srcdir/sundials-3.1.0/
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/ -DCMAKE_TOOLCHAIN_FILE=/opt/$target/$target.toolchain -DEXAMPLES_ENABLE=OFF -DKLU_ENABLE=ON DKLU_INCLUDE_DIR=$WORKSPACE/srcdir/SuiteSparse/KLU/Include/ -DKLU_LIBRARY_DIR=$WORKSPACE/srcdir/SuiteSparse/lib ..
make -j8
make install

"""

products = prefix -> [
    LibraryProduct(prefix,"libbtf"),
    LibraryProduct(prefix,"libsundials_kinsol"),
    LibraryProduct(prefix,"libsundials_cvode"),
    LibraryProduct(prefix,"libsundials_ida"),
    LibraryProduct(prefix,"libsundials_cvodes"),
    LibraryProduct(prefix,"libcolamd"),
    LibraryProduct(prefix,"libsundials_sunmatrixband"),
    LibraryProduct(prefix,"libsundials_sunlinsoldense"),
    LibraryProduct(prefix,"libsundials_nvecserial"),
    LibraryProduct(prefix,"libsundials_sunlinsolspgmr"),
    LibraryProduct(prefix,"libsundials_sunlinsolspbcgs"),
    LibraryProduct(prefix,"libsundials_sunmatrixdense"),
    LibraryProduct(prefix,"libsundials_idas"),
    LibraryProduct(prefix,"libsundials_sunlinsolsptfqmr"),
    LibraryProduct(prefix,"libsundials_sunlinsolspfgmr"),
    LibraryProduct(prefix,"libsundials_sunmatrixsparse"),
    LibraryProduct(prefix,"libsundials_arkode"),
    LibraryProduct(prefix,"libklu"),
    LibraryProduct(prefix,"libsuitesparseconfig"),
    LibraryProduct(prefix,"libsundials_sunlinsolband"),
    LibraryProduct(prefix,"libsundials_sunlinsolpcg"),
    LibraryProduct(prefix,"libamd")
]

# Build the given platforms using the given sources
hashes = autobuild(pwd(), "SundialsKLUBuilder2", platforms, sources, script, products)

if !isempty(get(ENV,"TRAVIS_TAG",""))
    print_buildjl(pwd(), products, hashes,
        "https://github.com/tshort/SundialsKLUBuilder2/releases/download/$(ENV["TRAVIS_TAG"])")
end

ext: jl


text:
import SwitchOnSafety: veroneselift
import Base: copy

copy(Lmap::LinearMap) = LinearMap(copy(Lmap.A))

copy(Lmap::ConstrainedLinearMap) = ConstrainedLinearMap(copy(Lmap.A), copy(Lmap.X))

copy(s::ConstrainedContinuousIdentitySystem) = ConstrainedContinuousIdentitySystem(s.statedim, copy(s.X))

copy(s::AbstractSwitchedSystem) = HybridSystem(s.automaton, copy.(s.modes), copy.(s.resetmaps), s.switchings)

veroneselift(Lmap::LinearMap, d) = LinearMap(veroneselift(Lmap.A, d))

function veroneselift(Lmap::ConstrainedLinearMap, d)
    A = veroneselift(Lmap.A, d)
    X = Lmap.X
    if typeof(X) <: AbstractArray
        for (i, E) in enumerate(X)
            X[i] = veroneselift(E, d)
        end
    end
    ConstrainedLinearMap(A, X)
end

veroneselift(s::ContinuousIdentitySystem, d) = s

function veroneselift(s::ConstrainedContinuousIdentitySystem, d)
    X = copy(s.X)
    if typeof(X) <: AbstractArray
        for (i, E) in enumerate(s.X)
            X[i] = veroneselift(E, d)
        end
    end
    ConstrainedContinuousIdentitySystem(s.statedim, X)
end

function veroneselift!(s::AbstractSwitchedSystem, d)
    for (i, mode) in enumerate(s.modes)
        s.modes[i] = veroneselift(mode, d)
    end
    for (i, Rmap) in enumerate(s.resetmaps)
        s.resetmaps[i] = veroneselift(Rmap, d)
    end
end

function veroneselift(s::StateDepDiscreteSwitchedLinearSystem, d)
    t = copy(s)
    veroneselift!(t, d)
    t
end

const AbstractPolyVec = AbstractVector{<:MultivariatePolynomials.AbstractVariable}

function veroneselift(x::AbstractPolyVec, d)
    n = length(x)
    N = binomial(n + d - 1, d)
    X = monomials(x, d)
    df = factorial(d)
    scaling(n) = sqrt(df / prod(factorial, exponents(n)))
    scales = Float64[scaling(n) for n in X]
    return Vector(terms(scales ⋅ X))
end


text:
using PoolQueues
using Test

struct MyItem
    eof::Bool
    data::Vector{UInt8}
end
MyItem(n=16) = MyItem(false, Vector{UInt8}(undef, n))

function process_data_closure(io::IO, pq::PoolQueue)
    eof = false
    while !eof
        consume!(pq) do item
            item.eof || println(io, join(string.(item.data, base=16, pad=2), ' '))
            eof = item.eof
            return item
        end
    end
end

function process_data_args(io::IO, pq::PoolQueue)
    eof::Ref{Bool} = Ref{Bool}(false)
    while !eof[]
        consume!(pq, eof) do item, eof
            item.eof || println(io, join(string.(item.data, base=16, pad=2), ' '))
            eof[] = item.eof
            return item
        end
    end
end

@testset "PoolQueues.jl" begin
    @testset for consumer_func in (process_data_closure, process_data_args)
        # Make data in and data out IOBuffers
        din = IOBuffer(UInt8.(0x00:0xff))
        dout = IOBuffer()
        expected = """
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af
b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf
d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df
e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef
f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
"""

        pq = PoolQueue(MyItem, 2)
        consumer = @async consumer_func(dout, pq)

        eof = false
        while !eof
            produced_item = produce!(pq, din) do item, din
                try
                    read!(din, item.data)
                catch
                    return MyItem(true, item.data)
                end
                item
            end
            eof = produced_item.eof
        end

        # Wait for consumer to finish
        wait(consumer)

        got = String(take!(dout))
        @test got == expected

        @test istaskdone(consumer)
        @test !istaskfailed(consumer)
        @test istaskstarted(consumer)
    end

    @testset begin
        pq = PoolQueue(Ref, 2, 2, 0)
        @test isready(pq.pool)
        @test !isready(pq.queue)
        # Test that returning nothing will recycle item
        @test produce!(item->nothing, pq) === nothing
        @test isready(pq.pool)
        @test !isready(pq.queue)
        r1 = acquire!(pq)
        r2 = acquire!(pq)
        @test !isready(pq.pool)
    end
end


text:
# please instantiate ./test/Manifest.toml for this

using Combinatorics, JLD2, DataStructures
import Statistics

# ===============================
# Grow and Shrink Fuzz Fixtures
# ===============================

# format: [(values, expected_medians)]

grow_shrink_values = [[1.], [1,2], [2,1]]
append!(grow_shrink_values, permutations([1,2,3])|>collect)
append!(grow_shrink_values, permutations([1,2,3,4])|>collect)
for n in [6,7,13,14,50,51], _ in 1:20
    push!(grow_shrink_values, rand(n))
end

function generate_grow_shrink_medians(values)
    N = length(values)
    medians = Array{Float64}(undef, 2N-1)
    # grow phase
    for i in 1:N
        medians[i] = Statistics.median(values[1:i])
    end
    # shrink phase
    for i in N+1:2N-1
        medians[i] = Statistics.median(values[i-N+1:end])
    end
    return medians
end

grow_shrink_medians = map(generate_grow_shrink_medians, grow_shrink_values)

grow_shrink_fixtures = [(grow_shrink_values[i], grow_shrink_medians[i]) for i in eachindex(grow_shrink_values)]

grow_shrink_filename = dirname(@__FILE__) * "/grow_shrink.jld2"
@save grow_shrink_filename grow_shrink_fixtures

max_stars_repo_name: Firionus/FastRunningMedian

max_issues_repo_name: Firionus/FastRunningMedian


text:
const ValType{T<:Real} = Union{T, Vector{T}}

struct VarInput
    name::Symbol
    shift::Int
end

var(name::Symbol; shift::Int=0) = VarInput(name, shift)
var(v::VarInput) = v

lag(name::Symbol, n=1) = var(name, shift=-n)
lead(name::Symbol, n=1) = var(name, shift=n)

name(v::VarInput) = getfield(v, :name)
shift(v::VarInput) = getfield(v, :shift)

name(v::Symbol) = v

convert(::Type{Symbol}, v::VarInput) = v.name

abstract type AbstractBlock end

inputs(b::AbstractBlock) = getfield(b, :ins)
invars(b::AbstractBlock) = getfield(b, :invars)
ssinputs(b::AbstractBlock) = getfield(b, :ssins)
outputs(b::AbstractBlock) = getfield(b, :outs)

function _checkinsouts(ins, outs, ssins)
    length(ins) > 0 || throw(ArgumentError("the inputs of a block cannot be empty"))
    length(outs) > 0 || throw(ArgumentError("the outputs of a block cannot be empty"))
    ssins ⊆ ins || throw(ArgumentError("ssins must be a subset of the block inputs"))
    isempty(intersect(ins, outs)) ||
        throw(ArgumentError("an input cannot be the output of the same block"))
end

struct SimpleBlock{F<:Function} <: AbstractBlock
    ins::Vector{Symbol}
    invars::Vector{VarInput}
    ssins::Set{Symbol}
    outs::Vector{Symbol}
    f::F
    function SimpleBlock(ins::Vector{Symbol}, invars::Vector{VarInput},
            ssins::Set{Symbol}, outs::Vector{Symbol}, f::F) where F
        _checkinsouts(ins, outs, ssins)
        return new{F}(ins, invars, ssins, outs, f)
    end
end

function block(f::Function, ins, outs; ssins=ins)
    ins isa Union{Symbol,VarInput} && (ins = (ins,))
    outs isa Symbol && (outs = (outs,))
    ssins isa Union{Symbol,VarInput} && (ssins = (ssins,))
    invars = var.(ins)
    ins = name.(invars)
    ssins = Set{Symbol}(name.(ssins))
    outs = collect(Symbol, outs)
    return SimpleBlock(ins, invars, ssins, outs, f)
end

function (b::SimpleBlock)(x...)
    out = b.f(x...)
    # This prevents iterating over the elements of a Vector output
    return out isa Tuple ? out : (out,)
end

function steadystate!(varvals::AbstractDict, b::SimpleBlock)
    vals = b((varvals[n] for n in b.ins)...)
    for (i, n) in enumerate(b.outs)
        val = get(varvals, n, nothing)
        val isa AbstractVector ? (val.=vals[i]) : (varvals[n] = vals[i])
    end
end

function jacobian(b::SimpleBlock, i::Int, varvals::Dict{Symbol,ValType{TF}}) where TF
    ins = inputs(b)
    vi = ins[i]
    val = varvals[vi]
    function f(x)
        val isa Vector || (x = x[1])
        xs = (ifelse(k==i, x, varvals[ins[k]]) for k in 1:length(ins))
        return collect(Iterators.flatten(b.f(xs...)))
    end
    x = val isa Vector ? val : [val]
    J = Matrix{TF}(undef, length(b.outs), length(x))
    ForwardDiff.jacobian!(J, f, x)
    return J
end

_shift(p::Real, s::Int, nT::Int) = p
_shift(p::Pair{Int,<:Vector}, s::Int, nT::Int) = view(p[2], p[1]+s+1:p[1]+s+nT)
_shift(p::Pair{Int,<:Matrix}, s::Int, nT::Int) = view(p[2], :, p[1]+s+1:p[1]+s+nT)

_getval(p::Real, t::Int) = p
_getval(p::AbstractVector, t::Int) = p[t]
_getval(p::AbstractMatrix, t::Int) = view(p, :, t)

function transition!(varpaths::AbstractDict, b::SimpleBlock, nT::Int)
    inpaths = ((_shift(varpaths[name(v)], shift(v), nT) for v in invars(b))...,)
    outpaths = ((_shift(varpaths[n], 0, nT) for n in outputs(b))...,)
    for t in 1:nT
        vals = b((_getval(p, t) for p in inpaths)...)
        for (i, val) in enumerate(vals)
            if val isa AbstractVector
                outpaths[i][:,t] .= val
            else
                outpaths[i][t] = val
            end
        end
    end
end


text:
# Polar heatmaps

Using Plots

x = range(0, 2π, length = 9)
y = 0:4
z = (1:4) .+ (1:8)'
heatmap(x, y, z, projection = :polar)

max_stars_repo_name: Ellon-M/visualizations

max_issues_repo_name: Ellon-M/visualizations

max_forks_repo_name: Ellon-M/visualizations


text:
abstract type AbstractCoord{N,T} end

coord_axis(A::AbstractCoord{N,T}) where{N,T} = N
coord_eltype(A::AbstractCoord{N,T}) where{N,T} = T

struct CartesianCoord{N,T<:Real} <: AbstractCoord{N,T}
    name  :: String
    min   :: T
    max   :: T
    nodes :: Int
end

struct GaussLobattoCoord{N,T<:Real} <: AbstractCoord{N,T}
    name  :: String
    min   :: T
    max   :: T
    nodes :: Int
end

coord_type(coord::CartesianCoord)    = "Cartesian"
coord_type(coord::GaussLobattoCoord) = "GaussLobatto"

struct Cartesian{N} end
function Cartesian{N}(name::String, xmin::T, xmax::T, nodes::Integer;
                      endpoint::Bool=true) where {T<:Real,N}
    min_ = xmin
    if (!endpoint)
        h    = (xmax - xmin) / nodes
        max_ = xmax - h
    else
        max_ = xmax
    end
    CartesianCoord{N,T}(name, min_, max_, nodes)
end
Cartesian(args...; endpoint=true) = Cartesian{1}(args...; endpoint=endpoint)

struct GaussLobatto{N} end
function GaussLobatto{N}(name::String, xmin::T, xmax::T,
                          nodes::Integer) where {T<:Real,N}
    GaussLobattoCoord{N,T}(name, xmin, xmax, nodes)
end
GaussLobatto(args...) = GaussLobatto{1}(args...)

struct Coord{N} end
function Coord{N}(coord_type::String, name::String, min::T, max::T, nodes::Int) where {T<:Real,N}
    if coord_type == "Cartesian"
        return CartesianCoord{N,T}(name, min, max, nodes)
    elseif coord_type == "GaussLobatto"
        return GaussLobattoCoord{N,T}(name, min, max, nodes)
    else
        error("Unknown coord type")
    end
end

@inline function delta(coord::CartesianCoord)
    (coord.max - coord.min) / (coord.nodes - 1)
end

# a Gauss-Lobatto grid has non-uniform spacing. not sure if the best is to
# return "missing", not define the method, or just have it return NaN, as now.
@inline delta(coord::GaussLobattoCoord) = NaN

@inline function Base.getindex(coord::CartesianCoord, i::Union{Int, UnitRange})
    h = delta(coord)
    coord.min .+ (i .- 1) * h
end

@inline function Base.getindex(coord::GaussLobattoCoord{N,T},
                               j::Union{Int, UnitRange}) where {N,T}
    xmin  = coord.min
    xmax  = coord.max
    M     = coord.nodes - 1
    xj    = -cos.( (j .- 1) * (T(pi) / M))
    0.5 * (xmax .+ xmin .+ (xmax - xmin) * xj)
end

@inline Base.firstindex(coord::AbstractCoord) = 1
@inline Base.lastindex(coord::AbstractCoord)  = coord.nodes
@inline Base.length(coord::AbstractCoord) = coord.nodes

@inline Base.getindex(coord::AbstractCoord, ::Colon) = [coord[i] for i in 1:coord.nodes]

struct Chart{N,T,A}
    coords :: A
    function Chart{A}(coords) where {A}
        N = length(coords)
        T = coord_eltype(coords[1])
        for a in 1:N
            @assert(coord_axis(coords[a]) == a, "wrong order in grid array")
        end
        new{N,T,A}(coords)
    end
end
"""
    Chart(coords::A)

A `Chart` is a collection of `AbstractCoord`s
"""
Chart(coords::A) where {A} = Chart{A}(coords)

Chart(coords::Vararg{AbstractCoord,N}) where {N} = Chart(coords)

function Chart(coord_types::Vector, names::Vector, mins::Vector, maxs::Vector,
               nodess)
    dim_ = length(names)
    @assert(length(mins) == length(maxs) == length(nodess) == length(coord_types) == dim_)
    coords_ = [Coord{i}(coord_types[i], names[i], mins[i], maxs[i], nodess[i]) for i in 1:dim_]
    coords  = Tuple(coords_)
    Chart(coords)
end

@inline Base.ndims(chart::Chart{N}) where {N} = N

@inline function Base.getindex(chart::Chart{N}, idx::Vararg) where {N}
    [chart.coords[a][idx[a]] for a in 1:N]
end

@inline function Base.getindex(chart::Chart{N}, ::Colon) where {N}
    [chart.coords[a][:] for a in 1:N]
end

@inline function name(chart::Chart{N}) where {N}
    [chart.coords[a].name for a in 1:N]
end

@inline function min(chart::Chart{N}) where {N}
    [chart.coords[a].min for a in 1:N]
end

@inline function max(chart::Chart{N}) where {N}
    [chart.coords[a].max for a in 1:N]
end

@inline function nodes(chart::Chart{N}) where {N}
    [chart.coords[a].nodes for a in 1:N]
end

@inline function delta(chart::Chart{N}) where {N}
    [delta(chart.coords[a]) for a in 1:N]
end

@inline function coord_type(chart::Chart{N}) where {N}
    [coord_type(chart.coords[a]) for a in 1:N]
end

@inline Base.size(chart::Chart) = Tuple(nodes(chart))

"""
    Atlas{N,A<:Chart} <: AbstractPartition{N,A}

An `Atlas` is a collection of `Chart`s
"""
struct Atlas{N,A<:Chart} <: AbstractPartition{N,A}
    x :: NTuple{N,A}
end

"""
    Atlas(charts::A)
"""
Atlas(charts::Vararg{Chart,N}) where {N} = Atlas(charts)
Atlas(xx::Vector) = Atlas(Tuple(xx))

function Atlas(chart::Chart)
    charts = (chart)
    Atlas(charts)
end


text:
import JSON

AnyIntType = Union{Int8, Int32, Int64, Int128, Int}

@enum BoardIds begin

    PLAYBACK_FILE_BOARD = -3
    STREAMING_BOARD = -2
    SYNTHETIC_BOARD = -1
    CYTON_BOARD = 0
    GANGLION_BOARD = 1
    CYTON_DAISY_BOARD = 2
    NOVAXR_BOARD = 3
    GANGLION_WIFI_BOARD = 4
    CYTON_WIFI_BOARD = 5
    CYTON_DAISY_WIFI_BOARD = 6
    BRAINBIT_BOARD = 7
    UNICORN_BOARD = 8
    CALLIBRI_EEG_BOARD = 9
    CALLIBRI_EMG_BOARD = 10
    CALLIBRI_ECG_BOARD = 11
    FASCIA_BOARD = 12
    NOTION_1_BOARD = 13
    NOTION_2_BOARD = 14
    IRONBCI_BOARD = 15

end

@enum LogLevels begin

    LEVEL_TRACE = 0
    LEVEL_DEBUG = 1
    LEVEL_INFO = 2
    LEVEL_WARN = 3
    LEVEL_ERROR = 4
    LEVEL_CRITICAL = 5
    LEVEL_OFF = 6

end

@enum IpProtocolType begin

    NONE = 0
    UDP = 1
    TCP = 2

end

struct BrainFlowError <: Exception
    msg::String
    ec::Int32
end

mutable struct BrainFlowInputParams

    serial_port::String
    mac_address::String
    ip_address::String
    ip_port::Int32
    ip_protocol::Int32
    other_info::String
    timeout::Int32
    serial_number::String
    file::String

    function BrainFlowInputParams()
        new("", "", "", 0, Integer(NONE), "", 0, "", "")
    end

end

function get_timestamp_channel(board_id::AnyIntType)
    channel = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_timestamp_channel, "BoardController.dll"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    elseif Sys.isapple()
        ec = ccall((:get_timestamp_channel, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    else
        ec = ccall((:get_timestamp_channel, "libBoardController.so"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channel[1] + 1
    value
end

function get_package_num_channel(board_id::AnyIntType)
    channel = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_package_num_channel, "BoardController.dll"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    elseif Sys.isapple()
        ec = ccall((:get_package_num_channel, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    else
        ec = ccall((:get_package_num_channel, "libBoardController.so"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channel[1] + 1
    value
end

function get_battery_channel(board_id::AnyIntType)
    channel = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_battery_channel, "BoardController.dll"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    elseif Sys.isapple()
        ec = ccall((:get_battery_channel, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    else
        ec = ccall((:get_battery_channel, "libBoardController.so"), Cint, (Cint, Ptr{Cint}), Int32(board_id), channel)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channel[1] + 1
    value
end

function get_sampling_rate(board_id::AnyIntType)
    val = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_sampling_rate, "BoardController.dll"), Cint, (Cint, Ptr{Cint}), Int32(board_id), val)
    elseif Sys.isapple()
        ec = ccall((:get_sampling_rate, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}), Int32(board_id), val)
    else
        ec = ccall((:get_sampling_rate, "libBoardController.so"), Cint, (Cint, Ptr{Cint}), Int32(board_id), val)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    value = val[1]
    value
end

function get_num_rows(board_id::AnyIntType)
    val = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_num_rows, "BoardController.dll"), Cint, (Cint, Ptr{Cint}), Int32(board_id), val)
    elseif Sys.isapple()
        ec = ccall((:get_num_rows, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}), Int32(board_id), val)
    else
        ec = ccall((:get_num_rows, "libBoardController.so"), Cint, (Cint, Ptr{Cint}), Int32(board_id), val)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # here dont need to add 1, last element included
    value = val[1]
    value
end

function get_eeg_names(board_id::AnyIntType)
    names_string = Vector{Cuchar}(undef, 4096)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_eeg_names, "BoardController.dll"), Cint, (Cint, Ptr{UInt8}, Ptr{Cint}), Int32(board_id), names_string, len)
    elseif Sys.isapple()
        ec = ccall((:get_eeg_names, "libBoardController.dylib"), Cint, (Cint, Ptr{UInt8}, Ptr{Cint}), Int32(board_id), names_string, len)
    else
        ec = ccall((:get_eeg_names, "libBoardController.so"), Cint, (Cint, Ptr{UInt8}, Ptr{Cint}), Int32(board_id), names_string, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    sub_string = String(names_string)[1:len[1]]
    value = split(sub_string, ',')
    value
end

function get_eeg_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_eeg_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_eeg_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_eeg_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_exg_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_exg_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_exg_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_exg_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_emg_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_emg_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_emg_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_emg_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_ecg_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_ecg_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_ecg_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_ecg_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_eog_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_eog_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_eog_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_eog_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_eda_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_eda_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_eda_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_eda_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_ppg_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_ppg_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_ppg_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_ppg_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_accel_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_accel_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_accel_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_accel_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_analog_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_analog_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_analog_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_analog_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_gyro_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_gyro_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_gyro_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_gyro_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_other_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_other_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_other_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_other_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_temperature_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_temperature_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_temperature_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_temperature_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function get_resistance_channels(board_id::AnyIntType)
    channels = Vector{Cint}(undef, 512)
    len = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_resistance_channels, "BoardController.dll"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    elseif Sys.isapple()
        ec = ccall((:get_resistance_channels, "libBoardController.dylib"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    else
        ec = ccall((:get_resistance_channels, "libBoardController.so"), Cint, (Cint, Ptr{Cint}, Ptr{Cint}), Int32(board_id), channels, len)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get board info ", ec), ec))
    end
    # julia counts from 1
    value = channels[1:len[1]] .+ 1
    value
end

function set_log_level(log_level::AnyIntType)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:set_log_level, "BoardController.dll"), Cint, (Cint,), Int32(log_level))
    elseif Sys.isapple()
        ec = ccall((:set_log_level, "libBoardController.dylib"), Cint, (Cint,), Int32(log_level))
    else
        ec = ccall((:set_log_level, "libBoardController.so"), Cint, (Cint,), Int32(log_level))
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in set_log_level ", ec), ec))
    end
end

function enable_board_logger()
    set_log_level(2)
end

function enable_dev_board_logger()
    set_log_level(0)
end

function disable_board_logger()
    set_log_level(6)
end

function log_message(log_level::AnyIntType, message::String)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:log_message, "BoardController.dll"), Cint, (Cint, Ptr{UInt8}), Int32(log_level), message)
    elseif Sys.isapple()
        ec = ccall((:log_message, "libBoardController.dylib"), Cint, (Cint, Ptr{UInt8}), Int32(log_level), message)
    else
        ec = ccall((:log_message, "libBoardController.so"), Cint, (Cint, Ptr{UInt8}), Int32(log_level), message)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in log_message ", ec), ec))
    end
end

function set_log_file(log_file::String)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:set_log_file, "BoardController.dll"), Cint, (Ptr{UInt8},), log_file)
    elseif Sys.isapple()
        ec = ccall((:set_log_file, "libBoardController.dylib"), Cint, (Ptr{UInt8},), log_file)
    else
        ec = ccall((:set_log_file, "libBoardController.so"), Cint, (Ptr{UInt8},), log_file)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in set_log_file ", ec), ec))
    end
end

struct BoardShim

    master_board_id::Int32
    board_id::Int32
    input_json::String

    function BoardShim(id::AnyIntType, params::BrainFlowInputParams)
        master_id = id
        if id == Integer(STREAMING_BOARD) || id == Integer(PLAYBACK_FILE_BOARD)
            try
                master_id = parse(Int, id)
            catch
                throw(BrainFlowError("you need to provide master board id to other_info field"))
            end
        end
        new(master_id, id, JSON.json(params))
    end

end

function prepare_session(board_shim::BoardShim)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:prepare_session, "BoardController.dll"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:prepare_session, "libBoardController.dylib"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:prepare_session, "libBoardController.so"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in prepare_session ", ec), ec))
    end
end

function start_stream(board_shim::BoardShim, num_samples::AnyIntType, streamer_params::String)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:start_stream, "BoardController.dll"), Cint, (Cint, Ptr{UInt8}, Cint, Ptr{UInt8}), num_samples, streamer_params,
            board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:start_stream, "libBoardController.dylib"), Cint, (Cint, Ptr{UInt8}, Cint, Ptr{UInt8}), num_samples, streamer_params,
            board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:start_stream, "libBoardController.so"), Cint, (Cint, Ptr{UInt8}, Cint, Ptr{UInt8}), num_samples, streamer_params,
            board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in start_stream ", ec), ec))
    end
end

function start_stream(board_shim::BoardShim)
    start_stream(board_shim, 45000, "")
end

function is_prepared(board_shim::BoardShim)
    val = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:is_prepared, "BoardController.dll"), Cint, (Ptr{Cint}, Cint, Ptr{UInt8}), val, board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:is_prepared, "libBoardController.dylib"), Cint, (Ptr{Cint}, Cint, Ptr{UInt8}), val, board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:is_prepared, "libBoardController.so"), Cint, (Ptr{Cint}, Cint, Ptr{UInt8}), val, board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in is_prepared ", ec), ec))
    end
    value = Bool(val[1])
    value
end

function get_board_data_count(board_shim::BoardShim)
    val = Vector{Cint}(undef, 1)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_board_data_count, "BoardController.dll"), Cint, (Ptr{Cint}, Cint, Ptr{UInt8}), val, board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:get_board_data_count, "libBoardController.dylib"), Cint, (Ptr{Cint}, Cint, Ptr{UInt8}), val, board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:get_board_data_count, "libBoardController.so"), Cint, (Ptr{Cint}, Cint, Ptr{UInt8}), val, board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get_board_data_count ", ec), ec))
    end
    value = val[1]
    value
end

function stop_stream(board_shim::BoardShim)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:stop_stream, "BoardController.dll"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:stop_stream, "libBoardController.dylib"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:stop_stream, "libBoardController.so"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in stop_stream ", ec), ec))
    end
end

function release_session(board_shim::BoardShim)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:release_session, "BoardController.dll"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:release_session, "libBoardController.dylib"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:release_session, "libBoardController.so"), Cint, (Cint, Ptr{UInt8}), board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in release_session ", ec), ec))
    end
end

function get_board_data(board_shim::BoardShim)
    data_size = get_board_data_count(board_shim)
    num_rows = get_num_rows(board_shim.master_board_id)
    val = Vector{Float64}(undef, num_rows * data_size)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_board_data, "BoardController.dll"), Cint, (Cint, Ptr{Float64}, Cint, Ptr{UInt8}),
            data_size, val, board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:get_board_data, "libBoardController.dylib"), Cint, (Cint, Ptr{Float64}, Cint, Ptr{UInt8}),
            data_size, val, board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:get_board_data, "libBoardController.so"), Cint, (Cint, Ptr{Float64}, Cint, Ptr{UInt8}),
            data_size, val, board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get_board_data ", ec), ec))
    end
    value = transpose(reshape(val, (data_size, num_rows)))
    value
end

function get_current_board_data(num_samples::AnyIntType, board_shim::BoardShim)
    data_size = Vector{Cint}(undef, 1)
    num_rows = get_num_rows(board_shim.master_board_id)
    val = Vector{Float64}(undef, num_rows * num_samples)
    ec = STATUS_OK
    # due to this bug https://github.com/JuliaLang/julia/issues/29602 libname should be hardcoded
    if Sys.iswindows()
        ec = ccall((:get_current_board_data, "BoardController.dll"), Cint, (Cint, Ptr{Float64}, Ptr{Cint}, Cint, Ptr{UInt8}),
            num_samples, val, data_size, board_shim.board_id, board_shim.input_json)
    elseif Sys.isapple()
        ec = ccall((:get_current_board_data, "libBoardController.dylib"), Cint, (Cint, Ptr{Float64}, Ptr{Cint}, Cint, Ptr{UInt8}),
            num_samples, val, data_size, board_shim.board_id, board_shim.input_json)
    else
        ec = ccall((:get_current_board_data, "libBoardController.so"), Cint, (Cint, Ptr{Float64}, Ptr{Cint}, Cint, Ptr{UInt8}),
            num_samples, val, data_size, board_shim.board_id, board_shim.input_json)
    end
    if ec != Integer(STATUS_OK)
        throw(BrainFlowError(string("Error in get_current_board_data ", ec), ec))
    end
    value = transpose(reshape(val[1:data_size[1] * num_rows], (data_size[1], num_rows)))
    value
end


text:
"""
    SAASolver

Default `SampledSolver`. Generates a `StochasticSolution` through sequential SAA sampling to the desired confidence level.
"""
struct SAASolver{S <: SPSolverType} <: AbstractSampledSolver
    internal_solver::S

    function SAASolver(solver::SPSolverType)
        if isa(solver, JuMP.UnsetSolver)
            error("Cannot solve emerging SAA problems without functional solver.")
        end
        S = typeof(solver)
        return new{S}(solver)
    end
end
"""
    SAASolver(; solver::SPSolverType = JuMP.UnsetSolver())

Return an SAASolver where the emerging SAA problems are solved using `solver`.
"""
function SAASolver(; solver::SPSolverType = JuMP.UnsetSolver())
    return SAASolver(solver)
end

mutable struct SAAModel{M <: StochasticModel, S <: SPSolverType} <: AbstractSampledModel
    stochasticmodel::M
    solver::S
    solution::StochasticSolution
end

function SampledModel(stochasticmodel::StochasticModel, solver::SAASolver)
    return SAAModel(stochasticmodel, solver.internal_solver, EmptySolution())
end

function optimize_sampled!(saamodel::SAAModel, sampler::AbstractSampler, confidence::AbstractFloat; M::Integer = 10, tol::AbstractFloat = 1e-1, Nmax::Integer = 5000)
    sm = saamodel.stochasticmodel
    solver = saamodel.solver
    n = 16
    α = 1-confidence
    while true
        CI = confidence_interval(sm, sampler; solver = solver, confidence = 1-α, N = n, M = M)
        saa = SAA(sm, sampler, n)
        optimize!(saa, solver = solver)
        Q = optimal_value(saa)
        if length(CI)/abs(Q+1e-10) <= tol && Q ∈ CI
            saamodel.solution = StochasticSolution(optimal_decision(saa), Q, CI)
            return :Optimal
        end
        n = n * 2
        if n > Nmax
            return :LimitReached
        end
    end
end

function internal_solver(solver::SAASolver)
    return internal_solver(solver.internal_solver)
end

function stochastic_solution(saamodel::SAAModel)
    return saamodel.solution
end


text:
#=
This file is part of the replication code for: Hasenzagl, T., Pellegrino, F., Reichlin, L., & Ricco, G. (2020). A Model of the Fed's View on Inflation.
Please cite the paper if you are using any part of the code for academic work (including, but not limited to, conference and peer-reviewed papers).
=#

function get_logjacobian(θ_unb::Array{Float64,1}, MIN::Array{Float64,1}, MAX::Array{Float64,1}, opt_transf::Array{Int64, 1})

# ----------------------------------------------------------------------------------------------------------------------
# Logjacobian
# - Transformation: 1 natural logarithm, 2 no transformations, 3 generalized logit
# ----------------------------------------------------------------------------------------------------------------------

     i1 = opt_transf .== 1;
     i2 = opt_transf .== 2;
     i3 = opt_transf .== 3;

     # Log Jacobian
     JJ     = zeros(size(θ_unb));
     JJ[i1] = θ_unb[i1];
     JJ[i2] .= 0;
     JJ[i3] = log.(MAX[i3]-MIN[i3]) .+ θ_unb[i3] .- 2*log.(1 .+ exp.(θ_unb[i3]));

     return JJ;
end


text:
# open Demo/TableViewController/TableViewController.xcworkspace
# Run

using Swifter

vc = initial("http://localhost:8080")

sleep(0.5)

@query vc.tableView.tap(section: 0, row: 1)
sleep(0.5)
@query vc.navigationController.pop()
sleep(0.5)

@query vc.tableView.tap(index: 2)
sleep(0.5)
@query vc.navigationController.pop()
sleep(0.5)

@query vc.tableView.tap(text: "cyan")
sleep(0.5)
@query vc.navigationController.pop()
sleep(0.5)

@query vc.navigationController.topViewController

max_stars_repo_name: wookay/Console

max_issues_repo_name: wookay/Console

max_forks_repo_name: wookay/Console


text:
import CSV
using CSV
using Random

function gen_html(team_type)
    image_vars = ["img-var-1", "img-var-2", "img-var-3"]

    try
        person_list = readdir("./_assets/team/$team_type")

        for person in person_list
            if isempty(image_vars)
                image_vars = ["img-var-1", "img-var-2", "img-var-3"]
            end

            image_var = rand(image_vars)
            image_vars = filter(e->e!=image_var, image_vars)
            person_data_reader = CSV.File("./_assets/team/$(team_type)/$(person)/profile_info.csv", delim=";")
            row = person_data_reader[1]

            println("
            ~~~

$(row.title === missing ? "" : row.title)

$(row.name)

$(row.position)

                        $(row.interests === missing ? "" : row.interests)

                        $(row.contact)

            ~~~")
        end

    catch e

    end
end


text:
using Test

@testset "UWDDynam" begin
  include("uwd_dynam.jl")
end

@testset "DWDDynam" begin
  include("dwd_dynam.jl")
end

@testset "CPGDynam" begin
  include("cpg_dynam.jl")
end

@testset "Trajectories and Solutions" begin
  include("trajectories.jl")
end

@testset "Tonti Diagrams" begin
  include("TontiDiagrams.jl")
end


text:
struct DistanceSphereToCylinder{R3,R1,S} <: TaskMap{R3,R1,S}
    center_bottom::SVector{3,S}
    axis::SVector{3,S}
    cyl_radius::S
    sph_radius
    height::S
end
function DistanceSphereToCylinder{R3,R1}(cb::SVector{3,S}, a::SVector{3,S}, cr::S,
        cs::S, h::S) where {R3,R1,S}
    DistanceSphereToCylinder{R3,R1,S}(cb, a./norm(a), cr, cs, h)
end

struct DistanceSphereToCylinderT{R3,R1,S} <: TaskMapT{R3,R1,S}
    base_map::DistanceSphereToCylinder{R3,R1,S}
end
function DistanceSphereToCylinderT{R3,R1}(cb::SVector{3,S}, a::SVector{3,S}, cr::S,
        cs::S, h::S) where {R3,R1,S}
    DistanceSphereToCylinderT{R3,R1,S,m}(DistanceSphereToCylinder{R3,R1}(cb, a, cr, cs, h))
end

function task_map_emb(::EmbRep, ::EmbRep, xme, task_map::DistanceSphereToCylinder{R3,R1})
    center_bottom, axis = task_map.center_bottom, task_map.axis
    cyl_radius, sph_radius, height = task_map.cyl_radius, task_map.sph_radius, task_map.height
    α = 1.e-20                                  # Smoothing factor
    p_world = xme - center_bottom               # Position from origin in world frame
    y_dist_cyl = dot(axis, p_world)             # Axial distance from origin
    y_world = y_dist_cyl*axis                   # Axial position in world frame
    x_world = p_world - y_world                 # Transverse position in world frame
    x_dist_cyl = smooth_norm(x_world, α)        # Transverse distance from origin
    p_cyl = SA[x_dist_cyl, y_dist_cyl]

    p_corner1 = SA[cyl_radius, 0.]
    p_corner2 = SA[cyl_radius, height]
    d = line_segment_distance(p_cyl, p_corner1, p_corner2)
    SA[max(0., d - sph_radius)]
end


text:
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(The BTG Program)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#################################################################################
#copy this box into a file to use the BTG program
using LinearAlgebra
using Test
using Distributions
using DataFrames
using CSV
using Polynomials
using PyPlot
include("../validation/det_loocv.jl")
include("../validation/loocv.jl")
include("../quadrature/quadrature.jl")
include("../transforms/transforms.jl")
include("../priors/priors.jl")
include("../bayesopt/incremental.jl")
include("../kernels/kernel.jl")
include("../datastructs.jl")
include("../computation/buffers0.jl") #datastruct, kernel, incremental, quadrature
include("../model0.jl") #buffers, datastructs, several auxiliary
include("../computation/finitedifference.jl")
include("../computation/tdist.jl") #model0 and buffer0
#################################################################################

df = DataFrame(CSV.File("../datasets/abalone.csv"))
data = convert(Matrix, df[:,2:8]) #length, diameter, height, whole weight, shucked weight, viscera weight, shell weight
target = convert(Array, df[:, 9]) #age
normalizing_constant = maximum(target)
target = target/normalizing_constant #normalization

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~(btg1)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Attributes:
# - single length scale
# - 2-dimensional covariates
# - 3-multidimensional location vectors

#ind = 120:125
#ind = [5:9;11:17]
#ind = 5:17
ind = 1:25
posx = 1:3 #
posc = 1:3
x = data[ind, posx]
#choose a subset of variables to be regressors for the mean

Fx = data[ind, posc]
y = float(target[ind])
pind = 10:10 #prediction index
trainingData1 = trainingData(x, Fx, y) #training data used for testing various functions
#prediction data

d = getDimension(trainingData1); n = getNumPts(trainingData1); p = getCovDimension(trainingData1)
Fx0 = reshape(data[pind, posc], 1, length(posc))
x0 = reshape(data[pind, posx], 1, length(posx))
rangeθ = [100.0 200]
rangeλ = [0.5 3] #we will always used 1 range scale for lambda
btg1 = btg(trainingData1, rangeθ, rangeλ)
#θ1 = btg1.nodesWeightsθ.nodes[1, 6] #pick some theta value which doubles as quadrature node
#λ1 = btg1.nodesWeightsλ.nodes[3]
##################################################################################################

i=10
println("Validation point attributes")
println("Location:")
println(getPosition(btg1.trainingData)[i, :])
println("Covariates")
println(getCovariates(btg1.trainingData)[i, :])
println("Label")
println(getLabel(btg1.trainingData)[i])

function validate(btg1::btg, i)
    (pdf1, cdf1, dpdf1) = solve(btg1; validate = i)
    a1 = y0 -> dpdf1(x0, Fx0, y0)
    b1 = y0 -> pdf1(x0, Fx0, y0)
    c1 = y0 -> cdf1(x0, Fx0, y0)
    return (a1, b1, c1)
end

(a2, b2, c2) = validate(btg1, i);

#println("Checking derivative of btg_pdf...")
(_, _, plt1, pol1) = checkDerivative(b2, a2, 0.5, nothing, 4, 12, 10); #first arg is function, second arg is derivative
(_, _, plt2, pol2) = checkDerivative(c2, b2, 0.5, nothing, 4, 12, 10); #first arg is function, second arg is derivative

(x, y) = plt_data(b2, .1, 1)
(x1, y1) = plt_data(c2, .1, 1)

PyPlot.figure(10)
PyPlot.plot(x, y)
#PyPlot.plot(x1, y1)

#plt(b, .01, 1; label = "pdf")
#plt!(c, .01, 1; label = "cdf")
#plt(target[pind])

#(pdf, cdf, dpdf) = solve(btg1)
#a1 = y0 -> dpdf(x0, Fx0, y0)
#b1 = y0 -> pdf(x0, Fx0, y0)
#c1 = y0 -> cdf(x0, Fx0, y0)

#i=4

#plt!(b2, .01, 1)
#plt!(c2, .01, 1)
#plt!(target[ind[i]])

keys(btg1.θλbuffer_dict)|> y-> filter(x -> x[1][1] >130 && x[1][1]<132, y)

max_stars_repo_name: dbindel/btg

max_issues_repo_name: dbindel/btg

max_forks_repo_name: dbindel/btg


text:
# Only the affine constraints

using SparseArrays

include("../other_utils/feasibleCheck.jl")
include("projections.jl")

# ---------------------
# Equality constraints
# ---------------------
"""
    AL_AffineEquality(A, b)

Creates an Affine Equality Constraint of the form `Ax = b`.

To check constraint satisfaction, use:
[`satisified`](@ref) and [`whichSatisfied`](@ref)

To evaluate the constraint, use:
- [`getRaw`](@ref) to evaluate `Ax - b`
- [`getNormToProjVals`](@ref) to evaluate the projection to `Ax = b`
"""
struct AL_AffineEquality <: constraint
    A
    b
end

# Check that a constraint is satisfied (Ax - b = 0)
"""
    satisfied(r::AL_AffineEquality, x) = (r.A * x == r.b)

Check constraint satisfaction. See also [`whichSatisfied`](@ref)
"""
satisfied(r::AL_AffineEquality, x) = (r.A * x == r.b)

"""
    whichSatisfied(r::AL_AffineEquality, x) = (r.A * x .== r.b)

Check constraint satisfaction. See also [`satisfied`](@ref)
"""
whichSatisfied(r::AL_AffineEquality, x) = (r.A * x .== r.b)

# Evaluate the constraint.
"""
    getRaw(r::AL_AffineEquality, x) = r.A * x - r.b

Evaluate the constraint without projection
"""
getRaw(r::AL_AffineEquality, x) = r.A * x - r.b

"""
    getProjVecs(r::AL_AffineEquality, x)

For every row, get the projection onto the line `a'x = b`.

See [`projAffineEq`](@ref) for more information.
"""
function getProjVecs(r::AL_AffineEquality, x)
    #=
    We want to project each constraint
    We write Ax = b
    But this is equivalent to a1'x = b1, a2'x = b2, ..., am'x = bm
    Where ak is the row k in A
    =#
    projVecs = []
    for ind in 1:size(r.b, 1)
        aI = r.A[ind, :]
        bI = r.b[ind]
        push!(projVecs, projAffineEq(aI, bI, x))
    end

    return projVecs
end

"""
    getNormToProjVals(r::AL_AffineEquality, x)

Get the row by row constraint violation based on projections. This is better
than [`getRaw`](@ref) since it doesn't depend on the magnitude of the elements
in `r.A` and `r.b`.

See also [`AL_AffineEquality`](@ref), [`getProjVecs`](@ref),
[`projAffineEq`](@ref)
"""
function getNormToProjVals(r::AL_AffineEquality, x)
    #=
    We want to get the projected vector and calculate the distance between the
    original point and the constraint.
    =#
    projVecs = getProjVecs(r, x)
    pvsDiff = [pv - x for pv in projVecs]

    # NaN removal due to floating point error near 0.0
    return replace(norm.(pvsDiff, 2), NaN=>0.0)
end

# Lastly, we do some calculus
"""
    getGradC(r::AL_AffineEquality, x)

Calculates the gradient of a constraint.

For `r::AL_AffineEquality`, `∇c(x) = A`

The variable "x" is passed to the header to maintain the same function header
but it is not used in the function.
"""
function getGradC(r::AL_AffineEquality, x)
    #=
    We want the gradient of the constraints. This is piecewise in the
    inequality case. But it is a single function in the equality case
    =#
    return r.A
end

"""
    getHessC(r::AL_AffineEquality)

Calculates the hessian of a constraint.

For `r::AL_AffineEquality`, `H(c(x)) = 0`
"""
function getHessC(r::AL_AffineEquality)
    #=
    We want the hessian of the constraints. This is just zero for affine
    constraints, but we need to match the dimensions.
    =#
    return spzeros(size(r.A, 2), size(r.A, 2)) # nxn
end

"""
    getHessC_ALTerm(r::AL_AffineEquality, x, rho = 1)

Calculates the hessian of the augmented lagrangian constraint term.

For `r::AL_AffineEquality`, `H(ρ c(x)'c(x) + λ c(x)) = ρ A'A`

The variable "x" is passed to the header to maintain the same function header
but it is not used in the function.
"""
function getHessC_ALTerm(r::AL_AffineEquality, x, rho = 1)
    #=
    The augmented lagrangian constraint term is of the form:
    ρ c(x)'c(x) + λ c(x)

    where c(x) = Ax - b.

    The Hessian matrix is then
    Htot = ρ (c.H + J.J + H.c) + λ H

    But H = 0 for Affine equalities, so
    Htot = ρ (J.J) = ρ A'A
    =#

    jacob = getGradC(r, x)
    return rho * jacob'jacob
end

# -----------------------
# Inequality constraints
# -----------------------

"""
    AL_AffineInequality(A, b)

Creates an Affine Inequality Constraint of the form `Ax ≤ b`.

To check constraint satisfaction, use:
[`satisified`](@ref) and [`whichSatisfied`](@ref)

To evaluate the constraint, use:
- [`getRaw`](@ref) to evaluate `Ax - b`
- [`getNormToProjVals`](@ref) to evaluate the projection to `Ax ≤ b`
"""
struct AL_AffineInequality <: constraint
    A
    b
end

# Check that a constraint is satisfied (Ax - b ≤ 0)
"""
    satisfied(r::AL_AffineInequality, x)

Check constraint satisfaction. See also [`whichSatisfied`](@ref)
"""
satisfied(r::AL_AffineInequality, x) = isFeasiblePolyHedron(r.A, r.b, x)

"""
    whichSatisfied(r::AL_AffineInequality, x)

Check constraint satisfaction. See also [`satisfied`](@ref)
"""
whichSatisfied(r::AL_AffineInequality, x) = (r.A * x .≤ r.b)

# Evaluate the constraint.
"""
    getRaw(r::AL_AffineInequality, x) = r.A * x - r.b

Evaluate the constraint without projection
"""
getRaw(r::AL_AffineInequality, x) = r.A * x - r.b

"""
    getProjVecs(r::AL_AffineInequality, x)

For every row, get the projection onto the line `a'x = b`.

See [`projAffineEq`](@ref) for more information.
"""
function getProjVecs(r::AL_AffineInequality, x)
    #=
    We want to project each constraint
    We write Ax = b
    But this is equivalent to a1'x = b1, a2'x = b2, ..., am'x = bm
    Where ak is the row k in A
    =#
    projVecs = []
    for ind in 1:size(r.b, 1)
        aI = r.A[ind, :]
        bI = r.b[ind]
        push!(projVecs, projAffineIneq(aI, bI, x))
    end

    return projVecs
end

"""
    getNormToProjVals(r::AL_AffineInequality, x)

Get the row by row constraint violation based on projections. This is better
than [`getRaw`](@ref) since it doesn't depend on the magnitude of the elements
in `r.A` and `r.b`. It also automatically accounts for the inequality.

See also [`AL_AffineInequality`](@ref), [`getProjVecs`](@ref),
[`projAffineEq`](@ref)
"""
function getNormToProjVals(r::AL_AffineInequality, x)
    #=
    We want to get the projected vector and calculate the distance between the
    original point and the constraint.
    =#
    projVecs = getProjVecs(r, x)
    pvsDiff = [pv - x for pv in projVecs]
    return norm.(pvsDiff, 2)
end

# Lastly, we do some calculus
"""
    getGradC(r::AL_AffineInequality, x)

Calculates the gradient of a constraint.

For `r::AL_AffineInequality`, `∇c(x) = A` for rows where the constraint is NOT
satisfied.

Unlike the [`AL_AffineEquality`](@ref) case, the variable "x" is passed to
is needed to determine if the constraint is satisfied.
"""
function getGradC(r::AL_AffineInequality, x)
    #=
    We want the gradient of the constraints. This is piecewise in the
    inequality case. But it is a single function in the equality case
    =#
    APost = zeros(size(r.A))
    rowPassed = whichSatisfied(r, x)
    for row in 1:size(r.A, 1)
        if !rowPassed[row]
            # Constraint is not met
            APost[row, :] = r.A[row, :]
        end
    end
    return APost
end

"""
    getHessC(r::AL_AffineEquality)

Calculates the hessian of a constraint.

For `r::AL_AffineInequality`, `H(c(x)) = 0`
"""
function getHessC(r::AL_AffineInequality)
    #=
    We want the hessian of the constraints. This is just zero for affine
    constraints, but we need to match the dimensions.
    =#
    return spzeros(size(r.A, 2), size(r.A, 2)) # nxn
end

"""
    getHessC_ALTerm(r::AL_AffineInequality, x, rho = 1)

Calculates the hessian of the augmented lagrangian constraint term.

For `r::AL_AffineEquality`, `H(ρ c(x)'c(x) + λ c(x)) = ρ A+'A+`

The variable "x" is passed to the header to maintain the same function header
but it is not used in the function.
"""
function getHessC_ALTerm(r::AL_AffineInequality, x, rho = 1)
    #=
    The augmented lagrangian constraint term is of the form:
    ρ c(x)'c(x) + λ c(x)

    where c(x) = Ax - b.

    The Hessian matrix is then
    Htot = ρ (c.H + J.J + H.c) + λ H

    But H = 0 for Affine equalities, so
    Htot = ρ (J.J) = ρ A'A
    =#

    jacob = getGradC(r, x)
    return sparse(rho * jacob'jacob)
end

max_stars_repo_name: danineamati/TrajOptSOCPs


text:
using Documenter, Rebugger

makedocs(
    modules = [Rebugger],
    clean = false,
    format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"),
    sitename = "Rebugger.jl",
    authors = "Tim Holy",
    linkcheck = !("skiplinks" in ARGS),
    pages = [
        "Home" => "index.md",
        "usage.md",
        "config.md",
        "limitations.md",
        "internals.md",
        "reference.md",
    ],
)

deploydocs(
    repo = "github.com/timholy/Rebugger.jl.git",
)


text:
using PQEcolaPoint
using Dates

# create a SHORT put contract -
put_contract = CallContractModel()
put_contract.ticker = "XYZ"
put_contract.expiration_date = Date(2022, 04, 14)
put_contract.strike_price = 105.0
put_contract.premium = 1.0
put_contract.number_of_contracts = 1
put_contract.direction = 1

# setup the underlying -
underlying_range = range(100.0, stop = 110.0, length = 1000) |> collect

# compute the table -
short_put_table = expiration(put_contract, underlying_range)


text:
using Compat

using Compat.Test
using Compat.Pkg
using ApplicationBuilder

const julia_v07 = VERSION > v"0.7-"

builddir = mktempdir()
@assert isdir(builddir)

@testset "make_bundle_identifier Utils" begin
@test occursin(r"""^com.[a-z0-9]+.myappnamedthisapp22$""",
            ApplicationBuilder.make_bundle_identifier("My app named this_app22")
      )
end

@testset "HelloWorld.app" begin
@test 0 == include("build_examples/hello.jl")
@test isdir("$builddir/HelloWorld.app")
@test success(`$builddir/HelloWorld.app/Contents/MacOS/hello`)

# There shouldn't be a Libraries dir since none specified.
@test !isdir("$builddir/HelloWorld.app/Contents/Libraries")

# Ensure all dependencies on Julia libs are internal, so the app is portable.
@testset "No external Dependencies" begin
@test !success(pipeline(
                `otool -l "$builddir/HelloWorld.app/Contents/MacOS/hello"`,
                `grep 'julia'`,  # Get all julia deps
                `grep -v '@rpath'`))  # make sure all are relative.
end
end

@testset "commandline_app" begin
@test 0 == include("build_examples/commandline_hello.jl")
@test success(`open $builddir/hello.app`)
end

function testRunAndKillProgramSucceeds(cmd, timeout=10)
    out, _, p = readandwrite(cmd) # Make sure it runs correctly
    sleep(1)
    process_exited(p) && (println("Test Failed: failed to launch: \n", readstring(out)); return false)
    sleep(timeout)
    process_exited(p) && (println("Test Failed: Process died: \n", readstring(out)); return false)
    # Manually kill program after it's been running for a bit.
    kill(p); sleep(1)
    process_exited(p) || (println("Test Failed: Process failed to exit: \n", readstring(out)); return false)
    return true
end

# Test that it can run without .julia directory (Dangerous!)
function testBundledSuccessfully_macro(cmd_expr, timeout=10)
    quote
        val = false
        mv(Pkg.dir(), Pkg.dir()*".bak")  # NOTE: MUST mv() THIS BACK
        try
            val = testRunAndKillProgramSucceeds($cmd_expr, $timeout)
        catch
        end
        mv(Pkg.dir()*".bak", Pkg.dir())  # NOTE: MUST RUN THIS LINE IF ABOVE IS RUN
        val
    end
end
macro testBundledSuccessfully(expr...)
    testBundledSuccessfully_macro(expr...)
end

if !julia_v07  # Blink and SDL don't yet work on julia v0.7.

# Disabling the SDL tests since Cairo is currently broken in METADATA.
#@testset "sdl: simple example of binary dependencies" begin
#@test 0 == include("build_examples/sdl.jl")
## Test that it runs correctly
#@test testRunAndKillProgramSucceeds(`$builddir/HelloSDL2.app/Contents/MacOS/sdl`)
## Test that it can run without .julia directory
#@test @testBundledSuccessfully(`$builddir/HelloSDL2.app/Contents/MacOS/sdl`, 3)
#end

@testset "HelloBlink.app" begin
@test 0 == include("build_examples/blink.jl")

@test isdir("$builddir/HelloBlink.app")
# Test that it copied the correct files
@test isdir("$builddir/HelloBlink.app/Contents/Libraries")
@test isfile("$builddir/HelloBlink.app/Contents/Resources/main.js")
# Test that it runs correctly
@test testRunAndKillProgramSucceeds(`$builddir/HelloBlink.app/Contents/MacOS/blink`)
# Test that it can run without .julia directory
@test @testBundledSuccessfully(`$builddir/HelloBlink.app/Contents/MacOS/blink`, 10)
end
end


text:
"""
Cache for a single parameter/variable/dual.
Stores arrays chronologically by simulation timestamp.
"""
mutable struct ParamResultCache
    key::ParamCacheKey
    "Contains both clean and dirty entries. Any key in data that is earlier than the first
    dirty timestamp must be clean."
    data::OrderedDict{Dates.DateTime, Array}
    "Oldest entry is first"
    dirty_timestamps::Deque{Dates.DateTime}
    stats::CacheStats
    size_per_entry::Int
    flush_rule::CacheFlushRule
end

function ParamResultCache(key, flush_rule)
    return ParamResultCache(
        key,
        OrderedDict{Dates.DateTime, Array}(),
        Deque{Dates.DateTime}(),
        CacheStats(),
        0,
        flush_rule,
    )
end

Base.length(x::ParamResultCache) = length(x.data)
get_cache_hit_percentage(x::ParamResultCache) = get_cache_hit_percentage(x.stats)
get_size(x::ParamResultCache) = length(x) * x.size_per_entry
has_clean(x::ParamResultCache) = !isempty(x.data) && !is_dirty(x, first(keys(x.data)))
has_dirty(x::ParamResultCache) = !isempty(x.dirty_timestamps)
should_keep_in_cache(x::ParamResultCache) = x.flush_rule.keep_in_cache

function get_dirty_size(cache::ParamResultCache)
    return length(cache.dirty_timestamps) * cache.size_per_entry
end

function is_dirty(cache::ParamResultCache, timestamp)
    isempty(cache.dirty_timestamps) && return false
    return timestamp >= first(cache.dirty_timestamps)
end

function Base.empty!(cache::ParamResultCache)
    empty!(cache.data)
    empty!(cache.dirty_timestamps)
    cache.size_per_entry = 0
end

"""
Adds thrame result to the cache.
Return true if the cache needs to be flushed.
"""
function add_result!(cache::ParamResultCache, timestamp, array, system_cache_is_full)
    if cache.size_per_entry == 0
        cache.size_per_entry = length(array) * sizeof(first(array))
    end

    @debug "add_result!" cache.key timestamp get_size(cache)
    @assert !haskey(cache.data, timestamp) "$(cache.key) $timestamp"

    if system_cache_is_full && has_clean(cache)
        popfirst!(cache.data)
        @debug "replaced cache entry" cache.key
    end

    _add_result!(cache, timestamp, array)
    return cache.size_per_entry
end

function _add_result!(cache::ParamResultCache, timestamp, data)
    cache.data[timestamp] = data
    push!(cache.dirty_timestamps, timestamp)
end

function discard_results!(cache::ParamResultCache, timestamps)
    for timestamp in timestamps
        pop!(cache.data, timestamp)
    end

    @debug "Removed $(first(timestamps)) - $(last(timestamps)) from cache" cache.key
end

function get_data_to_flush!(cache::ParamResultCache, flush_size)
    num_chunks = flush_size < cache.size_per_entry ? 1 : flush_size ÷ cache.size_per_entry
    num_chunks = minimum((num_chunks, length(cache.dirty_timestamps)))
    @assert_op num_chunks > 0

    timestamps = [popfirst!(cache.dirty_timestamps) for i in 1:num_chunks]
    # Uncomment for performance testing of CacheFlush
    #TimerOutputs.@timeit RUN_SIMULATION_TIMER "Concatenate arrays for flush" begin
    arrays = (cache.data[x] for x in timestamps)
    arrays = cat(arrays..., dims = ndims(first(arrays)) + 1)
    #end

    return timestamps, arrays
end

function has_timestamp(cache::ParamResultCache, timestamp)
    present = haskey(cache.data, timestamp)
    if present
        cache.stats.hits += 1
    else
        cache.stats.misses += 1
    end

    return present
end


text:
## Polynomial root finder for polynomials with multiple roots
##
## Based on "Computing multiple roots of inexact polynomials"
## http://www.neiu.edu/~zzeng/mathcomp/zroot.pdf
## Author: Zhonggang Zeng
## Journal: Math. Comp. 74 (2005), 869-903
##
## Zeng has a MATLAB package `multroot`, from which this name is derived.
## Basic idea is
## 1) for polynomial p we do gcd decomposition p = u * v; p' = u * w. Then roots(v) are the roots without multiplicities.
## 2) can repeat with u to get multiplicities.
##
## This is from Gauss, as explained in paper. Zeng shows how to get u,v,w when the polynomials
## are inexact due to floating point approximations or even model error. This is done in his
## algorithm II.
## 3) Zeng's algorithm I (pejroot) uses the pejorative manifold of Kahan and Gauss-Newton to
## improve the root estimates from algorithm II (roots(v)). The pejorative manifold is defined by
## the multiplicities l and is operationalized in evalG and evalJ from Zeng's paper.

using Polynomials
import Polynomials: degree

## map monic(p) to a point in C^n
## p = 1x^n + a1x^n-1 + ... + an_1 x + an -> (a1,a2,...,an)
function p2a(p::Poly)
    p = monic(p)
    rcoeffs(p)[2:end]
end

## get value of gl(z). From p16
function evalG(zs::Vector, ls::Vector)
    length(zs) == length(ls) || throw("Length mismatch")

    s = prod([poly([z])^l for (z,l) in zip(zs, ls)])  # \prod (x-z_i)^l_i
    p2a(s)
#    rcoeffs(s)[2:end]
end

## get jacobian J_l(z), p16
function evalJ(zs::Vector, ls::Vector)
    length(zs) == length(ls) || throw("Length mismatch")
    m = length(zs)

    u = prod([poly([z])^(l-1) for (z,l) in zip(zs, ls)]) ## Pi (1-z)^(l-1)

    J = zeros(eltype(zs), sum(ls), m)
    for j in 1:m
        s = -ls[j] * u
        for i in 1:m
            if i != j
                s = s * poly([zs[i]])
            end
        end
        J[:,j] = rcoeffs(s)
    end
    J
end

## Gauss-Newton iteration to solve weighted least squares problem
## G_l(z) = a, where a is related to monic version of polynomial p
## l is known multiplicity structure of polynomial p = (x-z1)^l1 * (x-z2)^l2 * ... * (x-zn)^ln
## Algorithm I, p17
function pejroot(p::Poly, z0::Vector, l::Vector{Int};
                 wts::(@compat Union{Vector, Void})=nothing, # weight vector
                 tol = 1e-8,
                 maxsteps = 100
                      )

    a = p2a(p) #rcoeffs(monic(p))[2:end] # an_1, an_2, ..., a2, a1, a0

    if wts == nothing
        wts = map(u -> min(1, 1/abs(u)), a)
    end
    W = diagm(wts)

    ## Solve WJ Δz = W(Gl(z) - a) in algorithm I
    G(z) = (evalG(z, l) - a)
    update(z, l) = z -  weighted_least_square(evalJ(z,l), G(z), wts)

    zk = copy(z0); zk1 = update(zk, l)
    deltaold = norm(zk1 - zk,2); zk = zk1

    cvg = false
    for ctr in 1:maxsteps
        zk1 = update(zk, l)
        delta = norm(zk1 - zk, 2)

        if delta > deltaold
            println("Growing delta. Best guess is being returned.")
            break
        end

        ## add extra abs(delta) < 100*eps() condition
        if delta^2 / (deltaold - delta) < tol || abs(delta) < 100*eps()
            cvg = true
            break
        end

        deltaold = delta
        zk=zk1
    end

    if !cvg println("""
Returning the initial estimates, as the
algorithm failed to improve estimates for the roots on the given
pejorative manifold.
""")
        return(z0)
    end
    return(zk1)
end

## Main interface to finding roots of polynomials with multiplicities
##
## The `multroot` function returns the roots and their multiplicities
## for `Poly` objects. It performs better than `roots` if the
## polynomial has multiplicities.
##
## julia> x = poly([0.0]);
## julia> p = (x-1)^4 * (x-2)^3 * (x-3)^3 * (x-4)l
## julia> multroot(p)
## ([1.0,2.0,3.0,4.0],[4,3,3,1])
## ## For "prettier" printing, results can be coerced to a dict
## julia> [k => v for (k,v) in zip(multroot(p)...)]
## Dict{Any,Int64} with 4 entries:
##   1.0000000000000007 => 4
##   3.000000000000018  => 3
##   1.9999999999999913 => 3
##   3.999999999999969  => 1
## ## Large order polynomials prove difficult. We can't match the claims in Zeng's paper
## ## as we don't get the pejorative manifold structure right.
## julia> p = poly([1.0:10.0]);
## julia> multroot(p) ## should be 1,2,3,4,...,10 all with multplicity 1, but
## ([1.0068,2.14161,3.63283,5.42561,7.25056,8.81228,9.98925],[1,2,1,2,2,1,1])
##
## nearby roots can be an issue
## julia> delta = 0.0001  ## delta = 0.001 works as desired.
## julia> p = (x-1 - delta)*(x-1)*(x-1 + delta)
## julia> multroot(p)
## ([0.999885,1.00006],[1,2])
function multroot(p::Poly;
                  θ::Real=1.0,   # singular threshold, 1.0 is replaced by normf(A)*eps()
                  ρ::Real=1e-10, # initial residual tolerance
                  ϕ::Real=1e2,   # residual tolerance growth factor
                  δ::Real=1e-8   # passed to solve y sigma

                  )

    degree(p) == 0 && error("Degree of `p` must be atleast 1")

    if degree(p) == 1
        return(roots(p), [1])
    end

    ## if degree(p) == 2
    ##     a,b,c = coeffs(p)
    ##     discr = b^2 - 4a*c
    ##     if discr < 0
    ##         discr = Complex(discr, 0)
    ##     end
    ##     return  ( -2c / (-b - sqrt(discr)), -2c/(-b + sqrt(discr)))
    ## end

    p = Poly(float(coeffs(p)))  # floats, not Int

    u_j, v_j, w_j, residual= agcd(p, p',  ρ = ρ)
    ρ = max(ρ, ϕ * residual)

    ## bookkeeping
    zs = roots(v_j)
    ls = ones(Int, length(zs))

    p0 = u_j

    while degree(p0) > 0
        if degree(p0) == 1
            z = roots(p0)[1]
            _, ind = findmin(abs(zs .- z))
            ls[ind] = ls[ind] + 1
            break
        end

        u_j, v_j, w_j, residual= agcd(p0, p0', ρ=ρ)

        ## need to worry about residual between
        ## u0 * v0 - monic(p0) and u0 * w0 - monic(Polynomials.polyder(p0))
        ## resiudal tolerance grows with m, here it depends on
        ## initial value and previous residual times a growth tolerance, ϕ
        ρ = max(ρ, ϕ * residual)

        ## update multiplicities
        for z in roots(v_j)
            _, ind = findmin(abs(zs .- z))
            ls[ind] = ls[ind] + 1
        end

        ## rename
        p0 = u_j
    end

    if maximum(ls) == 1
        return(zs, ls)
    else
        zs = pejroot(p, zs, ls)
        return(zs, ls)
    end
end

## Different interfaces

## can pass in vector too
multroot{T <: Real}(p::Vector{T}; kwargs...) = multroot(Poly(p); kwargs...)

## Can pass in function
function multroot(f::Function; kwargs...)
    p = Poly([0.0])
    try
        p = convert(Poly{Float64}, f)
    catch err
        error("The function does not compute a univariate polynomial")
    end
    multroot(p; kwargs...)

end

## add funciton interface to Polynomials.roots
Polynomials.roots(f::Function) = roots(convert(Poly{Float64}, f))


text:
using ExtensibleScheduler
using TimeFrames
using Dates: now

function print_time_noparam()
    println("From print_time_noparam $(now(Dates.UTC))")
end

function print_time_args(x)
    println("From print_time_args $(now(Dates.UTC)) $x")
end

function print_time_kwargs(; a="default")
    println("From print_time_kwargs $(now(Dates.UTC)) $a")
end

function sample()
    # Use BlockingScheduler with default jobstore, default executor...
    sched = BlockingScheduler()

    # Define what action will be executed
    action = Action(print_time_kwargs; Dict(:a=>"keyword")...)

    # Define when job should be triggered
    trigger = Trigger(DateTime(2017, 12, 17, 20, 8, 0))  # execute one time at given DateTime
    #trigger = Trigger(DateTime(2017, 12, 17, 20, 8, 5))  # execute one time at given DateTime
    #trigger = Trigger(now(Dates.UTC) - Dates.Second(5))  # execute one time at given DateTime (misfire)
    #trigger = Trigger(now(Dates.UTC) + Dates.Second(5))  # execute one time at given DateTime
    #trigger = Trigger(Dates.Time(15, 58), n=1)  # execute one time at given Time (of current day or of next day)
    #trigger = Trigger(Second(5), n=1)  # execute one time (5 seconds after being add)
    #trigger = Trigger(tf"5s")  # periodic job ; priority=0 by default

    # Add job to jobstore
    add(sched, action, trigger)

    # Run scheduler
    run(sched)
end

sample()


text:
module TestCodeCov

_identity(x) = x
_add_one(x) = x + one(x)

end


text:
csv_dir = Pkg.dir("D4M") * "/examples/1Intro/2EdgeArt/Edge.csv"
print(csv_dir)

E = ReadCSV(csv_dir);                       # Read CSV file into associative array.
print(E);                                # Show in tabular form.

Eo = E[(E[:,"Color,"] == "Orange" ).row,:];   # Get orange edges.
print(Eo);                               # Show in tabular form.

Eog = E[ StartWith("O,G,") ,:];               # Get orange and green edges.
print(Eog);


text:
include("tg_scrape.jl")

const main_html = getmain()

const page = matchFirst(sel"div#dev_page_content", main_html)

const funcs = process_page(page)
create_api(funcs, joinpath("..", "src", "telegram_api.jl"))
create_docs(funcs, joinpath("..", "docs", "src", "reference.md"))


text:
abstract type StateEstimator end

mutable struct CarStateEstimator{S <: AbstractSensor} <: StateEstimator
    mdp::PedCarMDP
    id::Int64
    ego::Vehicle
    sensor::S
end

function ParticleFilters.generate_s(model::CarStateEstimator, s::CarState, rng::AbstractRNG)
    car, route = s
    if  car == model.mdp.off_grid
        act_car = LonAccelDirection(0., 0)
    else
        cur_lane = get_lane(model.mdp.env.roadway, car)
        set_direction!(model.mdp.car_models[route].navigator, cur_lane, model.mdp.env.roadway)
        act_car = LonAccelDirection(0.0, model.mdp.car_models[route].navigator.dir)
    end
    d = model.mdp._car_transition_dict[(car, route, act_car)]
    return rand(rng, d)
end

function ParticleFilters.obs_weight(model::CarStateEstimator, s::CarState, sp::CarState, o::Vector{Vehicle})
    car, route = sp
    car_ind = findfirst(x->x.id==model.id, o)
    if car_ind == nothing
        obs = nothing
    else
        obs = o[car_ind].state
    end
    if car == model.mdp.off_grid
        weight = AutomotiveSensors.obs_weight(model.sensor, model.ego.state, obs, nothing, model.mdp.env.obstacles)
    else
        weight = AutomotiveSensors.obs_weight(model.sensor, model.ego.state, obs, car, model.mdp.env.obstacles)
    end
end

function init_car_belief(mdp::PedCarMDP, n_particles::Int64, rng::AbstractRNG)
    routes = get_car_routes(mdp.env)
    sampled_full_routes = rand(rng, routes, n_particles)
    sampled_states = [rand(rng, get_car_states(mdp.env, route, mdp.pos_res, mdp.vel_res)) for route in sampled_full_routes]
    sampled_routes = [SVector(route[1], route[end]) for route in sampled_full_routes]
    #TODO sample off the grid states
    return ParticleCollection(collect(zip(sampled_states, sampled_routes)))
end

mutable struct PedStateEstimator{S <: AbstractSensor} <: StateEstimator
    mdp::PedCarMDP
    id::Int64
    ego::Vehicle
    sensor::S
end

function ParticleFilters.generate_s(model::PedStateEstimator, ped::VehicleState, rng::AbstractRNG)
    d = model.mdp._ped_transition_dict[(ped, ConstantSpeedDawdling(0., 0))]
    return rand(rng, d)
end

function ParticleFilters.obs_weight(model::PedStateEstimator, ped::VehicleState, pedp::VehicleState, o::Vector{Vehicle})
    ped_ind = findfirst(x->x.id==model.id, o)
    if ped_ind == nothing
        obs = nothing
    else
        obs = o[ped_ind].state
    end
    if ped == model.mdp.off_grid
        weight = AutomotiveSensors.obs_weight(model.sensor, model.ego.state, obs, nothing, model.mdp.env.obstacles)
    else
        weight = AutomotiveSensors.obs_weight(model.sensor, model.ego.state, obs, ped, model.mdp.env.obstacles)
    end
end

function init_ped_belief(mdp::PedCarMDP, n_particles::Int64, rng::AbstractRNG)
    sampled_states = rand(rng, get_ped_states(mdp.env, mdp.pos_res, mdp.vel_ped_res), n_particles)
    return ParticleCollection(sampled_states)
end

function ParticleFilters.update(up::SimpleParticleFilter, b::ParticleCollection{S}, o::Vector{Vehicle}) where S <: Union{CarState, VehicleState}
    ps = particles(b)
    pm = up._particle_memory
    wm = up._weight_memory
    resize!(pm, 0)
    resize!(wm, 0)
    sizehint!(pm, n_particles(b))
    sizehint!(wm, n_particles(b))
    all_terminal = true
    for i in 1:n_particles(b)
        s = ps[i]
        sp = ParticleFilters.generate_s(up.model, s, up.rng)
        push!(pm, sp)
        push!(wm, ParticleFilters.obs_weight(up.model, s, sp, o))
    end
    return resample(up.resample, WeightedParticleBelief{S}(pm, wm, sum(wm), nothing), up.rng)
end

@with_kw mutable struct ParticleOverlay{S <: Union{CarState, VehicleState}} <: SceneOverlay
    mdp::PedCarMDP = PedCarMDP()
    b::ParticleCollection{S} = ParticleCollection(Vector{S}(undef, 0))
    n_render::Int64 = 100
    rng::AbstractRNG = MersenneTwister(1)
    color::Colorant = RGBA(0.976, 0.592, 0.122, 0.15) # orange
end

function AutoViz.render!(rendermodel::RenderModel, overlay::ParticleOverlay{CarState}, scene::Scene, roadway::R) where R
    for i=1:overlay.n_render
        car, route = rand(overlay.rng, overlay.b)
        p = car.posG
        length = overlay.mdp.car_type.length
        width = overlay.mdp.car_type.width
        add_instruction!(rendermodel, render_vehicle, (p.x, p.y, p.θ, length, width, overlay.color, overlay.color, overlay.color))
    end
    overlay.rng = MersenneTwister(1) # reset rng
    return rendermodel
end

function AutoViz.render!(rendermodel::RenderModel, overlay::ParticleOverlay{VehicleState}, scene::Scene, roadway::R) where R
    for i=1:overlay.n_render
        ped = rand(overlay.rng, overlay.b)
        p = ped.posG
        length = overlay.mdp.ped_type.length
        width = overlay.mdp.ped_type.width
        add_instruction!(rendermodel, render_vehicle, (p.x, p.y, p.θ, length, width, overlay.color, overlay.color, overlay.color))
    end
    overlay.rng = MersenneTwister(1) # reset rng
    return rendermodel
end


text:
using Random

export AbstractAgent, start!, step!, get_action

abstract type AbstractAgent end

"""
    start!(agent::AbstractAgent, env_s_tp1, rng=Random.GLOBAL_RNG, kwargs...)

Function for starting the agent for a new episode.

returns an action to get passed to the environment
"""
function start!(agent::AbstractAgent, env_s_tp1; kwargs...)
    return start!(agent, env_s_tp1, Random.GLOBAL_RNG; kwargs...)
end

"""
    start!(agent::AbstractAgent, env_s_tp1, rng=Random.GLOBAL_RNG, kwargs...)
"""
function start!(agent::AbstractAgent, env_s_tp1, rng::AbstractRNG; kwargs...)
    @error "Implement start! function for agent $(typeof(agent))"
end

"""
    step!(agent::AbstractAgent, env_s_tp1, r, terminal; kwargs...)

Function to take a step with an agent.

Returns an action to get passed to the environment.
"""
function step!(agent::AbstractAgent, env_s_tp1, r, terminal; kwarngs...)
    return step!(agent, env_s_tp1, r, terminal, Random.GLOBAL_RNG; kwargs...)
end

"""
    step!(agent::AbstractAgent, env_s_tp1, r, terminal, rng::AbstractRNG; kwargs...)
"""
function step!(agent::AbstractAgent, env_s_tp1, r, terminal, rng::AbstractRNG; kwargs...)
    @error "Implement step! function for agent $(typeof(agent))"
end

"""
    get_action(agent::AbstractAgent, state; kwargs...)

Returns an action, deterministacally.
"""
get_action(agent::AbstractAgent, state) = get_action(agent, state, Random.GLOBAL_RNG)

"""
    get_action(agent::AbstractAgent, state, rng::AbstractRNG; kwargs...)
"""
function get_action(agent::AbstractAgent, state, rng::AbstractRNG)
    @error "Implement get Action for agent"
end

max_stars_repo_name: mkschleg/JuliaRL

max_issues_repo_name: mkschleg/JuliaRL

max_forks_repo_name: mkschleg/JuliaRL