text:
# https://adventofcode.com/2020/day/12

#  * Action `N` means to move north by the given value.
#  * Action `S` means to move south by the given value.
#  * Action `E` means to move east by the given value.
#  * Action `W` means to move west by the given value.
#  * Action `L` means to turn left the given number of degrees.
#  * Action `R` means to turn right the given number of degrees.
#  * Action `F` means to move forward by the given value in the
#    direction the ship is currently facing.

example = "
F10
N3
F7
R90
F11
"

const directions = (:North, :East, :South, :West)
const char_to_direction = Dict("N" => :North, "E" => :East, "S" => :South, "W" => :West)

Point = Tuple{Integer,Integer}

mutable struct Ship
    facing::Symbol
    position::Point
    waypoint::Point
end

facing(ship::Ship) = ship.facing
position(ship::Ship) = ship.position
waypoint(ship::Ship) = ship.waypoint

function rotate(facing::Symbol, direction::Symbol, deg::Integer=90)
    steps, reminder = divrem(deg, 90)
    reminder == 0 || error("ups, it can move diagonally!")
    direction = direction == :Right ? +1 : -1
    direction_index = findfirst(directions .== facing)
    direction_index = mod1(direction_index + steps * direction, 4)
    return directions[direction_index]
end

@assert rotate(:East, :Left) == :North
@assert rotate(:North, :Left) == :West
@assert rotate(:North, :Right) == :East
@assert rotate(:East, :Right) == :South
@assert rotate(:North, :Left, 360) == :North

function part1(string::AbstractString; verbose=false)
    ship = Ship(:East, (0, 0), (0, 0))

    for row in split(string, '\n', keepempty=false)
        row = strip(row)
        m = match(r"([NSEWLRF])(\d+)", row)
        if isnothing(m)
            error("invalid input")
        end
        action, value = m.captures
        value = parse(Int, value)

        if action in ("L", "R")
            direction = rotate(facing(ship), action == "L" ? :Left : :Right, value)
            ship.facing = direction
            value = 0
        elseif action == "F"
            direction = facing(ship)
        else
            direction = char_to_direction[action]
        end

        x, y = position(ship)
        if direction == :North
            x += value
        elseif direction == :South
            x -= value
        elseif direction == :East
            y += value
        else
            y -= value
        end
        ship.position = (x, y)

    end
    return position(ship) .|> abs |> sum
end

@assert part1(example) == 25

function rotate(point::Point, deg::Integer)::Point
    θ = deg2rad(deg)
    x, y = point
    x_new = cos(θ) * x - sin(θ) * y
    y_new = sin(θ) * x + cos(θ) * y
    return Int(round(x_new)), Int(round(y_new))
end

@assert rotate((10, 4), 90) == (-4, 10)

function part2(string::AbstractString; verbose=false)
    ship = Ship(:East, (0, 0), (1, 10))

    for row in split(string, '\n', keepempty=false)
        row = strip(row)
        m = match(r"([NSEWLRF])(\d+)", row)
        if isnothing(m)
            error("invalid input")
        end
        action, value = m.captures
        value = parse(Int, value)

        if action in ("N", "S", "W", "E")
            x, y = waypoint(ship)
            if action == "N"
                x += value
            elseif action == "S"
                x -= value
            elseif action == "E"
                y += value
            else
                y -= value
            end
            ship.waypoint = (x, y)
        elseif action in ("L", "R")
            if action == "L"
                value = 360 - value
            end
            ship.waypoint = rotate(waypoint(ship), value)
        else
            wx, wy = waypoint(ship)
            px, py = position(ship)
            ship.position = (px + wx * value, py + wy * value)
        end

        verbose && println("$(row):\t Ship at $(position(ship)), with waypoint $(waypoint(ship))")

    end
    return position(ship) .|> abs |> sum
end

@assert part2(example) == 286

test = read("data/day-12.txt", String)
println("Part 1: $(result1 = part1(test))")
println("Part 2: $(result2 = part2(test))")

@assert result1 == 415
@assert result2 == 29401

max_stars_repo_name: twolodzko/advent-of-code

max_issues_repo_name: twolodzko/advent-of-code

max_forks_repo_name: twolodzko/advent-of-code


text:
#
# Correctness Tests
#

using Test
using PowerAnalysis
using HypothesisTests

include("t_test.jl")
include("z_test.jl")

ext: jl


text:
using Test
using MianalyzerBackend
const Stats = MianalyzerBackend.Stats

@testset "Stats" begin
  @testset ".max" begin
    @test Stats.max([1 2 3 4 5]) == 5
    @test Stats.max([10.1 10.2 10.9 10.6]) == 10.9
  end

  @testset ".min" begin
    @test Stats.min([1 2 3 4 5]) == 1
    @test Stats.min([10.1 10.2 10.9 10.6]) == 10.1
  end

  @testset ".mean" begin
    @test Stats.mean([1 2 3 4 5]) == 3
    @test Stats.mean([10.1 10.2 10.9 10.6]) == 10.45
  end

  @testset ".sum" begin
    @test Stats.sum([1 2 3 4 5]) == 15
    @test Stats.sum([10.1 10.2 10.9 10.6]) == 41.8
  end

  @testset ".count" begin
    @test Stats.count([1 2 3 4 5]) == 5
    @test Stats.count([10.1 10.2 10.9 10.6]) == 4
  end

end


text:
include("debug.jl")

# Data Structures

mutable struct SSLConfig
    data::Ptr{Cvoid}
    rng
    chain::CRT
    dbg
    cert
    key
    alpn_protos

    function SSLConfig()
        conf = new()
        conf.data = Libc.malloc(1000)  # 360
        ccall((:mbedtls_ssl_config_init, libmbedtls), Cvoid, (Ptr{Cvoid},), conf.data)
        finalizer(x->begin
            data = x.data
            @async begin
                ccall((:mbedtls_ssl_config_free, libmbedtls),
                      Cvoid, (Ptr{Cvoid},), data)
                Libc.free(data)
            end
        end, conf)
        conf
    end
end

Base.show(io::IO, c::SSLConfig) = print(io, "MbedTLS.SSLConfig()")

mutable struct SSLContext <: IO
    data::Ptr{Cvoid}
    datalock::ReentrantLock
    waitlock::ReentrantLock
    config::SSLConfig
    isreadable::Bool
    bytesavailable::Int
    close_notify_sent::Bool
    async_exception::Union{Nothing,MbedException}
    bio

    function SSLContext()
        ctx = new()
        ctx.data = Libc.malloc(1000)  # 488
        ctx.datalock = ReentrantLock()
        ctx.waitlock = ReentrantLock()
        ctx.isreadable = false
        ctx.bytesavailable = -1
        ctx.close_notify_sent = false
        ctx.async_exception = nothing
        ccall((:mbedtls_ssl_init, libmbedtls), Cvoid, (Ptr{Cvoid},), ctx.data)
        finalizer(x->begin
            data = x.data
            @async begin
                ccall((:mbedtls_ssl_free, libmbedtls),
                      Cvoid, (Ptr{Cvoid},), ctx.data)
                Libc.free(ctx.data)
            end
        end, ctx)
        ctx
    end
end

# Handshake

function handshake(ctx::SSLContext)

    ctx.isreadable && throw(ArgumentError("handshake() already done!"))
                                                                                ;@😬 "🤝 ..."
    while true
        n = ssl_handshake(ctx)
        if n == 0
            break
        elseif n == MBEDTLS_ERR_SSL_WANT_READ                                   ;@😬 "🤝  ⌛"
            if eof(ctx.bio)
                throw(EOFError())                                               ;@💀 "🤝  🛑"
            end
        else
            ssl_abandon(ctx)                                                    ;@💀 "🤝  💥"
            throw(MbedException(n))
        end
    end
                                                                                ;@😬 "🤝  ✅"
    ctx.isreadable = true
    ctx.bytesavailable = 0
    ctx.close_notify_sent = false

    @async try
        while ctx.isreadable
            wait_for_decrypted_data(ctx)
            while ctx.bytesavailable > 0
                sleep(5)
            end
        end
    catch e
        ctx.async_exception = e
    end

    nothing
end

function check_async_exception(ctx::SSLContext)
    if ctx.async_exception !== nothing
        e = ctx.async_exception
        ctx.async_exception = nothing
        throw(e)
    end
end

# Fatal Errors

"""
The documentation for `ssl_read`, `ssl_write` and `ssl_close_notify` all say:

> If this function returns something other than 0 or
> MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using the SSL context
> for reading or writing, and either free it or call

This function ensures that the `SSLContext` is won't be used again.
"""
function ssl_abandon(ctx::SSLContext)                                           ;@💀 "ssl_abandon 💥"
    ctx.isreadable = false
    ctx.bytesavailable = 0
    ctx.close_notify_sent = true
    close(ctx.bio)
    n = ssl_session_reset(ctx)
    n == 0 || throw(MbedException(n))
end

# Base ::IO Connection State Methods

Sockets.getsockname(ctx::SSLContext) = Sockets.getsockname(ctx.bio)

"""
    isreadable(ctx::SSLContext)

True unless:
 - TLS `close_notify` was received, or
 - the peer closed the connection (and the TLS buffer is empty), or
 - an un-handled exception occurred while reading.
"""
function Base.isreadable(ctx::SSLContext)
    ctx.isreadable || return false
    # It's possible we received the shutdown, but didn't process it yet - if so,
    # do that now.
    if bytesavailable(ctx.bio) > 0 || ssl_check_pending(ctx)
        ssl_unsafe_read(ctx, Ptr{UInt8}(C_NULL), UInt(0))
    end
    return ctx.isreadable
end

"""
    iswritable(ctx::SSLContext)

True unless:
 - `close(::SSLContext)` is called, or
 -  the peer closed the connection.
"""
Base.iswritable(ctx::SSLContext) = !ctx.close_notify_sent && isopen(ctx.bio)

"""
    isopen(ctx::SSLContext)

Same as `iswritable(ctx)`.
> "...a closed stream may still have data to read in its buffer,
>  use eof to check for the ability to read data." [?Base.isopen]
"""
Base.isopen(ctx::SSLContext) = iswritable(ctx)

@static if isdefined(Base, :bytesavailable)
"""
    bytesavailable(ctx::SSLContext)

Number of decrypted bytes waiting in the TLS buffer.
"""
Base.bytesavailable(ctx::SSLContext) = ctx.bytesavailable
else
Base.nb_available(ctx::SSLContext) = ctx.bytesavailable
end

"""
    eof(ctx::SSLContext)

True if not `isreadable` and there are no more `bytesavailable` to read.
"""
function Base.eof(ctx::SSLContext)
    if ctx.bytesavailable > 0
        return false
    end                                                                         ;@😬 "eof ⌛"
    wait_for_decrypted_data(ctx)
    @assert ctx.bytesavailable > 0 || !ctx.isreadable
    return ctx.bytesavailable <= 0
end

"""
    close(ctx::SSLContext)

Send a TLS `close_notify` message to the peer.
"""
function Base.close(ctx::SSLContext)                                            ;@💀 "close iswritable=$(iswritable(ctx))"

    if iswritable(ctx)

        n = ssl_close_notify(ctx)
        ctx.close_notify_sent = true                                            ;@💀 "close 🗣"

        if n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE    ;@💀 "close ⌛"
            @assert false "Should not get to here because `f_send` " *
                          "never returns ...WANT_READ/WRITE."
        elseif n != 0
            ssl_abandon(ctx)
            throw(MbedException(n))
        end
    end
    @assert !iswritable(ctx)
    nothing
end

# Sending Data

"""
Copy `nbytes` from `buf` to `ctx`.

The TLS library function `ssl_write` is called as many times as needed to send
all the data. The TLS library encrypts the data and passes it to the `f_send`
function which sends it to the underlying connection (`ctx.bio`).
See `f_send` and `set_bio!` below.
"""
function ssl_unsafe_write(ctx::SSLContext, buf::Ptr{UInt8}, nbytes::UInt)

    Base.check_open(ctx.bio) # Throw error if `bio` is closed.

    iswritable(ctx) ||
    throw(ArgumentError("`unsafe_write` requires `iswritable(::SSLContext)`"))

    nwritten = 0                                                                ;@🤖 "ssl_write ➡  $nbytes"
    while nwritten < nbytes
        n = ssl_write(ctx, buf + nwritten, nbytes - nwritten)
        if n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE    ;@💀 "ssl_write ⌛"
            @assert false "Should not get to here because `f_send` " *
                          "never returns ...WANT_READ/WRITE."
            yield()
            continue
        elseif n == MBEDTLS_ERR_NET_CONN_RESET
            ssl_abandon(ctx)                                                    ;@🤖 "ssl_write 🛑"
            Base.check_open(ctx.bio)
            @assert false
        elseif n < 0
            ssl_abandon(ctx)                                                    ;@🤖 "ssl_write 💥"
            throw(MbedException(n))
        end
        nwritten += n
    end
    return Int(nwritten)
end

# Sending Encrypted Data

"""
Copy `nbytes` of encrypted data from `buf` to the underlying `bio` connection.
"""
function f_send(c_bio, buf, nbytes)                                             ;@🤖 "f_send ➡  $nbytes"
    bio = unsafe_pointer_to_objref(c_bio)
    if !isopen(bio) || bio.status == Base.StatusClosing
        return Cint(MBEDTLS_ERR_NET_CONN_RESET)
    end
    return Cint(unsafe_write(bio, buf, nbytes))
end

"""
Connect `f_send` and `f_recv` callback functions to `SSLContext`.
"""
function set_bio!(ctx::SSLContext, bio::T) where {T<:IO}
    isopen(bio) || throw(ArgumentError("`set_bio!` requires `isopen(bio)`"))
    ctx.bio = bio
    ssl_set_bio(ctx, pointer_from_objref(bio), c_send[], c_recv[])
    nothing
end

# Receiving Data

"""
While there are no decrypted bytes available but the connection is readable:
 - If the TLS buffer has no pending (unprocessed) data wait for
   more encrypted data to arrive on the underlying connection.
 - Run a zero-byte read to allow the library to process its internal buffer,
   and/or read from the underlying connection.
 - `ssl_unsafe_read` updates the `isreadable` and `bytesavailable` state.
"""
function wait_for_decrypted_data(ctx)
    lock(ctx.waitlock)
    try
        check_async_exception(ctx)
        while ctx.isreadable && ctx.bytesavailable <= 0
            if !ssl_check_pending(ctx)                                          ;@🤖 "wait_for_encrypted_data ⌛";
                wait_for_encrypted_data(ctx)
            end
            ssl_unsafe_read(ctx, Ptr{UInt8}(C_NULL), UInt(0))                   ;@🤖 "wait_for_decrypted_data 📥  $(ctx.bytesavailable)"
        end
    finally
        unlock(ctx.waitlock)
    end
end

"""
    ssl_unsafe_read(::SSLContext, buf, nbytes)

Copies at most `nbytes` of decrypted data into `buf`.
Never blocks to wait for more data to arrive.
Returns number of bytes copied into `buf` (`<= nbytes`).
Updates `ctx.bytesavailable` with the number of decrypted bytes remaining in
the TLS buffer.

Stops when:
 - `nbytes` have been copied, or
 - there are no more decrypted bytes available in the TLS buffer, or
 - a TLS `close_notify` message is received.

When TLS `close_notify` is received:
 - `isreadable` is set to false and `bytesavailable` is set to zero.
   [RFC5246 7.2.1]: "Any data received after a closure alert is ignored."
 - the number of bytes read before the `close_notify` is returned as usual.

Throws a `MbedException` if `ssl_read` returns an unhandled error code.

When an unhandled exception occurs `isreadable` is set to false.
"""
function ssl_unsafe_read(ctx::SSLContext, buf::Ptr{UInt8}, nbytes::UInt)

    check_async_exception(ctx)

    ctx.isreadable ||
    throw(ArgumentError("`ssl_unsafe_read` requires `isreadable(::SSLContext)`"))

    nread::UInt = 0
    try
        while true

            n = ssl_read(ctx, buf + nread, nbytes - nread)                      ;@😬 "ssl_read ⬅  $n $(n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ? "(CLOSE_NOTIFY)" :
                                                                                n == MBEDTLS_ERR_NET_CONN_RESET        ? "(CONN_RESET)" :
                                                                                n == MBEDTLS_ERR_SSL_WANT_READ         ? "(WANT_READ)" : "")"
            if n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ||
               n == MBEDTLS_ERR_NET_CONN_RESET
                ssl_abandon(ctx)
                return nread
            elseif n == MBEDTLS_ERR_SSL_WANT_READ
                ctx.bytesavailable = 0                                          ;@😬 "ssl_read ⌛ $nread"
                return nread
            elseif n < 0
                ssl_abandon(ctx)
                throw(MbedException(n))
            end

            nread += n
            @assert nread <= nbytes

            if nread == nbytes
                ctx.bytesavailable = ssl_get_bytes_avail(ctx)                   ;@🤖 "ssl_read ⬅  $nread, 📥  $(ctx.bytesavailable)"
                return nread
            end
        end
    catch e                                                                     ;@💀 "ssl_read 💥"
        ssl_abandon(ctx)
        rethrow(e)
    end

    @assert false "unreachable"
end

# Receiving Encrypted Data

function wait_for_encrypted_data(ctx)
    try
        eof(ctx.bio)
    catch e
        if !(e isa Base.IOError) || e.code != Base.UV_ECONNRESET
            rethrow(e)
        end
    end
end

"""
Copy at most `nbytes` of encrypted data to `buf` from the `bio` connection.
If no encrypted bytes are available return:
 - `MBEDTLS_ERR_SSL_WANT_READ` if the connection is still open, or
 - `MBEDTLS_ERR_NET_CONN_RESET` if it is closed.
"""
function f_recv(c_bio, buf, nbytes)
    @assert nbytes > 0
    bio = unsafe_pointer_to_objref(c_bio)
    n = bytesavailable(bio)
    if n == 0                                                                   ;@🤖 "f_recv $(isopen(bio) ? "WANT_READ" : "CONN_RESET")"
        return isopen(bio) ? Cint(MBEDTLS_ERR_SSL_WANT_READ) :
                             Cint(MBEDTLS_ERR_NET_CONN_RESET)
    end
    n = min(nbytes, n)                                                          ;@🤖 "f_recv ⬅  $n"
    unsafe_read(bio, buf, n)
    return Cint(n)
end

# Base ::IO Write Methods -- wrappers for `ssl_unsafe_write`

Base.unsafe_write(ctx::SSLContext, msg::Ptr{UInt8}, N::UInt) =
    ssl_unsafe_write(ctx, msg, N)

Base.write(ctx::SSLContext, msg::UInt8) = write(ctx, Ref(msg))

# Base ::IO Read Methods -- wrappers for `ssl_unsafe_read`

"""
    unsafe_read(ctx::SSLContext, buf::Ptr{UInt8}, nbytes::UInt)

Copy `nbytes` of decrypted data from `ctx` into `buf`.
Wait for sufficient decrypted data to be available.
Throw `EOFError` if the peer sends TLS `close_notify` or closes the
connection before `nbytes` have been copied.
"""
function Base.unsafe_read(ctx::SSLContext, buf::Ptr{UInt8}, nbytes::UInt)
    nread = 0
    while nread < nbytes
        if eof(ctx)                                                             ;@💀 "unsafe_read 🛑"
            throw(EOFError())
        end
        nread += ssl_unsafe_read(ctx, buf + nread, nbytes - nread)
    end                                                                         ;@😬 "unsafe_read ⬅ $nread"
    nothing
end

"""
    readbytes!(ctx::SSLContext, buf::Vector{UInt8}, nbytes=length(buf); kw...)

Copy at most `nbytes` of decrypted data from `ctx` into `buf`.
If `all=true`: wait for sufficient decrypted data to be available.
Less than `nbytes` may be copied if the peer sends TLS `close_notify` or closes
the connection.
Returns number of bytes copied into `buf` (`<= nbytes`).
"""
Base.readbytes!(ctx::SSLContext, buf::Vector{UInt8}, nbytes=length(buf); kw...) =
    readbytes!(ctx, buf, UInt(nbytes); kw...)

function Base.readbytes!(ctx::SSLContext, buf::Vector{UInt8}, nbytes::UInt;
                         all::Bool=true)
    nbytes <= length(buf) || throw(ArgumentError("`buf` too small!"))
    nread = 0
    while nread < nbytes
        nread += ssl_unsafe_read(ctx, pointer(buf) + nread, nbytes - nread)
        if (nread == nbytes) || !all || eof(ctx)
            break
        end
    end                                                                         ;@😬 "readbytes! ⬅  $nread"
    return nread
end

"""
    readavailable(ctx::SSLContext)

Read available decrypted data from `ctx`,
but don't wait for more data to arrive.

The amount of decrypted data that can be read at once is limited by
`MBEDTLS_SSL_MAX_CONTENT_LEN`.
"""
function Base.readavailable(ctx::SSLContext)
    n = UInt(MBEDTLS_SSL_MAX_CONTENT_LEN)
    buf = Vector{UInt8}(undef, n)
    n = ssl_unsafe_read(ctx, pointer(buf), n)                                   ;@😬 "readavailable ⬅  $n"
    return resize!(buf, n)
end

# Configuration

function config_defaults!(config::SSLConfig; endpoint=MBEDTLS_SSL_IS_CLIENT,
    transport=MBEDTLS_SSL_TRANSPORT_STREAM, preset=MBEDTLS_SSL_PRESET_DEFAULT)
    @err_check ccall((:mbedtls_ssl_config_defaults, libmbedtls), Cint,
        (Ptr{Cvoid}, Cint, Cint, Cint),
        config.data, endpoint, transport, preset)
end

function authmode!(config::SSLConfig, auth)
    ccall((:mbedtls_ssl_conf_authmode, libmbedtls), Cvoid,
        (Ptr{Cvoid}, Cint),
        config.data, auth)
end

function rng!(config::SSLConfig, f_rng::Ptr{Cvoid}, rng)
    ccall((:mbedtls_ssl_conf_rng, libmbedtls), Cvoid,
        (Ptr{Cvoid}, Ptr{Cvoid}, Any),
        config.data, f_rng, rng)
end

function rng!(config::SSLConfig, rng::AbstractRNG)
    config.rng = rng
    rng!(config, c_rng[], rng)
end

function ca_chain!(config::SSLConfig, chain=crt_parse(DEFAULT_CERT))
    config.chain = chain
    ccall((:mbedtls_ssl_conf_ca_chain, libmbedtls), Cvoid,
        (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
        config.data, chain.data, C_NULL)
end

"""
Enable / Disable renegotiation support for connection when initiated by peer
(MBEDTLS_SSL_RENEGOTIATION_ENABLED or MBEDTLS_SSL_RENEGOTIATION_DISABLED).
See: https://tls.mbed.org/api/ssl_8h.html#aad4f50fc1c0a018fd5eb18fd9621d0d3
"""
function ssl_conf_renegotiation!(config::SSLConfig, renegotiation)
    ccall((:mbedtls_ssl_conf_renegotiation, libmbedtls), Cvoid,
        (Ptr{Cvoid}, Cint),
        config.data, renegotiation)
end

function own_cert!(config::SSLConfig, cert::CRT, key::PKContext)
    config.cert = cert
    config.key = key
    @err_check ccall((:mbedtls_ssl_conf_own_cert, libmbedtls), Cint,
        (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
        config.data, cert.data, key.data)
end

function setup!(ctx::SSLContext, conf::SSLConfig)
    ctx.config = conf
    ssl_setup(ctx, conf)
end

function dbg!(conf::SSLConfig, f::Ptr{Cvoid}, p)
    ccall((:mbedtls_ssl_conf_dbg, libmbedtls), Cvoid,
        (Ptr{Cvoid}, Ptr{Cvoid}, Any),
        conf.data, f, p)
end

function f_dbg(f, level, filename, number, msg)
    f(level, unsafe_string(filename), number, unsafe_string(msg))
    nothing
end

function dbg!(conf::SSLConfig, f)
    conf.dbg = f
    dbg!(conf, c_dbg[], f)
    nothing
end

@enum(DebugThreshold,
    NONE = 0,
    ERROR,
    STATE_CHANGE,
    INFO,
    VERBOSE)

function set_dbg_level(level)
    ccall((:mbedtls_debug_set_threshold, libmbedtls), Cvoid,
        (Cint,), Cint(level))
    nothing
end

function set_alpn!(conf::SSLConfig, protos)
    conf.alpn_protos = protos
    @err_check ccall((:mbedtls_ssl_conf_alpn_protocols, libmbedtls), Cint,
                     (Ptr{Cvoid}, Ptr{Ptr{Cchar}}), conf.data, protos)
    nothing
end

function alpn_proto(ctx::SSLContext)
    rv = ccall((:mbedtls_ssl_get_alpn_protocol, libmbedtls), Ptr{Cchar},
               (Ptr{Cvoid},), ctx.data)
    unsafe_string(rv)
end

# C API

macro lockdata(ctx, expr)
    esc(quote
        lock($ctx.datalock)
        @assert $ctx.datalock.reentrancy_cnt == 1
        try
            $expr
        finally
            unlock($ctx.datalock)
        end
    end)
end

function get_peer_cert(ctx::SSLContext)
    data = ccall((:mbedtls_ssl_get_peer_cert, libmbedtls), Ptr{Cvoid}, (Ptr{Cvoid},), ctx.data)
    return CRT(data)
end

function get_version(ctx::SSLContext)
    if isdefined(ctx, :config)
        data = ccall((:mbedtls_ssl_get_version, libmbedtls), Ptr{UInt8}, (Ptr{Cvoid},), ctx.data)
        return unsafe_string(data)
    else
        throw(ArgumentError("`ctx` hasn't been initialized with an MbedTLS.SSLConfig; run `MbedTLS.setup!(ctx, conf)`"))
    end
end

function get_ciphersuite(ctx::SSLContext)
    data = ccall((:mbedtls_ssl_get_ciphersuite, libmbedtls), Ptr{UInt8}, (Ptr{Cvoid},), ctx.data)
    return unsafe_string(data)
end

function ssl_setup(ctx::SSLContext, conf::SSLConfig)
    @lockdata ctx begin
        @err_check ccall((:mbedtls_ssl_setup, libmbedtls), Cint,
            (Ptr{Cvoid}, Ptr{Cvoid}),
            ctx.data, conf.data)
    end
end

function ssl_handshake(ctx::SSLContext)
    n = @lockdata ctx begin
        ccall((:mbedtls_ssl_handshake, libmbedtls), Cint,
              (Ptr{Cvoid},), ctx.data)
    end
end

"""
Return the number of application data bytes remaining to be read
from the current record.

https://tls.mbed.org/api/ssl_8h.html#ad43142085f3182e9b0dc967ec582032b:
"""
function ssl_get_bytes_avail(ctx::SSLContext)::Int
    @lockdata ctx begin
        return ccall((:mbedtls_ssl_get_bytes_avail, libmbedtls),
                     Csize_t, (Ptr{Cvoid},), ctx.data)
    end
end

"""
    ssl_check_pending(::SSLContext)::Bool

Check if there is data already read from the underlying transport
but not yet processed.

If the SSL/TLS module successfully returns from an operation - e.g.
a handshake or an application record read - and you're awaiting
incoming data next, you must not immediately idle on the underlying
transport to have data ready, but you need to check the value of
this function first.  The reason is that the desired data might
already be read but not yet processed.  If, in contrast, a previous
call to the SSL/TLS module returned MBEDTLS_ERR_SSL_WANT_READ, it
is not necessary to call this function, as the latter error code
entails that all internal data has been processed.

https://tls.mbed.org/api/ssl_8h.html#a4075f7de9877fd667bcfa2e819e33426
"""
function ssl_check_pending(ctx::SSLContext)::Bool
    @lockdata ctx begin
	return ccall((:mbedtls_ssl_check_pending, libmbedtls),
		     Cint, (Ptr{Cvoid},), ctx.data) > 0
    end
end

function ssl_set_bio(ctx::SSLContext, bio, f_send::Ptr{Cvoid}, f_recv::Ptr{Cvoid})
    @lockdata ctx begin
        ccall((:mbedtls_ssl_set_bio, libmbedtls), Cvoid,
            (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
            ctx.data, bio, f_send, f_recv, C_NULL)
    end
end

"""
    ssl_read(::SSLContext, ptr, n)

Returns One of the following:
0 if the read end of the underlying transport was closed,
the (positive) number of bytes read, or
a negative error code on failure.

If MBEDTLS_ERR_SSL_WANT_READ is returned, no application data is
available from the underlying transport. In this case, the function
needs to be called again at some later stage.

If this function returns something other than a positive value or
MBEDTLS_ERR_SSL_WANT_READ/WRITE or MBEDTLS_ERR_SSL_CLIENT_RECONNECT,
you must stop using the SSL context for reading or writing, and
either free it or call mbedtls_ssl_session_reset() on it before
re-using it for a new connection; the current connection must be
closed.

https://tls.mbed.org/api/ssl_8h.html#aa2c29eeb1deaf5ad9f01a7515006ede5
"""
function ssl_read(ctx::SSLContext, ptr, n)::Int
    @lockdata ctx begin
        return ccall((:mbedtls_ssl_read, libmbedtls), Cint,
                     (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t),
                     ctx.data, ptr, n)
    end
end

"""
    ssl_write(::SSLContext, ptr, n)

When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must
be called later with the same arguments, until it returns a value
greater that or equal to 0. When the function returns
MBEDTLS_ERR_SSL_WANT_WRITE there may be some partial data in the
output buffer, however this is not yet sent.

If this function returns something other than 0, a positive value
or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using the SSL
context for reading or writing, and either free it or call
mbedtls_ssl_session_reset() on it before re-using it for a new
connection; the current connection must be closed.

https://tls.mbed.org/api/ssl_8h.html#a5bbda87d484de82df730758b475f32e5
"""
function ssl_write(ctx::SSLContext, ptr, n)::Int
    @lockdata ctx begin
        return ccall((:mbedtls_ssl_write, libmbedtls), Cint,
                     (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t),
                     ctx.data, ptr, n)
    end
end

"""
    ssl_close_notify(::SSLContext)

Notify the peer that the connection is being closed.
0 if successful, or a specific SSL error code.

If this function returns something other than 0 or
MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using the SSL context
for reading or writing, and either free it or call
mbedtls_ssl_session_reset() on it before re-using it for a new
connection; the current connection must be closed.

https://tls.mbed.org/api/ssl_8h.html#ac2c1b17128ead2df3082e27b603deb4c
"""
function ssl_close_notify(ctx::SSLContext)
    @lockdata ctx begin
        return ccall((:mbedtls_ssl_close_notify, libmbedtls),
                     Cint, (Ptr{Cvoid},), ctx.data)
    end
end

"""
Reset an already initialized SSL context for re-use while retaining
application-set variables, function pointers and data.
"""
function ssl_session_reset(ctx::SSLContext)
    @lockdata ctx begin
        return ccall((:mbedtls_ssl_session_reset, libmbedtls),
                     Cint, (Ptr{Cvoid},), ctx.data)
    end
end

function hostname!(ctx::SSLContext, hostname)
    @err_check ccall((:mbedtls_ssl_set_hostname, libmbedtls), Cint,
      (Ptr{Cvoid}, Cstring), ctx.data, hostname)
end

const c_send = Ref{Ptr{Cvoid}}(C_NULL)
const c_recv = Ref{Ptr{Cvoid}}(C_NULL)
const c_dbg = Ref{Ptr{Cvoid}}(C_NULL)
const DEFAULT_CERT = read(joinpath(@__DIR__, "cacert.pem"), String)

function __sslinit__()
    c_send[] = @cfunction(f_send, Cint, (Ptr{Cvoid}, Ptr{UInt8}, Csize_t))
    c_recv[] = @cfunction(f_recv, Cint, (Ptr{Cvoid}, Ptr{UInt8}, Csize_t))
    c_dbg[] = @cfunction(f_dbg, Cvoid, (Any, Cint, Ptr{UInt8}, Cint, Ptr{UInt8}))
end


text:
# Many derivative free methods of different orders

##################################################
## Helpers for the various methods
## issue with approx derivative
isissue(x) = (x == 0.0) || isnan(x) || isinf(x)

"""
heuristic to get a decent first step with Steffensen steps
"""
function steff_step(x::T, fx) where {T}
    thresh =  max(1, norm(x)) * sqrt(eps(T)) # max(1, sqrt(abs(x/fx))) * 1e-6
    norm(fx) <= thresh ? fx : sign(fx) * thresh
end

function guarded_secant_step(alpha::T, beta::T, falpha, fbeta) where {T <: AbstractFloat}

    fp = (fbeta - falpha) /  (beta - alpha)
    Δ::T = fbeta / fp
    ## odd, we get allocations if we define Delta, then beta - Delta
    ## Δ = beta - fbeta * (beta - alpha) / (fbeta - falpha)

    if isissue(Δ)
        Δ = one(T)/1000
    elseif norm(Δ) >= 100 * norm(alpha - beta) # guard runaway
        Δ = sign(Δ) * 100 * min(one(T), norm(alpha - beta))
    end

    if isissue(Δ)
        return (alpha + (beta - alpha)*(0.5), true) # midpoint
    else
        return (beta - Δ, false)
    end
end

## Different functions for approximating f'(xn)
## return fpxn and whether it is an issue

## use f[a,b] to approximate f'(x)
function _fbracket(a, b, fa, fb)
    num, den = fb - fa, b - a
    num == 0 && den == 0 && return Inf, true
    out = num / den
    out, isissue(out)
end

## use f[y,z] - f[x,y] + f[x,z] to approximate
function _fbracket_diff(a,b,c, fa, fb, fc)
    x1, _ = _fbracket(b, c, fb,  fc)
    x2, _ = _fbracket(a, b, fa,  fb)
    x3, _ = _fbracket(a, c, fa,  fc)
    out = x1 - x2 + x3
    out, isissue(out)
end

## use f[a,b] * f[a,c] / f[b,c]
function _fbracket_ratio(a, b, c, fa, fb, fc)
    x1, _ = _fbracket(a, b, fa, fb)
    x2, _ = _fbracket(a, c, fa, fc)
    x3, _ = _fbracket(b, c, fb, fc)
    out = (x2 * x3) / x3
    out, isissue(out)
end

##################################################

## Order0 and Secant are related
"""
    Order0()

The `Order0` method is engineered to be a more robust, though possibly
slower, alternative to to the other derivative-free root-finding
methods. The implementation roughly follows the algorithm described in
*Personal Calculator Has Key to Solve Any Equation f(x) = 0*, the
SOLVE button from the
[HP-34C](http://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1979-12.pdf).
The basic idea is to use a secant step. If along the way a bracket is
found, switch to bisection, using `Bisection` if possible, else
`A42`.  If the secant step fails to decrease the function value, a
quadratic step is used up to 3 times.

"""
mutable struct Order0 <: AbstractSecant end

"""
    Order1()

The `Order1()` method is an alias for `Secant` and performs a secant
step. This method keeps two values in its state, `x_n` and `x_n1`. The
updated point is the intersection point of x axis with the secant line
formed from the two points. The secant method uses 1 function
evaluation and has order `(1+sqrt(5))/2`.

"""
mutable struct Secant <: AbstractSecant end
const Order1 = Secant

function init_state(method::AbstractSecant, fs, x::Union{T, Vector{T}}, bracket) where {T <: AbstractFloat}

    if isa(x, Vector)
        x0, x1 = x[1:2]
        #        fx0, fx1 = fs.f(x0), fs.f(x1)
        fx0, fx1 = fs(x0), fs(x1)
    else
        x0 = float(x)
        #        fx0 = fs.f(x0)
        fx0 = fs(x0)
        stepsize = max(1/100, min(abs(fx0), abs(x0/100)))
        x1 = x0 + stepsize
#        x0, x1, fx0, fx1  = x1, x0, fs.f(x1), fx0 # switch
        x0, x1, fx0, fx1  = x1, x0, fs(x1), fx0 # switch
    end

    state = UnivariateZeroStateBase(
                                    promote(float(x1), float(x0))...,
                                    promote(fx1, fx0)...,
                                    bracket,
                                    0, 2,
                                    false, false, false, false,
                                    "")
    state
end

##################################################

## in Order0, we run bisection if a bracketing interval is found
function _run_bisection(fs, o, options)
    verbose = options.verbose; options.verbose=false # turn off verbose
    find_zero(Bisection(), fs, o, options)
    options.verbose = verbose
    o.message = "Used bisection for last step"
end

## order 0
# goal: more robust to initial guess than higher order methods
# follows roughly algorithm described http://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1979-12.pdf, the SOLVE button from the HP-34C
# though some modifications were made.
# * use secant step
# * if along the way a bracket is found, switch to bisection. (We use float64 bisection not a42 if available)
# * if secant step fails to decrease, we use quadratic step up to 3 times
#
# Goal is to return a value `x` with either:
# * `f(x) == 0.0` or
# * `f(prevfloat(x)) * f(nextfloat(x)) < 0`.
# if a bracket is found that can be done, otherwise secant step is used
function update_state(method::Order0, fs, o::UnivariateZeroState{T}, options::UnivariateZeroOptions) where {T}

    f = fs.f
    alpha, beta = o.xn0, o.xn1
    falpha, fbeta = o.fxn0, o.fxn1

    incsteps(o)

    if sign(falpha) * sign(fbeta) < 0.0
        _run_bisection(fs, o, options)
        return nothing
    end

    gamma, issue = guarded_secant_step(alpha, beta, falpha, fbeta)

    fgamma = f(gamma); incfn(o)

    if sign(fgamma)*sign(fbeta) < 0.0
        o.xn0, o.xn1 = gamma, beta
        o.fxn0, o.fxn1 = fgamma, fbeta
        _run_bisection(fs, o, options)
        return nothing
    end

    if norm(fgamma) <= norm(fbeta)
        o.xn0, o.xn1 = beta, gamma
        o.fxn0, o.fxn1 = fbeta, fgamma
        return nothing
    end

    ctr = 0
    while true
        ## quadratic step
        ctr += 1
        if ctr >= 3
            o.stopped = true
            o.message = "dithering, algorithm failed to improve using quadratic steps"
            return nothing
        end

        # quadratic_step. Put new gamma at vertex of parabola through alpha, beta, (old) gamma
        denom = (beta - alpha) * (fbeta - fgamma)  - (beta - fgamma) * (fbeta - falpha)
        if isissue(denom)
            o.stopped
            o.message = "dithering, algorithm failed to improve using quadratic steps"
            return nothing
        end
        gamma = beta -  ((beta - alpha)^2 * (fbeta - fgamma) - (beta - gamma)^2 * (fbeta - falpha))/denom/2

        fgamma = f(gamma); incfn(o)
        incfn(o)

        if norm(fgamma) < norm(fbeta)
            o.xn0, o.xn1 = beta, gamma
            o.fxn0, o.fxn1 = fbeta, fgamma
            return nothing
        end

        theta, issue = guarded_secant_step(beta, gamma, fbeta, fgamma)

        ftheta = f(theta); incfn(o)

        if sign(ftheta) * sign(fbeta) < 0
            o.xn0, o.xn1 = beta, theta
            o.fxn0, o.fxn1 = fbeta, ftheta

            _run_bisection(fs, o, options)
            return nothing
        end
    end

    # failed to improve
    o.stopped = true
    o.message = "failure to improve"
    return nothing
end

##################################################

## Secant
## https://en.wikipedia.org/wiki/Secant_method
function update_state(method::Secant, fs, o::UnivariateZeroState{T}, options::UnivariateZeroOptions{T}) where {T <: AbstractFloat}

    incsteps(o)

    fp, issue = _fbracket(o.xn0, o.xn1, o.fxn0, o.fxn1)
    if issue
        o.stopped = true
        o.message = "Derivative approximation had issues"
        return
    end

    o.xn0 = o.xn1
    o.fxn0 = o.fxn1

    o.xn1 = o.xn1 -  o.fxn1 / fp
    #    o.fxn1 = fs.f(o.xn1)
    o.fxn1 = fs(o.xn1)
    incfn(o)

    nothing
end

"""

    secant_method(f, x0, x1; [kwargs...])

Solve for zero of `f(x) = 0` using the secant method.

Not exported.  Use `find_zero` with `Order1()`.
"""
secant_method(f, x0::Real, x1::Real; kwargs...) = find_zero(f, map(float, [x0,x1]), Order1(); kwargs...)

##################################################

### Steffensen
## https://en.wikipedia.org/wiki/Steffensen's_method#Simple_description
mutable struct Steffensen <: UnivariateZeroMethod
end

"""
    Order2()

The quadratically converging
[Steffensen](https://en.wikipedia.org/wiki/Steffensen's_method#Simple_description)
method is used for the derivative free `Order2()` algorithm. Unlike
the quadratically converging Newton's method, no derivative is
necessary, though like Newton's method, two function calls per step
are. This algorithm is more sensitive than Newton's method to poor
initial guesses.

"""
const Order2 = Steffensen

function update_state(method::Steffensen, fs, o::UnivariateZeroState{T}, options::UnivariateZeroOptions{T}) where {T <: AbstractFloat}

    S = eltype(o.fxn1)

    incsteps(o)

    wn = o.xn1 + steff_step(o.xn1, o.fxn1)::T
    fwn = fs(wn)::S
    incfn(o)

    fp, issue = _fbracket(o.xn1, wn, o.fxn1, fwn)

    if issue
        o.stopped = true
        o.message = "Derivative approximation had issues"
        return
    end

    o.xn0 = o.xn1
    o.fxn0 = o.fxn1
    o.xn1 = o.xn1 - o.fxn1 / fp #xn1
    o.fxn1 = fs(o.xn1)
    incfn(o)

    nothing
end

steffenson(f, x0; kwargs...) = find_zero(f, x0, Steffensen(); kwargs...)

##################################################

"""
    Order5()

Implements an algorithm
from *A New Fifth Order Derivative Free Newton-Type Method for Solving Nonlinear Equations*
by Manoj Kumar, Akhilesh Kumar Singh, and Akanksha,
Appl. Math. Inf. Sci. 9, No. 3, 1507-1513 (2015). Four function calls per step are needed.

"""
mutable struct Order5 <: UnivariateZeroMethod end

function update_state(method::Order5, fs, o::UnivariateZeroState{T}, options::UnivariateZeroOptions) where {T}

    xn = o.xn1
    fxn = o.fxn1
    S = eltype(o.fxn1)

    incsteps(o)

    wn::T = o.xn1 + steff_step(o.xn1, o.fxn1)
    fwn = fs(wn)::S
    incfn(o)

    fp, issue = _fbracket(o.xn1, wn, o.fxn1, fwn)
    if issue
        o.xn0, o.xn1 = o.xn1, wn
        o.fxn0, o.fxn1 = o.fxn1, fwn
        o.message = "Issue with divided difference f[xn, wn]"
        o.stopped  = true
        return
    end

    yn::T = o.xn1 - o.fxn1 / fp
    fyn = fs(yn)::S
    incfn(o)

    zn::T = xn - (fxn + fyn) / fp
    fzn = fs(zn)::S
    incfn(o)

    fp, issue = _fbracket_ratio(yn, o.xn1, wn, fyn, o.fxn1, fwn)
    if issue
        o.xn0, o.xn1 = o.xn1, yn
        o.fxn0, o.fxn1 = o.fxn1, fyn
        o.message = "Issue with f[xn,yn]*f[yn,wn] / f[xn, wn]"
        o.stopped = true
        return
    end

    o.xn0 = o.xn1
    o.fxn0 = o.fxn1
    o.xn1 = zn  - fzn  / fp
    o.fxn1 = fs(o.xn1)
    incfn(o)

    nothing
end

## If we have a derivative
function update_state(method::Order5, fs::FirstDerivative, o::UnivariateZeroState{T}, options::UnivariateZeroOptions) where {T}

    xn, fxn = o.xn1, o.fxn1
    S = eltype(fxn)

    incsteps(o)

    fpxn = fs.fp(xn)
    incfn(o)

    if isissue(fpxn)
        o.stopped  = true
        return
    end

    yn = xn - fxn / fpxn
    fyn, fpyn = fs.f(yn), fs.fp(yn)
    incfn(o, 2)

    if isissue(fpyn)
        o.xn0, o.xn1 = xn, yn
        o.fxn0, o.fxn1 = fxn, fyn
        o.stopped  = true
        return
    end

    zn = xn  - (fxn + fyn) / fpxn
    fzn = fs.f(zn)
    incfn(o, 1)

    xn1 = zn - fzn / fpyn
    fxn1 = fs.f(xn1)
    incfn(o, 1)

    o.xn0, o.xn1 = xn, xn1
    o.fxn0, o.fxn1 = fxn, fxn1

    nothing
end

##################################################

"""
    Order8()

Implements an algorithm from
*New Eighth-Order Derivative-Free Methods for Solving Nonlinear Equations*
by Rajinder Thukral,
International Journal of Mathematics and Mathematical Sciences
Volume 2012 (2012), Article ID 493456, 12 pages. Four function calls per step are required.
"""
mutable struct Order8 <: UnivariateZeroMethod
end

function update_state(method::Order8, fs, o::UnivariateZeroState{T}, options::UnivariateZeroOptions) where {T}
    xn = o.xn1
    fxn = o.fxn1
    S = eltype(fxn)

    incsteps(o)

    wn::T = xn + steff_step(xn, fxn)
    fwn::S = fs(wn)
    incfn(o)

    if isissue(fwn)
        o.xn0,o.xn1 = xn, wn
        o.fxn0,o.fxn1 = fxn, fwn
        o.stopped = true
        o.message = "issue with Steffensen step fwn"
        return
    end

    fp, issue = _fbracket(xn, wn, fxn, fwn)
    issue && return (xn, true)

    if issue
        o.stopped = true
        o.message = "issue with divided difference f[xn, wn]"
        return
    end

    yn::T = xn - fxn / fp
    fyn::S = fs(yn)
    incfn(o)

    fp, issue = _fbracket(yn, xn, fyn, fxn)
    if issue #fp
        o.xn0,o.xn1 = xn, yn
        o.fxn0,o.fxn1 = fxn, fyn
        o.stopped = true
        o.message = "issue with divided difference f[xn, yn]"
        return
    end

    phi = (1 + fyn / fwn)           # pick one of options
    zn =  yn - phi * fyn / fp
    fzn::S = fs(zn)
    incfn(o)

    fp, issue =  _fbracket_diff(xn, yn, zn, fxn, fyn, fzn)
    if issue
        o.xn0,o.xn1 = xn, zn
        o.fxn0,o.fxn1 = fxn, fzn
        o.message = "issue with divided difference  f[y,z] - f[x,y] + f[x,z]"
        o.stopped = true
        return
    end

    w::T = 1 / (1 - fzn/fwn)

    xi::T = (1 - 2fyn*fyn*fyn / (fwn * fwn * fxn))

    xn1::T = zn - w * xi * fzn / fp
    fxn1::S = fs(xn1)
    incfn(o)

    o.xn0,o.xn1 = xn, xn1
    o.fxn0,o.fxn1 = fxn, fxn1

    nothing
end

##################################################

"""
    Order16()

Implement the algorithm from
*New Sixteenth-Order Derivative-Free Methods for Solving Nonlinear Equations*
by R. Thukral,
American Journal of Computational and Applied Mathematics
p-ISSN: 2165-8935;    e-ISSN: 2165-8943; 2012;  2(3): 112-118
doi: 10.5923/j.ajcam.20120203.08.

Five function calls per step are required. Though rapidly converging, this method generally isn't faster (fewer
function calls/steps) over other methods when using `Float64` values,
but may be useful for solving over `BigFloat`.
"""
mutable struct Order16 <: UnivariateZeroMethod
end

function update_state(method::Order16, fs, o::UnivariateZeroState{T}, options::UnivariateZeroOptions) where {T}
    xn = o.xn1
    fxn = o.fxn1
    S = eltype(fxn)

    incsteps(o)

    wn::T = xn + steff_step(xn, fxn)
    fwn::S = fs(wn)
    incfn(o)

    fp, issue = _fbracket(xn, wn, fxn, fwn)

    if issue
        o.xn0, o.xn1 = xn, wn
        o.fxn0, o.fxn1 = fxn, fwn
        o.message = "issue with f[xn,wn]"
        o.stopped = true
        return
    end

    yn::T = xn - fxn / fp
    fyn::S = fs(yn)
    incfn(o)

    fp, issue = _fbracket_ratio(yn, xn, wn, fyn, fxn, fwn)
    if issue
        o.xn0, o.xn1 = xn, yn
        o.fxn0, o.fxn1 = fxn, fyn
        o.message = "issue with f[xn,yn]*f[yn,wn]/f[xn,wn]"
        o.stopped = true
        return
    end

    zn::T = yn - fyn / fp
    fzn::S = fs(zn)
    incfn(o)

    fp, issue = _fbracket_diff(xn, yn, zn, fxn, fyn, fzn)
    u2, u3, u4 = fzn/fwn, fyn/fxn, fyn/fwn
    eta = 1 / (1 + 2*u3*u4^2) / (1 - u2)
    if issue
        o.xn0, o.xn1 = xn, zn
        o.fxn0, o.fxn1 = fxn, fzn
        o.stopped = true
        o.message = "Approximate derivative failed"
        return
    end

    an = zn - eta * fzn / fp
    fan = fs(an)
    incfn(o)

    fp, issue = _fbracket_ratio(an, yn, zn, fan, fyn, fzn)
    if issue
        o.xn0, o.xn1 = xn, an
        o.fxn0, o.fxn1 = fxn, fan
        o.stopped = true
        o.message = "Approximate derivative failed"
        return
    end

    u1, u5, u6 = fzn/fxn, fan/fxn, fan/fwn
    sigma =  1 + u1*u2 - u1*u3*u4^2 + u5 + u6 + u1^2*u4 +
    u2^2*u3 + 3*u1*u4^2*(u3^2 - u4^2)/_fbracket(xn,yn, fxn, fyn)[1]

    xn1 = an - sigma * fan / fp
    fxn1 = fs(xn1)
    incfn(o)

    o.xn0, o.xn1 = xn, xn1
    o.fxn0, o.fxn1 = fxn, fxn1

    nothing
end


text:
type Dependency
  name::ASCIIString
  path::ASCIIString
  has_export::Bool
end

function write_html_head(io::IO)
	print(io, """
	<link href="https://cdnjs.cloudflare.com/ajax/libs/dc/1.7.5/dc.css" rel="stylesheet">

	""")
end

function write_html_chart_entry(io::IO, chart::DCChart, indent::Int=0)
	tabbing = "  "^indent
	println(io, tabbing, "
")
	println(io, tabbing, "
", chart.title, "
")
	println(io, tabbing, "

")
	println(io, tabbing, "
")
end

function write_html_widget_entry(io::IO, widget::DCWidget, indent::Int=0)
  tabbing = "  "^indent
  println(io, tabbing, widget.html, "
")
end

function write_html_body(io::IO, dcout::DCOut)
	print(io, "

&nbsp

  ")
	for chart in dcout.charts
		write_html_chart_entry(io, chart, 1)
	end
	print(io, """

  """)
  for widget in dcout.widgets
    write_html_widget_entry(io, widget, 0)
  end
	println(io, "")
end

function write_script_dependencies(io::IO, dependencies::Vector{Dependency})
  print(io, """require.config({paths: {""")
	for i in 1 : length(dependencies)
    #=
		@printf(io, "\n", src)
    =#
    @printf(io, """ "%s": "%s" """, dependencies[i].name, dependencies[i].path)
    if i < length(dependencies)
      print(io, ",\n")
    end
	end
  print(io, """}});\n""")
end

function write_json_entry(io, names::Vector{Symbol}, values::Vector{Any})
	print(io, "{")
	for i in 1 : length(names)
		print(io, "\"", names[i], "\": ")
		v = values[i]
		if isa(v, AbstractString)
			print(io, "\"", v, "\"")
		else
			print(io, v)
		end
		if i < length(names)
			print(io, ", ")
		end
	end
	print(io, "}")
end

function write_data(io::IO, dcout::DCOut)
    df = dcout.df
	values = Array(Any, length(dcout.dims))
    colnames = map(dim->dim.name, dcout.dims)
	print(io, "data = [")
	for i in 1:nrow(df)
		for (j,dim) in enumerate(dcout.dims)
			values[j] = df[i,dim.name]
		end
		write_json_entry(io, colnames, values)
		if i < nrow(df)
			print(io, ",")
		end
	end
	println(io, "];")
end

function write_reset_script(io::IO, dcout::DCOut)
	print(io, "var charts = [")
	for i in 1 : length(dcout.charts)
		print(io, dcout.charts[i].parent)
		if i < length(dcout.charts)
			print(io, ", ")
		end
	end
	println(io, "];")
	print(io, "var chart_names = [")
	for i in 1 : length(dcout.charts)
		print(io, "\"", dcout.charts[i].parent, "\"")
		if i < length(dcout.charts)
			print(io, ", ")
		end
	end
	println(io, "];")
	print(io, """

var update_reset_buttons = function(chart) {
  var filter_in_use = false;
  for (var i = 0; i < charts.length; i++) {
    if (charts[i].filters().length > 0) {
      filter_in_use = true;
      break;
    }
  }
  var idx = charts.indexOf(chart);
  d3.select("#reset_all_btn_""", dcout.output_id,
  """").remove();
  d3.select("#heading_" + chart_names[idx]).select(".chart-reset").remove();

  if (filter_in_use) {
    d3.select("#reset_all_well_""", dcout.output_id, """")
      .append("a")
      .attr("class", "button-label")
      .attr("id", "reset_all_btn_""", dcout.output_id, """")
      .text(function(d) {
        return "Reset All";
      })
      .on("click", function() {
        for (var i = 0; i < charts.length; i++) {
          charts[i].filter(null);
        }
        dc.redrawAll();
      });

    if (chart.filters().length > 0) {
      d3.select("#heading_" + chart_names[idx])
        .append("span")
        .attr("class", "chart-reset fake-link")
        .text(function(d) {
          return "Reset";
        })
        .on("click", function(d) {
          chart.filter(null);
          dc.redrawAll();
        });
    }
  }
};

for (var i = 0; i < charts.length; i++) {
  charts[i].on('filtered', function(chart) {
    update_reset_buttons(chart);
  });
}
	""")
end

function quote_corrector() # doesn't actually do anything, just corrects quotes on sublime
	x = """
	a"c
	"""
end
function write_script(io::IO, dcout::DCOut, dependencies::Vector{Dependency})
	println(io, """")
end

function write_source_html(io::IO, dcout::DCOut)
	write_html_head(io)
	write_html_body(io, dcout)
 	# Note: dependencies must be ordered! (hence why this is not a dictionary)
	dependencies = [Dependency("d3","https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min", true),
	                Dependency("crossfilter","https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.7/crossfilter", false),
	                Dependency("dc","https://cdnjs.cloudflare.com/ajax/libs/dc/1.7.1/dc", true)]
	write_script(io, dcout, dependencies)
end

function Base.writemime(io::IO, ::MIME"text/html", dcout::DCOut)

	# If IJulia is present, go for it
	if isdefined(Main, :IJulia)
		#=
		1 - determine chart layout
		2 - generate iframe html + js page
		3 - write link to it
		=#

		iframe_width = 975 # [pix]
		row_height = 275 # [pix]x`

		ncharts = length(dcout.charts)
    widget_height_addition = 0
    for widget in dcout.widgets
      if widget.typ.concreteName == "dataCount"
        widget_height_addition += 15
      else
        widget_height_addition += 150
      end
    end
    has_widget = length(dcout.widgets) > 0
		nrows = ceil(Int, ncharts/3)
		iframe_height = nrows*275 + widget_height_addition + 15

    randomize_ids!(dcout)
    if dcout.elastic_height
      write(io, """
""")
    else
      write(io, """
""")
    end
    write_source_html(io, dcout)
    write(io, """
""")

    if dcout.html_debug
      f = open("output.html", "w")
      write(f, """

        """)
      write(f, """
""")
      write_source_html(f, dcout)
      write(f, """
""")
      close(f)
    end
	else
		# TODO
		# decide what to do in the absence of IJulia
	end
end


text:
# Begin Blocks

z = begin
  x = 1
  y = 2
  x + y # Value of Last subexpression assigned to z
end

println(z)

# Chains

z = (x = 1; y = 2; x + y)

# Combined

begin x = 1; y = 2; x + y end

max_stars_repo_name: cadamsmith/julia-programming

max_issues_repo_name: cadamsmith/julia-programming

max_forks_repo_name: cadamsmith/julia-programming


text:
function build_datastore_apicall_model(callFunction::Function, tickerSymbol::String;
    output::Symbol = :compact, datatype::Symbol = :json)::PSResult

    # TODO: Checks -

    # initialize -
    output_type_set = Set{Symbol}()
    datatype_set = Set{Symbol}()

    # check output types -
    push!(output_type_set,:compact)
    push!(output_type_set,:full)
    if (in(output,output_type_set) == false)
        return PSResult(ArgumentError("Incompatible output type. Expected {:compact,:full} but received $(string(output))"))
    end

    # check datatype -
    push!(datatype_set, :json)
    push!(datatype_set, :csv)
    if (in(datatype,datatype_set) == false)
        return PSResult(ArgumentError("Incompatible data type. Expected {:json,:csv} but received $(string(output))"))
    end

    # check the tickerSymbol -
    if (isa(tryparse(Float64,tickerSymbol), Number) == true)
        return PSResult(ArgumentError("Incompatible ticker symbol type"))
    end

    # TODO: how do we check is we have a specific function?

    # get stuff -
    parameter_object = PSDataStoreAPICallModel(callFunction, tickerSymbol; dataType=datatype, output=output)

    # return -
    return PSResult(parameter_object)
end

"""
    execute_api_call(user::PSUserModel, api::PSDataStoreAPICallModel; logger::Union{Nothing,AbstractLogger} = nothing)::PSResult

High-level convenience function to execute an data store application programming interface call.

# Arguments
"""
function execute_api_call(usermodel::PSUserModel, apimodel::PSDataStoreAPICallModel;
    logger::Union{Nothing,AbstractLogger} = nothing)::PSResult

    # get stuff from the parameters -
    tickerSymbol = apimodel.ticker
    outputsize = apimodel.outputsize
    datatype = apimodel.dataType
    apicall = apimodel.apicall

    # make the call -
    return apicall(usermodel,tickerSymbol; data_type = datatype, outputsize = outputsize, logger = logger)
end


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

using Markdown
using InteractiveUtils

# ╔═╡ 75852c20-4878-11ec-0f17-77727b908d78
using Pkg; Pkg.activate("C:\\Users\\sdani\\MakieDev")

# ╔═╡ f2f63242-e0ba-4fd6-a454-7fd0952236ad
using Revise, GLMakie

# ╔═╡ d5201580-108d-42e2-8eb5-c4489cc2ceb4
begin
	using WGLMakie, JSServe
	wgl = WGLMakie.activate!()
	Page(offline=true, exportable=true)
end

# ╔═╡ abe526e5-6d36-4ac5-876f-ebd65ff8a05f
using RPRMakie

# ╔═╡ ba3bb4b5-73b6-44b1-ab84-f57406058479
begin
	using MeshIO, FileIO, GeometryBasics

	colors = Dict(
	    "eyes" => "#000",
	    "belt" => "#000059",
	    "arm" => "#009925",
	    "leg" => "#3369E8",
	    "torso" => "#D50F25",
	    "head" => "yellow",
	    "hand" => "yellow"
	)

	origins = Dict(
	    "arm_right" => Point3f(0.1427, -6.2127, 5.7342),
	    "arm_left" => Point3f(0.1427, 6.2127, 5.7342),
	    "leg_right" => Point3f(0, -1, -8.2),
	    "leg_left" => Point3f(0, 1, -8.2),
	)

	rotation_axes = Dict(
	    "arm_right" => Vec3f(0.0000, -0.9828, 0.1848),
	    "arm_left" => Vec3f(0.0000, 0.9828, 0.1848),
	    "leg_right" => Vec3f(0, -1, 0),
	    "leg_left" => Vec3f(0, 1, 0),
	)

	function plot_part!(scene, parent, name::String)
	    m = load(assetpath("lego_figure_" * name * ".stl"))
	    color = colors[split(name, "_")[1]]
	    trans = Transformation(parent)
	    ptrans = Makie.transformation(parent)
	    origin = get(origins, name, nothing)
	    if !isnothing(origin)
	        centered = m.position .- origin
	        m = GeometryBasics.Mesh(meta(centered; normals=m.normals), faces(m))
	        translate!(trans, origin)
	    else
	        translate!(trans, -ptrans.translation[])
	    end
	    return mesh!(scene, m; color=color, transformation=trans)
	end
	function plot_lego_figure(s, floor=true)
		# Plot hierarchical mesh!
		figure = Dict()
	    # Plot hierarchical mesh!
	    figure["torso"] = plot_part!(s, s, "torso")
	        figure["head"] = plot_part!(s, figure["torso"], "head")
	            figure["eyes_mouth"] = plot_part!(s, figure["head"], "eyes_mouth")
	        figure["arm_right"] = plot_part!(s, figure["torso"], "arm_right")
	            figure["hand_right"] = plot_part!(s, figure["arm_right"], "hand_right")
	        figure["arm_left"] = plot_part!(s, figure["torso"], "arm_left")
	            figure["hand_left"] = plot_part!(s, figure["arm_left"], "hand_left")
	        figure["belt"] = plot_part!(s, figure["torso"], "belt")
	            figure["leg_right"] = plot_part!(s, figure["belt"], "leg_right")
	            figure["leg_left"] = plot_part!(s, figure["belt"], "leg_left")
		# lift the little guy up
	    translate!(figure["torso"], 0, 0, 20)
	    # add some floor
	    floor && mesh!(s, Rect3f(Vec3f(-400, -400, -2), Vec3f(800, 800, 2)), color=:white)
		return figure
	end
end

# ╔═╡ 87021c46-5fcd-4995-80fc-bb49fd194978
gl = GLMakie.activate!(); scene = Scene(;
    # clear everything behind scene
    clear = true,
    # the camera struct of the scene.
    visible = true,
    ssao = Makie.SSAO(),
    # Creates lights from theme, which right now defaults to `
	# set_theme!(lightposition=:eyeposition, ambient=RGBf(0.5, 0.5, 0.5))`
    lights = Makie.automatic,
    backgroundcolor = :gray,
	resolution = (500, 500)
)

# ╔═╡ 09fcfad7-0c3b-4910-b6ad-ba8e2f6b24f2
md"""
By default, the scenes goes from -1 to 1.
So to draw a rectangle outlining the screen, the following rectangle does the job:
"""

# ╔═╡ 98c046a9-9a4e-4dc2-ad5a-205805064a03
lines!(scene, Rect2f(-1, -1, 2, 2), linewidth=5, color=:black); scene

# ╔═╡ 8bae338c-1041-4cda-be49-2328f0d01d9c
md"""
this is, because the projection matrix and view matrix are the identity matrix by default, and Makie's default unit space is what's called `Clip space` in the OpenGL world
"""

# ╔═╡ 5bca7128-10fa-4674-b20d-a4ebdcbf6ec6
cam = camera(scene) # this is how to access the scenes camera

# ╔═╡ 4b05f6ee-ffe3-4658-87d0-4930035ee7d9
md"""
One can change the mapping, to e.g. draw from -3 to 5:
"""

# ╔═╡ f3842cab-564b-42ca-a7a2-cbe4a8991f86
cam.projection[] = Makie.orthographicprojection(-3f0, 5f0, -3f0, 5f0, -100f0, 100f0); scene

# ╔═╡ fc346f84-9d87-4f40-9bbe-ea1592db66bb
md"""
one can also change the camera to a perspective 3d projection:
"""

# ╔═╡ 20f97b0c-b579-4bdb-82e9-a0976f015a74
begin
	w, h = size(scene)
	cam.projection[] = Makie.perspectiveprojection(45f0, Float32(w / h), 0.1f0, 100f0)
	# Now, we also need to change the view matrix
	# to "put" the camera into some place.
	cam.view[] = Makie.lookat(Vec3f(10), Vec3f(0), Vec3f(0, 0, 1))
	scene
end

# ╔═╡ 68b06965-c4a9-4324-8ddd-1f30a1e4cf11
md"""
These are all camera transformations of the object.
In contrast, there are also scene transformations, or commonly referred to as world transformations.
To learn more about the different spaces, [learn opengl](https://learnopengl.com/Getting-started/Coordinate-Systems) offers some pretty nice explanations

The "world" transformations are implemented via the `Transformation` struct in Makie. Scenes and plots both contain these, so these types are considered as "Makie.Transformable".
The transformation of a scene will get inherited by all plots added to the scene.
An easy way to manipulate any `Transformable` is via these 3 functions:
{{doc translate!}}
{{doc rotate!}}
{{doc scale!}}
"""

# ╔═╡ e56bea12-6c1f-4057-88b5-a1557396854c
begin
	sphere_plot = mesh!(scene, Sphere(Point3f(0), 0.5), color=:red)
	scale!(scene, 2, 2, 2)
	rotate!(scene, Vec3f(1, 0, 0), 0.5) # 0.5 rad around the y axis
	scene
end

# ╔═╡ 8d18a5fe-ca4a-4bc7-a547-f816d634e2db
md"""
One can also transform the plot objects directly, which then adds the transformation from the plot object on top of the transformation from the scene.
One can add subscenes and interact with those dynamically.
Makie offers here what's usually referred to as a scene graph.
"""

# ╔═╡ 82e9cdd2-aa5e-451b-8af4-d181acaf9910
translate!(sphere_plot, Vec3f(0, 0, 1)); scene

# ╔═╡ e53c8b75-a44a-459d-a5f6-975dd9cc9c1b
md"""
The scene graph can be used to create rigid transformations, like for a robot arm:
"""

# ╔═╡ 7015a26c-f5a7-4e86-9c56-58432dbb0b44
begin
	gl
	parent = Scene()
	cam3d!(parent)
	camc = cameracontrols(parent)

	update_cam!(parent, camc, Vec3f(0, 8, 0), Vec3f(4.0, 0, 0))
	s1 = Scene(parent, camera=parent.camera)
	mesh!(s1, Rect3f(Vec3f(0, -0.1, -0.1), Vec3f(5, 0.2, 0.2)))
	s2 = Scene(s1, camera=parent.camera)
	mesh!(s2, Rect3f(Vec3f(0, -0.1, -0.1), Vec3f(5, 0.2, 0.2)), color=:red)
	translate!(s2, 5, 0, 0)
	s3 = Scene(s2, camera=parent.camera)
	mesh!(s3, Rect3f(Vec3f(-0.2), Vec3f(0.4)), color=:blue)
	translate!(s3, 5, 0, 0)
	parent
end

# ╔═╡ 032eac09-a05c-43f6-af93-4b268957d6d5
begin
	# Now, rotate the "joints"
	rotate!(s2, Vec3f(0, 1, 0), 0.5)
	rotate!(s3, Vec3f(1, 0, 0), 0.5)
	parent
end

# ╔═╡ 045c8539-6e68-40ec-bf93-889ba481e374
md"""
With this basic principle, we can even bring robots to life :)
[Kevin Moerman](https://github.com/Kevin-Mattheus-Moerman) was so nice to supply a Lego mesh, which we're going to animate!
When the scene graph is really just about a transformation Graph, one can use the Transformation struct directly, which is what we're going to do here.
This is more efficient and easier than creating a scene for each model.
"""

# ╔═╡ fa2373db-edd0-42fa-9256-492061a35190
App() do session
	wgl
	s = Scene(resolution=(500, 500))
    cam3d!(s)
	figure = plot_lego_figure(s, false)
	bodies = [
		"arm_left", "arm_right",
		"leg_left", "leg_right"]
	sliders = map(bodies) do name
		slider = if occursin("arm", name)
			JSServe.Slider(-60:4:60)
		else
			JSServe.Slider(-30:4:30)
		end
        rotvec = rotation_axes[name]
		bodymesh = figure[name]
        on(slider) do val
            rotate!(bodymesh, rotvec, deg2rad(val))
        end
        DOM.div(name, slider)
    end
	center!(s)
	JSServe.record_states(session, DOM.div(sliders..., s))
end

# ╔═╡ 3495dfd6-4a10-4178-9136-dd10a4d055b5
let
	RPRMakie.activate!(iterations=20)
	radiance = 50000
    lights = [
        # EnvironmentLight(1.5, rotl90(load(assetpath("sunflowers_1k.hdr"))')),
        PointLight(Vec3f(50, 0, 200), RGBf(radiance, radiance, radiance*1.1)),
    ]
    s = Scene(lights=lights, resolution=(500, 500))
    cam3d!(s)
    c = cameracontrols(s)
    c.near[] = 5
    c.far[] = 1000
    update_cam!(s, c, Vec3f(100, 30, 80), Vec3f(0, 0, -10))
	figure = plot_lego_figure(s)

    rot_joints_by = 0.25*pi
    total_translation = 50
    animation_strides = 10

    a1 = LinRange(0, rot_joints_by, animation_strides)
    angles = [a1; reverse(a1[1:end-1]); -a1[2:end]; reverse(-a1[1:end-1]);]
    nsteps = length(angles); #Number of animation steps
    translations = LinRange(0, total_translation, nsteps)
    Makie.Record(s, zip(translations[1:4], angles[1:4])) do (translation, angle)
        #Rotate right arm+hand
        for name in ["arm_left", "arm_right",
                			 "leg_left", "leg_right"]
            rotate!(figure[name], rotation_axes[name], angle)
        end
        translate!(figure["torso"], translation, 0, 20)
    end
end

# ╔═╡ 01bc92cf-6943-48be-a373-3cd328660bed

# ╔═╡ Cell order:
# ╠═75852c20-4878-11ec-0f17-77727b908d78
# ╠═f2f63242-e0ba-4fd6-a454-7fd0952236ad
# ╠═87021c46-5fcd-4995-80fc-bb49fd194978
# ╟─09fcfad7-0c3b-4910-b6ad-ba8e2f6b24f2
# ╠═98c046a9-9a4e-4dc2-ad5a-205805064a03
# ╟─8bae338c-1041-4cda-be49-2328f0d01d9c
# ╠═5bca7128-10fa-4674-b20d-a4ebdcbf6ec6
# ╟─4b05f6ee-ffe3-4658-87d0-4930035ee7d9
# ╠═f3842cab-564b-42ca-a7a2-cbe4a8991f86
# ╟─fc346f84-9d87-4f40-9bbe-ea1592db66bb
# ╠═20f97b0c-b579-4bdb-82e9-a0976f015a74
# ╟─68b06965-c4a9-4324-8ddd-1f30a1e4cf11
# ╠═e56bea12-6c1f-4057-88b5-a1557396854c
# ╟─8d18a5fe-ca4a-4bc7-a547-f816d634e2db
# ╠═82e9cdd2-aa5e-451b-8af4-d181acaf9910
# ╟─e53c8b75-a44a-459d-a5f6-975dd9cc9c1b
# ╠═7015a26c-f5a7-4e86-9c56-58432dbb0b44
# ╠═032eac09-a05c-43f6-af93-4b268957d6d5
# ╟─045c8539-6e68-40ec-bf93-889ba481e374
# ╠═ba3bb4b5-73b6-44b1-ab84-f57406058479
# ╠═d5201580-108d-42e2-8eb5-c4489cc2ceb4
# ╠═fa2373db-edd0-42fa-9256-492061a35190
# ╠═abe526e5-6d36-4ac5-876f-ebd65ff8a05f
# ╠═3495dfd6-4a10-4178-9136-dd10a4d055b5
# ╠═01bc92cf-6943-48be-a373-3cd328660bed


text:
# TODO: make this immutable, possibly by changing the mechanism to build
# the `BasisSet`

"""
Contracted Gaussian basis function.
```
CGBF = sum_{i=Ncontr} coefs[i]*PGBF(expn, center, power)
```
"""
mutable struct CGBF
    center::Tuple3F64
    power::Tuple3I64
    pgbfs::Array{PGBF,1}
    coefs::Array{Float64,1}
    NORM::Float64
end

import Base.println
function println( bf::CGBF )
    Ncontr = length(bf.pgbfs)
    @printf("\nContracted Gaussian basis function\n")
    @printf("\nNcontr = %d\n\n", Ncontr)
    for i = 1:Ncontr
        @printf("Coef = %18.10f", bf.coefs[i])
        println(bf.pgbfs[i])
    end
end

function init_CGBF( center::Tuple3F64, power::Tuple3I64 )
    return CGBF( center, power, PGBF[], Float64[], 1.0 )
end

init_CGBF(x=0,y=0,z=0,I=0,J=0,K=0) = CGBF( (x,y,z), (I,J,K), PGBF[], Float64[], 1.0 )

function evaluate(bf::CGBF,x::Float64,y::Float64,z::Float64)
    s = 0
    for (c,pbf) in primitives(bf)
        s += c*evaluate(pbf,x,y,z)
    end
    return bf.NORM*s
end

function normalize!(bf::CGBF)
    bf.NORM /= sqrt(overlap(bf,bf))
end

primitives(a::CGBF) = zip(a.coefs,a.pgbfs)

# This should become the constructor along with center and power
import Base: push!
function push!(cbf::CGBF, expn, coef)
    Base.push!(cbf.pgbfs, PGBF(expn, cbf.center, cbf.power))
    Base.push!(cbf.coefs, coef)
    normalize!(cbf)
end

function contract(f, a::CGBF, b::CGBF)
    s = 0.0
    for (ca,abf) in primitives(a)
    for (cb,bbf) in primitives(b)
        s += ca*cb*f(abf,bbf)
    end
    end
    return a.NORM*b.NORM*s
end

function contract(f,a::CGBF,b::CGBF,c::CGBF,d::CGBF)
    s = 0
    for (ca,abf) in primitives(a)
    for (cb,bbf) in primitives(b)
    for (cc,cbf) in primitives(c)
    for (cd,dbf) in primitives(d)
        s += ca*cb*cc*cd*f(abf,bbf,cbf,dbf)
    end
    end
    end
    end
    return a.NORM*b.NORM*c.NORM*d.NORM*s
end


text:
using SymWoodburyMatrices
using Base.Test

n = 10; k = 3

A = SymWoodbury(Diag(rand(n)), randn(n,k), randn(k,k))
x = randn(n,1)

@test norm(A*x - full(A)*x) < 1e-10
@test norm(inv(A)*x - inv(full(A))*x) < 1e-10

A = SymWoodbury(Diag(rand(n)), sparse(randn(n,k)), sparse(randn(k,k)))
x = randn(n,1)

@test norm(A*x - full(A)*x) < 1e-10
@test norm(inv(A)*x - inv(full(full(A)))*x) < 1e-10

A = SymWoodbury(Diag(rand(n)), sparse(randn(n,k)), randn(k,k))
x = randn(n,1)

@test norm(A*x - full(A)*x) < 1e-10
@test norm(inv(A)*x - inv(full(full(A)))*x) < 1e-10

A = SymWoodbury(eye(n), sparse(randn(n,k)), randn(k,k))
x = randn(n,1)

@test norm(A*x - full(A)*x) < 1e-10
@test norm(inv(A)*x - inv(full(full(A)))*x) < 1e-10
@test norm(liftFactor(A)(x) - inv(full(full(A)))*x) < 1e-10

x = randn(n)
o = copy(x)


text:
#
# OIFITS.jl --
#
# Support for OI-FITS (optical interferometry data format) in Julia.
#
#------------------------------------------------------------------------------
#
# This file is part of OIFITS.jl which is licensed under the MIT "Expat"
# License:
#
# Copyright (C) 2015: Éric Thiébaut.
#
#------------------------------------------------------------------------------

module OIFITS

import Base: getindex, setindex!, haskey, keys, start, done, next, show

include("oidata.jl")
include("fix-fitsio.jl")
include("misc.jl")
include("oifile.jl")
include("oiformat1.jl")
include("oipost.jl") # must be *after* oifile.jl and all oiformat*.jl
include("deprecations.jl")

end # module


text:
using TensorBoardLogger
using Test

@testset "Scalar Value Logger" begin
    logger = Logger("log/")
    @test isdir("log/")
    step = 1
    log_value(logger, "float32", 1.25f0, step=step)
    log_value(logger, "float64", 1.5, step=step)
    log_value(logger, "irr", pi, step=step)
    log_value(logger, "complex", 1.0 + 1.0im, step=step)

    log_value(logger, "float32", 1.25f0, step)
    log_value(logger, "float64", 1.5, step)
    log_value(logger, "irr", pi, step)
    log_value(logger, "complex", 1.0 + 1.0im, step)
end

@testset "Histogram Value Logger" begin
    logger = Logger("log/")
    @test isdir("log/")
    step = 1

    x0 = 0.5+step/30; s0 = 0.5/(step/20);
    edges = collect(-5:0.1:5)
    centers = collect(edges[1:end-1] .+0.05)
    histvals = [exp(-((c-x0)/s0)^2) for c=centers]
    data_tuple = (edges, histvals)

    log_histogram(logger, "hist/cust", data_tuple, step=step)
    log_histogram(logger, "hist/cust", rand(100), step=step)
end


text:
"""
---------------------------------------------------
Types and Constructors
---------------------------------------------------
"""

mutable struct HIGH_RECONNAISSANCE_SAMPLER <: SAMPLER
	z::Sampleable
	vx::Sampleable
	radius::Sampleable
	arc_length::Sampleable
end

function high_reconnaissance_sampler(;z = Normal(500,10),
						vx = Uniform(30,45),
						radius = Uniform(200,400),
						arc_length = Uniform(90,270))
	return HIGH_RECONNAISSANCE_SAMPLER(z, vx, radius, arc_length)
end

mutable struct HIGH_RECONNAISSANCE <: UAM_TRAJECTORY
	z::Float64
	vx::Float64
	radius::Float64
	arc_length::Float64
	sampler::SAMPLER
	dt::Float64
	vz_max::Float64
	az_max::Float64
	ax_max::Float64
	p::Array{Float64,2}
	v::Array{Float64,2}
end

function high_reconnaissance(;z = 0.0,
						vx = 0.0,
						radius = 0.0,
						arc_length = 0.0,
						sampler = high_reconnaissance_sampler(),
						dt = 1.0,
						vz_max = 550fpm2fps, # This doesn't actually get used
						az_max = 0.3g_ft, # This doesn't actually get used
						ax_max = 0.1g, # This doesn't actually get used
						p = zeros(1,3),
						v = zeros(1,3))
	return HIGH_RECONNAISSANCE(z, vx, radius, arc_length, sampler, dt,
								vz_max, az_max, ax_max, p, v)
end

"""
---------------------------------------------------
Functions
---------------------------------------------------
"""

function solve_trajectory!(τ::HIGH_RECONNAISSANCE)
	# Get the circular portion first
	θ̇ = rad2deg(τ.vx/τ.radius)
	arc_time = τ.arc_length/θ̇
	arc_steps = convert(Int64, floor(arc_time/τ.dt))
	θ = zeros(arc_steps)
	for i = 2:arc_steps
		θ[i] = θ[i-1] + θ̇*τ.dt
	end

	x = τ.radius*cosd.(θ)
	y = τ.radius*sind.(θ)
	z = τ.z*ones(arc_steps)

	p = hcat(x, y, z)

	N = size(p,1)
	D = zeros(N-1,N)
	for i = 1:(N-1)
	    D[i,i] = -1
	    D[i,i+1] = 1
	end
	D = D./τ.dt

	v = D*p
	v = vcat(v, v[end,:]')

	τ.p = copy(p)
	τ.v = copy(v)

	return true # Because others return if they are optimal
end

max_stars_repo_name: Airspace-Encounter-Models/em-model-uam

max_issues_repo_name: Airspace-Encounter-Models/em-model-uam

max_forks_repo_name: Airspace-Encounter-Models/em-model-uam


text:
"""
when Flux is used, include this file will automatically add functor to existing
kernels in Stheno.
"""

using .Flux
using .Flux: @functor

kernels = [:EQ, :PerEQ, :Exp, :Matern12, :Matern32, :Matern52, :RQ, :Cosine, :Linear, :Poly, :GammaExp, :Wiener,
    :WienerVelocity, :Sum, :Product, :Scaled, :Stretched]

for k in kernels
    @eval @functor $k
end


text:
# Automatically generated using Clang.jl

# const CIMGUI_API = EXTERN

struct ImVec2_Simple
    x::Cfloat
    y::Cfloat
end

struct ImVec4_Simple
    x::Cfloat
    y::Cfloat
    z::Cfloat
    w::Cfloat
end

struct ImColor_Simple
    Value::ImVec4_Simple
end

const ImGuiID = UInt32

struct ImGuiStoragePair
    key::ImGuiID
end

struct ImGuiTextRange
    b::Cstring
    e::Cstring
end

struct ImVec2
    x::Cfloat
    y::Cfloat
end

struct ImVector_float
    Size::Cint
    Capacity::Cint
    Data::Ptr{Cfloat}
end

const ImWchar = UInt16

struct ImVector_ImWchar
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImWchar}
end

struct ImFontGlyph
    Codepoint::ImWchar
    AdvanceX::Cfloat
    X0::Cfloat
    Y0::Cfloat
    X1::Cfloat
    Y1::Cfloat
    U0::Cfloat
    V0::Cfloat
    U1::Cfloat
    V1::Cfloat
end

struct ImVector_ImFontGlyph
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImFontGlyph}
end

const ImFontAtlasFlags = Cint
const ImTextureID = Ptr{Cvoid}

struct ImVector_ImFontPtr{ImFont_}
    Size::Cint
    Capacity::Cint
    Data::Ptr{Ptr{ImFont_}}
end

struct ImVector_ImFontAtlasCustomRect{_ImFontAtlasCustomRect}
    Size::Cint
    Capacity::Cint
    Data::Ptr{_ImFontAtlasCustomRect}
end

struct ImFontConfig{ImFont_}
    FontData::Ptr{Cvoid}
    FontDataSize::Cint
    FontDataOwnedByAtlas::Bool
    FontNo::Cint
    SizePixels::Cfloat
    OversampleH::Cint
    OversampleV::Cint
    PixelSnapH::Bool
    GlyphExtraSpacing::ImVec2
    GlyphOffset::ImVec2
    GlyphRanges::Ptr{ImWchar}
    GlyphMinAdvanceX::Cfloat
    GlyphMaxAdvanceX::Cfloat
    MergeMode::Bool
    RasterizerFlags::UInt32
    RasterizerMultiply::Cfloat
    EllipsisChar::ImWchar
    Name::NTuple{40, UInt8}
    DstFont::Ptr{ImFont_}
end

struct ImVector_ImFontConfig
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImFontConfig}
end

struct ImFontAtlas
    Locked::Bool
    Flags::ImFontAtlasFlags
    TexID::ImTextureID
    TexDesiredWidth::Cint
    TexGlyphPadding::Cint
    TexPixelsAlpha8::Ptr{Cuchar}
    TexPixelsRGBA32::Ptr{UInt32}
    TexWidth::Cint
    TexHeight::Cint
    TexUvScale::ImVec2
    TexUvWhitePixel::ImVec2
    Fonts::ImVector_ImFontPtr
    CustomRects::ImVector_ImFontAtlasCustomRect
    ConfigData::ImVector_ImFontConfig
    CustomRectIds::NTuple{1, Cint}
end

struct ImFont
    IndexAdvanceX::ImVector_float
    FallbackAdvanceX::Cfloat
    FontSize::Cfloat
    IndexLookup::ImVector_ImWchar
    Glyphs::ImVector_ImFontGlyph
    FallbackGlyph::Ptr{ImFontGlyph}
    DisplayOffset::ImVec2
    ContainerAtlas::Ptr{ImFontAtlas}
    ConfigData::Ptr{ImFontConfig}
    ConfigDataCount::Int16
    FallbackChar::ImWchar
    EllipsisChar::ImWchar
    Scale::Cfloat
    Ascent::Cfloat
    Descent::Cfloat
    MetricsTotalSurface::Cint
    DirtyLookupTables::Bool
end

struct ImFontAtlasCustomRect
    ID::UInt32
    Width::UInt16
    Height::UInt16
    X::UInt16
    Y::UInt16
    GlyphAdvanceX::Cfloat
    GlyphOffset::ImVec2
    Font::Ptr{ImFont}
end

struct ImVec4
    x::Cfloat
    y::Cfloat
    z::Cfloat
    w::Cfloat
end

struct ImVector_ImGuiTextRange
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImGuiTextRange}
end

struct ImGuiTextFilter
    InputBuf::NTuple{256, UInt8}
    Filters::ImVector_ImGuiTextRange
    CountGrep::Cint
end

struct ImVector_char
    Size::Cint
    Capacity::Cint
    Data::Cstring
end

struct ImGuiTextBuffer
    Buf::ImVector_char
end

const ImGuiDir = Cint

struct ImGuiStyle
    Alpha::Cfloat
    WindowPadding::ImVec2
    WindowRounding::Cfloat
    WindowBorderSize::Cfloat
    WindowMinSize::ImVec2
    WindowTitleAlign::ImVec2
    WindowMenuButtonPosition::ImGuiDir
    ChildRounding::Cfloat
    ChildBorderSize::Cfloat
    PopupRounding::Cfloat
    PopupBorderSize::Cfloat
    FramePadding::ImVec2
    FrameRounding::Cfloat
    FrameBorderSize::Cfloat
    ItemSpacing::ImVec2
    ItemInnerSpacing::ImVec2
    TouchExtraPadding::ImVec2
    IndentSpacing::Cfloat
    ColumnsMinSpacing::Cfloat
    ScrollbarSize::Cfloat
    ScrollbarRounding::Cfloat
    GrabMinSize::Cfloat
    GrabRounding::Cfloat
    TabRounding::Cfloat
    TabBorderSize::Cfloat
    ColorButtonPosition::ImGuiDir
    ButtonTextAlign::ImVec2
    SelectableTextAlign::ImVec2
    DisplayWindowPadding::ImVec2
    DisplaySafeAreaPadding::ImVec2
    MouseCursorScale::Cfloat
    AntiAliasedLines::Bool
    AntiAliasedFill::Bool
    CurveTessellationTol::Cfloat
    Colors::NTuple{48, ImVec4}
end

struct ImVector_ImGuiStoragePair
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImGuiStoragePair}
end

struct ImGuiStorage
    Data::ImVector_ImGuiStoragePair
end

struct ImGuiSizeCallbackData
    UserData::Ptr{Cvoid}
    Pos::ImVec2
    CurrentSize::ImVec2
    DesiredSize::ImVec2
end

struct ImGuiPayload
    Data::Ptr{Cvoid}
    DataSize::Cint
    SourceId::ImGuiID
    SourceParentId::ImGuiID
    DataFrameCount::Cint
    DataType::NTuple{33, UInt8}
    Preview::Bool
    Delivery::Bool
end

struct ImGuiOnceUponAFrame
    RefFrame::Cint
end

struct ImGuiListClipper
    StartPosY::Cfloat
    ItemsHeight::Cfloat
    ItemsCount::Cint
    StepNo::Cint
    DisplayStart::Cint
    DisplayEnd::Cint
end

const ImGuiInputTextFlags = Cint
const ImGuiKey = Cint

struct ImGuiInputTextCallbackData
    EventFlag::ImGuiInputTextFlags
    Flags::ImGuiInputTextFlags
    UserData::Ptr{Cvoid}
    EventChar::ImWchar
    EventKey::ImGuiKey
    Buf::Cstring
    BufTextLen::Cint
    BufSize::Cint
    BufDirty::Bool
    CursorPos::Cint
    SelectionStart::Cint
    SelectionEnd::Cint
end

const ImGuiConfigFlags = Cint
const ImGuiBackendFlags = Cint

struct ImGuiIO
    ConfigFlags::ImGuiConfigFlags
    BackendFlags::ImGuiBackendFlags
    DisplaySize::ImVec2
    DeltaTime::Cfloat
    IniSavingRate::Cfloat
    IniFilename::Cstring
    LogFilename::Cstring
    MouseDoubleClickTime::Cfloat
    MouseDoubleClickMaxDist::Cfloat
    MouseDragThreshold::Cfloat
    KeyMap::NTuple{22, Cint}
    KeyRepeatDelay::Cfloat
    KeyRepeatRate::Cfloat
    UserData::Ptr{Cvoid}
    Fonts::Ptr{ImFontAtlas}
    FontGlobalScale::Cfloat
    FontAllowUserScaling::Bool
    FontDefault::Ptr{ImFont}
    DisplayFramebufferScale::ImVec2
    MouseDrawCursor::Bool
    ConfigMacOSXBehaviors::Bool
    ConfigInputTextCursorBlink::Bool
    ConfigWindowsResizeFromEdges::Bool
    ConfigWindowsMoveFromTitleBarOnly::Bool
    ConfigWindowsMemoryCompactTimer::Cfloat
    BackendPlatformName::Cstring
    BackendRendererName::Cstring
    BackendPlatformUserData::Ptr{Cvoid}
    BackendRendererUserData::Ptr{Cvoid}
    BackendLanguageUserData::Ptr{Cvoid}
    GetClipboardTextFn::Ptr{Cvoid}
    SetClipboardTextFn::Ptr{Cvoid}
    ClipboardUserData::Ptr{Cvoid}
    ImeSetInputScreenPosFn::Ptr{Cvoid}
    ImeWindowHandle::Ptr{Cvoid}
    RenderDrawListsFnUnused::Ptr{Cvoid}
    MousePos::ImVec2
    MouseDown::NTuple{5, Bool}
    MouseWheel::Cfloat
    MouseWheelH::Cfloat
    KeyCtrl::Bool
    KeyShift::Bool
    KeyAlt::Bool
    KeySuper::Bool
    KeysDown::NTuple{512, Bool}
    NavInputs::NTuple{21, Cfloat}
    WantCaptureMouse::Bool
    WantCaptureKeyboard::Bool
    WantTextInput::Bool
    WantSetMousePos::Bool
    WantSaveIniSettings::Bool
    NavActive::Bool
    NavVisible::Bool
    Framerate::Cfloat
    MetricsRenderVertices::Cint
    MetricsRenderIndices::Cint
    MetricsRenderWindows::Cint
    MetricsActiveWindows::Cint
    MetricsActiveAllocations::Cint
    MouseDelta::ImVec2
    MousePosPrev::ImVec2
    MouseClickedPos::NTuple{5, ImVec2}
    MouseClickedTime::NTuple{5, Cdouble}
    MouseClicked::NTuple{5, Bool}
    MouseDoubleClicked::NTuple{5, Bool}
    MouseReleased::NTuple{5, Bool}
    MouseDownOwned::NTuple{5, Bool}
    MouseDownWasDoubleClick::NTuple{5, Bool}
    MouseDownDuration::NTuple{5, Cfloat}
    MouseDownDurationPrev::NTuple{5, Cfloat}
    MouseDragMaxDistanceAbs::NTuple{5, ImVec2}
    MouseDragMaxDistanceSqr::NTuple{5, Cfloat}
    KeysDownDuration::NTuple{512, Cfloat}
    KeysDownDurationPrev::NTuple{512, Cfloat}
    NavInputsDownDuration::NTuple{21, Cfloat}
    NavInputsDownDurationPrev::NTuple{21, Cfloat}
    InputQueueCharacters::ImVector_ImWchar
end

const ImGuiContext = Cvoid

struct ImColor
    Value::ImVec4
end

const ImU32 = UInt32

struct ImVector_ImU32
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImU32}
end

struct ImFontGlyphRangesBuilder
    UsedChars::ImVector_ImU32
end

struct ImDrawVert
    pos::ImVec2
    uv::ImVec2
    col::ImU32
end

const ImDrawCallback = Ptr{Cvoid}

struct ImDrawCmd
    ElemCount::UInt32
    ClipRect::ImVec4
    TextureId::ImTextureID
    VtxOffset::UInt32
    IdxOffset::UInt32
    UserCallback::ImDrawCallback
    UserCallbackData::Ptr{Cvoid}
end

struct ImVector_ImDrawCmd
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImDrawCmd}
end

const ImDrawIdx = UInt16

struct ImVector_ImDrawIdx
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImDrawIdx}
end

struct ImDrawChannel
    _CmdBuffer::ImVector_ImDrawCmd
    _IdxBuffer::ImVector_ImDrawIdx
end

struct ImVector_ImDrawChannel
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImDrawChannel}
end

struct ImDrawListSplitter
    _Current::Cint
    _Count::Cint
    _Channels::ImVector_ImDrawChannel
end

const ImDrawListSharedData = Cvoid

struct ImVector_ImDrawVert
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImDrawVert}
end

const ImDrawListFlags = Cint

struct ImVector_ImVec4
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImVec4}
end

struct ImVector_ImTextureID
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImTextureID}
end

struct ImVector_ImVec2
    Size::Cint
    Capacity::Cint
    Data::Ptr{ImVec2}
end

struct ImDrawList
    CmdBuffer::ImVector_ImDrawCmd
    IdxBuffer::ImVector_ImDrawIdx
    VtxBuffer::ImVector_ImDrawVert
    Flags::ImDrawListFlags
    _Data::Ptr{ImDrawListSharedData}
    _OwnerName::Cstring
    _VtxCurrentOffset::UInt32
    _VtxCurrentIdx::UInt32
    _VtxWritePtr::Ptr{ImDrawVert}
    _IdxWritePtr::Ptr{ImDrawIdx}
    _ClipRectStack::ImVector_ImVec4
    _TextureIdStack::ImVector_ImTextureID
    _Path::ImVector_ImVec2
    _Splitter::ImDrawListSplitter
end

struct ImDrawData
    Valid::Bool
    CmdLists::Ptr{Ptr{ImDrawList}}
    CmdListsCount::Cint
    TotalIdxCount::Cint
    TotalVtxCount::Cint
    DisplayPos::ImVec2
    DisplaySize::ImVec2
    FramebufferScale::ImVec2
end

const ImGuiCol = Cint
const ImGuiCond = Cint
const ImGuiDataType = Cint
const ImGuiNavInput = Cint
const ImGuiMouseCursor = Cint
const ImGuiStyleVar = Cint
const ImDrawCornerFlags = Cint
const ImGuiColorEditFlags = Cint
const ImGuiComboFlags = Cint
const ImGuiDragDropFlags = Cint
const ImGuiFocusedFlags = Cint
const ImGuiHoveredFlags = Cint
const ImGuiSelectableFlags = Cint
const ImGuiTabBarFlags = Cint
const ImGuiTabItemFlags = Cint
const ImGuiTreeNodeFlags = Cint
const ImGuiWindowFlags = Cint
const ImGuiInputTextCallback = Ptr{Cvoid}
const ImGuiSizeCallback = Ptr{Cvoid}
const ImS8 = UInt8
const ImU8 = Cuchar
const ImS16 = Int16
const ImU16 = UInt16
const ImS32 = Cint
const ImS64 = Int64
const ImU64 = UInt64

struct ImVector
    Size::Cint
    Capacity::Cint
    Data::Ptr{Cvoid}
end

@cenum ImGuiWindowFlags_::UInt32 begin
    ImGuiWindowFlags_None = 0
    ImGuiWindowFlags_NoTitleBar = 1
    ImGuiWindowFlags_NoResize = 2
    ImGuiWindowFlags_NoMove = 4
    ImGuiWindowFlags_NoScrollbar = 8
    ImGuiWindowFlags_NoScrollWithMouse = 16
    ImGuiWindowFlags_NoCollapse = 32
    ImGuiWindowFlags_AlwaysAutoResize = 64
    ImGuiWindowFlags_NoBackground = 128
    ImGuiWindowFlags_NoSavedSettings = 256
    ImGuiWindowFlags_NoMouseInputs = 512
    ImGuiWindowFlags_MenuBar = 1024
    ImGuiWindowFlags_HorizontalScrollbar = 2048
    ImGuiWindowFlags_NoFocusOnAppearing = 4096
    ImGuiWindowFlags_NoBringToFrontOnFocus = 8192
    ImGuiWindowFlags_AlwaysVerticalScrollbar = 16384
    ImGuiWindowFlags_AlwaysHorizontalScrollbar = 32768
    ImGuiWindowFlags_AlwaysUseWindowPadding = 65536
    ImGuiWindowFlags_NoNavInputs = 262144
    ImGuiWindowFlags_NoNavFocus = 524288
    ImGuiWindowFlags_UnsavedDocument = 1048576
    ImGuiWindowFlags_NoNav = 786432
    ImGuiWindowFlags_NoDecoration = 43
    ImGuiWindowFlags_NoInputs = 786944
    ImGuiWindowFlags_NavFlattened = 8388608
    ImGuiWindowFlags_ChildWindow = 16777216
    ImGuiWindowFlags_Tooltip = 33554432
    ImGuiWindowFlags_Popup = 67108864
    ImGuiWindowFlags_Modal = 134217728
    ImGuiWindowFlags_ChildMenu = 268435456
end

@cenum ImGuiInputTextFlags_::UInt32 begin
    ImGuiInputTextFlags_None = 0
    ImGuiInputTextFlags_CharsDecimal = 1
    ImGuiInputTextFlags_CharsHexadecimal = 2
    ImGuiInputTextFlags_CharsUppercase = 4
    ImGuiInputTextFlags_CharsNoBlank = 8
    ImGuiInputTextFlags_AutoSelectAll = 16
    ImGuiInputTextFlags_EnterReturnsTrue = 32
    ImGuiInputTextFlags_CallbackCompletion = 64
    ImGuiInputTextFlags_CallbackHistory = 128
    ImGuiInputTextFlags_CallbackAlways = 256
    ImGuiInputTextFlags_CallbackCharFilter = 512
    ImGuiInputTextFlags_AllowTabInput = 1024
    ImGuiInputTextFlags_CtrlEnterForNewLine = 2048
    ImGuiInputTextFlags_NoHorizontalScroll = 4096
    ImGuiInputTextFlags_AlwaysInsertMode = 8192
    ImGuiInputTextFlags_ReadOnly = 16384
    ImGuiInputTextFlags_Password = 32768
    ImGuiInputTextFlags_NoUndoRedo = 65536
    ImGuiInputTextFlags_CharsScientific = 131072
    ImGuiInputTextFlags_CallbackResize = 262144
    ImGuiInputTextFlags_Multiline = 1048576
    ImGuiInputTextFlags_NoMarkEdited = 2097152
end

@cenum ImGuiTreeNodeFlags_::UInt32 begin
    ImGuiTreeNodeFlags_None = 0
    ImGuiTreeNodeFlags_Selected = 1
    ImGuiTreeNodeFlags_Framed = 2
    ImGuiTreeNodeFlags_AllowItemOverlap = 4
    ImGuiTreeNodeFlags_NoTreePushOnOpen = 8
    ImGuiTreeNodeFlags_NoAutoOpenOnLog = 16
    ImGuiTreeNodeFlags_DefaultOpen = 32
    ImGuiTreeNodeFlags_OpenOnDoubleClick = 64
    ImGuiTreeNodeFlags_OpenOnArrow = 128
    ImGuiTreeNodeFlags_Leaf = 256
    ImGuiTreeNodeFlags_Bullet = 512
    ImGuiTreeNodeFlags_FramePadding = 1024
    ImGuiTreeNodeFlags_SpanAvailWidth = 2048
    ImGuiTreeNodeFlags_SpanFullWidth = 4096
    ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 8192
    ImGuiTreeNodeFlags_CollapsingHeader = 26
end

@cenum ImGuiSelectableFlags_::UInt32 begin
    ImGuiSelectableFlags_None = 0
    ImGuiSelectableFlags_DontClosePopups = 1
    ImGuiSelectableFlags_SpanAllColumns = 2
    ImGuiSelectableFlags_AllowDoubleClick = 4
    ImGuiSelectableFlags_Disabled = 8
    ImGuiSelectableFlags_AllowItemOverlap = 16
end

@cenum ImGuiComboFlags_::UInt32 begin
    ImGuiComboFlags_None = 0
    ImGuiComboFlags_PopupAlignLeft = 1
    ImGuiComboFlags_HeightSmall = 2
    ImGuiComboFlags_HeightRegular = 4
    ImGuiComboFlags_HeightLarge = 8
    ImGuiComboFlags_HeightLargest = 16
    ImGuiComboFlags_NoArrowButton = 32
    ImGuiComboFlags_NoPreview = 64
    ImGuiComboFlags_HeightMask_ = 30
end

@cenum ImGuiTabBarFlags_::UInt32 begin
    ImGuiTabBarFlags_None = 0
    ImGuiTabBarFlags_Reorderable = 1
    ImGuiTabBarFlags_AutoSelectNewTabs = 2
    ImGuiTabBarFlags_TabListPopupButton = 4
    ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 8
    ImGuiTabBarFlags_NoTabListScrollingButtons = 16
    ImGuiTabBarFlags_NoTooltip = 32
    ImGuiTabBarFlags_FittingPolicyResizeDown = 64
    ImGuiTabBarFlags_FittingPolicyScroll = 128
    ImGuiTabBarFlags_FittingPolicyMask_ = 192
    ImGuiTabBarFlags_FittingPolicyDefault_ = 64
end

@cenum ImGuiTabItemFlags_::UInt32 begin
    ImGuiTabItemFlags_None = 0
    ImGuiTabItemFlags_UnsavedDocument = 1
    ImGuiTabItemFlags_SetSelected = 2
    ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 4
    ImGuiTabItemFlags_NoPushId = 8
end

@cenum ImGuiFocusedFlags_::UInt32 begin
    ImGuiFocusedFlags_None = 0
    ImGuiFocusedFlags_ChildWindows = 1
    ImGuiFocusedFlags_RootWindow = 2
    ImGuiFocusedFlags_AnyWindow = 4
    ImGuiFocusedFlags_RootAndChildWindows = 3
end

@cenum ImGuiHoveredFlags_::UInt32 begin
    ImGuiHoveredFlags_None = 0
    ImGuiHoveredFlags_ChildWindows = 1
    ImGuiHoveredFlags_RootWindow = 2
    ImGuiHoveredFlags_AnyWindow = 4
    ImGuiHoveredFlags_AllowWhenBlockedByPopup = 8
    ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 32
    ImGuiHoveredFlags_AllowWhenOverlapped = 64
    ImGuiHoveredFlags_AllowWhenDisabled = 128
    ImGuiHoveredFlags_RectOnly = 104
    ImGuiHoveredFlags_RootAndChildWindows = 3
end

@cenum ImGuiDragDropFlags_::UInt32 begin
    ImGuiDragDropFlags_None = 0
    ImGuiDragDropFlags_SourceNoPreviewTooltip = 1
    ImGuiDragDropFlags_SourceNoDisableHover = 2
    ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 4
    ImGuiDragDropFlags_SourceAllowNullID = 8
    ImGuiDragDropFlags_SourceExtern = 16
    ImGuiDragDropFlags_SourceAutoExpirePayload = 32
    ImGuiDragDropFlags_AcceptBeforeDelivery = 1024
    ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 2048
    ImGuiDragDropFlags_AcceptNoPreviewTooltip = 4096
    ImGuiDragDropFlags_AcceptPeekOnly = 3072
end

@cenum ImGuiDataType_::UInt32 begin
    ImGuiDataType_S8 = 0
    ImGuiDataType_U8 = 1
    ImGuiDataType_S16 = 2
    ImGuiDataType_U16 = 3
    ImGuiDataType_S32 = 4
    ImGuiDataType_U32 = 5
    ImGuiDataType_S64 = 6
    ImGuiDataType_U64 = 7
    ImGuiDataType_Float = 8
    ImGuiDataType_Double = 9
    ImGuiDataType_COUNT = 10
end

@cenum ImGuiDir_::Int32 begin
    ImGuiDir_None = -1
    ImGuiDir_Left = 0
    ImGuiDir_Right = 1
    ImGuiDir_Up = 2
    ImGuiDir_Down = 3
    ImGuiDir_COUNT = 4
end

@cenum ImGuiKey_::UInt32 begin
    ImGuiKey_Tab = 0
    ImGuiKey_LeftArrow = 1
    ImGuiKey_RightArrow = 2
    ImGuiKey_UpArrow = 3
    ImGuiKey_DownArrow = 4
    ImGuiKey_PageUp = 5
    ImGuiKey_PageDown = 6
    ImGuiKey_Home = 7
    ImGuiKey_End = 8
    ImGuiKey_Insert = 9
    ImGuiKey_Delete = 10
    ImGuiKey_Backspace = 11
    ImGuiKey_Space = 12
    ImGuiKey_Enter = 13
    ImGuiKey_Escape = 14
    ImGuiKey_KeyPadEnter = 15
    ImGuiKey_A = 16
    ImGuiKey_C = 17
    ImGuiKey_V = 18
    ImGuiKey_X = 19
    ImGuiKey_Y = 20
    ImGuiKey_Z = 21
    ImGuiKey_COUNT = 22
end

@cenum ImGuiNavInput_::UInt32 begin
    ImGuiNavInput_Activate = 0
    ImGuiNavInput_Cancel = 1
    ImGuiNavInput_Input = 2
    ImGuiNavInput_Menu = 3
    ImGuiNavInput_DpadLeft = 4
    ImGuiNavInput_DpadRight = 5
    ImGuiNavInput_DpadUp = 6
    ImGuiNavInput_DpadDown = 7
    ImGuiNavInput_LStickLeft = 8
    ImGuiNavInput_LStickRight = 9
    ImGuiNavInput_LStickUp = 10
    ImGuiNavInput_LStickDown = 11
    ImGuiNavInput_FocusPrev = 12
    ImGuiNavInput_FocusNext = 13
    ImGuiNavInput_TweakSlow = 14
    ImGuiNavInput_TweakFast = 15
    ImGuiNavInput_KeyMenu_ = 16
    ImGuiNavInput_KeyLeft_ = 17
    ImGuiNavInput_KeyRight_ = 18
    ImGuiNavInput_KeyUp_ = 19
    ImGuiNavInput_KeyDown_ = 20
    ImGuiNavInput_COUNT = 21
    ImGuiNavInput_InternalStart_ = 16
end

@cenum ImGuiConfigFlags_::UInt32 begin
    ImGuiConfigFlags_None = 0
    ImGuiConfigFlags_NavEnableKeyboard = 1
    ImGuiConfigFlags_NavEnableGamepad = 2
    ImGuiConfigFlags_NavEnableSetMousePos = 4
    ImGuiConfigFlags_NavNoCaptureKeyboard = 8
    ImGuiConfigFlags_NoMouse = 16
    ImGuiConfigFlags_NoMouseCursorChange = 32
    ImGuiConfigFlags_IsSRGB = 1048576
    ImGuiConfigFlags_IsTouchScreen = 2097152
end

@cenum ImGuiBackendFlags_::UInt32 begin
    ImGuiBackendFlags_None = 0
    ImGuiBackendFlags_HasGamepad = 1
    ImGuiBackendFlags_HasMouseCursors = 2
    ImGuiBackendFlags_HasSetMousePos = 4
    ImGuiBackendFlags_RendererHasVtxOffset = 8
end

@cenum ImGuiCol_::UInt32 begin
    ImGuiCol_Text = 0
    ImGuiCol_TextDisabled = 1
    ImGuiCol_WindowBg = 2
    ImGuiCol_ChildBg = 3
    ImGuiCol_PopupBg = 4
    ImGuiCol_Border = 5
    ImGuiCol_BorderShadow = 6
    ImGuiCol_FrameBg = 7
    ImGuiCol_FrameBgHovered = 8
    ImGuiCol_FrameBgActive = 9
    ImGuiCol_TitleBg = 10
    ImGuiCol_TitleBgActive = 11
    ImGuiCol_TitleBgCollapsed = 12
    ImGuiCol_MenuBarBg = 13
    ImGuiCol_ScrollbarBg = 14
    ImGuiCol_ScrollbarGrab = 15
    ImGuiCol_ScrollbarGrabHovered = 16
    ImGuiCol_ScrollbarGrabActive = 17
    ImGuiCol_CheckMark = 18
    ImGuiCol_SliderGrab = 19
    ImGuiCol_SliderGrabActive = 20
    ImGuiCol_Button = 21
    ImGuiCol_ButtonHovered = 22
    ImGuiCol_ButtonActive = 23
    ImGuiCol_Header = 24
    ImGuiCol_HeaderHovered = 25
    ImGuiCol_HeaderActive = 26
    ImGuiCol_Separator = 27
    ImGuiCol_SeparatorHovered = 28
    ImGuiCol_SeparatorActive = 29
    ImGuiCol_ResizeGrip = 30
    ImGuiCol_ResizeGripHovered = 31
    ImGuiCol_ResizeGripActive = 32
    ImGuiCol_Tab = 33
    ImGuiCol_TabHovered = 34
    ImGuiCol_TabActive = 35
    ImGuiCol_TabUnfocused = 36
    ImGuiCol_TabUnfocusedActive = 37
    ImGuiCol_PlotLines = 38
    ImGuiCol_PlotLinesHovered = 39
    ImGuiCol_PlotHistogram = 40
    ImGuiCol_PlotHistogramHovered = 41
    ImGuiCol_TextSelectedBg = 42
    ImGuiCol_DragDropTarget = 43
    ImGuiCol_NavHighlight = 44
    ImGuiCol_NavWindowingHighlight = 45
    ImGuiCol_NavWindowingDimBg = 46
    ImGuiCol_ModalWindowDimBg = 47
    ImGuiCol_COUNT = 48
end

@cenum ImGuiStyleVar_::UInt32 begin
    ImGuiStyleVar_Alpha = 0
    ImGuiStyleVar_WindowPadding = 1
    ImGuiStyleVar_WindowRounding = 2
    ImGuiStyleVar_WindowBorderSize = 3
    ImGuiStyleVar_WindowMinSize = 4
    ImGuiStyleVar_WindowTitleAlign = 5
    ImGuiStyleVar_ChildRounding = 6
    ImGuiStyleVar_ChildBorderSize = 7
    ImGuiStyleVar_PopupRounding = 8
    ImGuiStyleVar_PopupBorderSize = 9
    ImGuiStyleVar_FramePadding = 10
    ImGuiStyleVar_FrameRounding = 11
    ImGuiStyleVar_FrameBorderSize = 12
    ImGuiStyleVar_ItemSpacing = 13
    ImGuiStyleVar_ItemInnerSpacing = 14
    ImGuiStyleVar_IndentSpacing = 15
    ImGuiStyleVar_ScrollbarSize = 16
    ImGuiStyleVar_ScrollbarRounding = 17
    ImGuiStyleVar_GrabMinSize = 18
    ImGuiStyleVar_GrabRounding = 19
    ImGuiStyleVar_TabRounding = 20
    ImGuiStyleVar_ButtonTextAlign = 21
    ImGuiStyleVar_SelectableTextAlign = 22
    ImGuiStyleVar_COUNT = 23
end

@cenum ImGuiColorEditFlags_::UInt32 begin
    ImGuiColorEditFlags_None = 0
    ImGuiColorEditFlags_NoAlpha = 2
    ImGuiColorEditFlags_NoPicker = 4
    ImGuiColorEditFlags_NoOptions = 8
    ImGuiColorEditFlags_NoSmallPreview = 16
    ImGuiColorEditFlags_NoInputs = 32
    ImGuiColorEditFlags_NoTooltip = 64
    ImGuiColorEditFlags_NoLabel = 128
    ImGuiColorEditFlags_NoSidePreview = 256
    ImGuiColorEditFlags_NoDragDrop = 512
    ImGuiColorEditFlags_AlphaBar = 65536
    ImGuiColorEditFlags_AlphaPreview = 131072
    ImGuiColorEditFlags_AlphaPreviewHalf = 262144
    ImGuiColorEditFlags_HDR = 524288
    ImGuiColorEditFlags_DisplayRGB = 1048576
    ImGuiColorEditFlags_DisplayHSV = 2097152
    ImGuiColorEditFlags_DisplayHex = 4194304
    ImGuiColorEditFlags_Uint8 = 8388608
    ImGuiColorEditFlags_Float = 16777216
    ImGuiColorEditFlags_PickerHueBar = 33554432
    ImGuiColorEditFlags_PickerHueWheel = 67108864
    ImGuiColorEditFlags_InputRGB = 134217728
    ImGuiColorEditFlags_InputHSV = 268435456
    ImGuiColorEditFlags__OptionsDefault = 177209344
    ImGuiColorEditFlags__DisplayMask = 7340032
    ImGuiColorEditFlags__DataTypeMask = 25165824
    ImGuiColorEditFlags__PickerMask = 100663296
    ImGuiColorEditFlags__InputMask = 402653184
end

@cenum ImGuiMouseCursor_::Int32 begin
    ImGuiMouseCursor_None = -1
    ImGuiMouseCursor_Arrow = 0
    ImGuiMouseCursor_TextInput = 1
    ImGuiMouseCursor_ResizeAll = 2
    ImGuiMouseCursor_ResizeNS = 3
    ImGuiMouseCursor_ResizeEW = 4
    ImGuiMouseCursor_ResizeNESW = 5
    ImGuiMouseCursor_ResizeNWSE = 6
    ImGuiMouseCursor_Hand = 7
    ImGuiMouseCursor_COUNT = 8
end

@cenum ImGuiCond_::UInt32 begin
    ImGuiCond_Always = 1
    ImGuiCond_Once = 2
    ImGuiCond_FirstUseEver = 4
    ImGuiCond_Appearing = 8
end

@cenum ImDrawCornerFlags_::UInt32 begin
    ImDrawCornerFlags_None = 0
    ImDrawCornerFlags_TopLeft = 1
    ImDrawCornerFlags_TopRight = 2
    ImDrawCornerFlags_BotLeft = 4
    ImDrawCornerFlags_BotRight = 8
    ImDrawCornerFlags_Top = 3
    ImDrawCornerFlags_Bot = 12
    ImDrawCornerFlags_Left = 5
    ImDrawCornerFlags_Right = 10
    ImDrawCornerFlags_All = 15
end

@cenum ImDrawListFlags_::UInt32 begin
    ImDrawListFlags_None = 0
    ImDrawListFlags_AntiAliasedLines = 1
    ImDrawListFlags_AntiAliasedFill = 2
    ImDrawListFlags_AllowVtxOffset = 4
end

@cenum ImFontAtlasFlags_::UInt32 begin
    ImFontAtlasFlags_None = 0
    ImFontAtlasFlags_NoPowerOfTwoHeight = 1
    ImFontAtlasFlags_NoMouseCursors = 2
end


text:
# TODO allow the lower and upper bounds to be parameterized.
# use default values for the lower and upper bounds ? (0 and 1)

struct Beta <: Distribution{Float64} end

"""
    beta(alpha::Real, beta::Real)

Sample a `Float64` from a beta distribution.
"""
const beta = Beta()

function logpdf(::Beta, x::Real, alpha::Real, beta::Real)
    (alpha - 1) * log(x) + (beta - 1) * log1p(-x) - logbeta(alpha, beta)
end

function logpdf_grad(::Beta, x::Real, alpha::Real, beta::Real)
    if 0 <= x <= 1
        deriv_x = (alpha - 1) / x - (beta - 1) / (1 - x)
        deriv_alpha = log(x) - (digamma(alpha) - digamma(alpha + beta))
        deriv_beta = log1p(-x) - (digamma(beta) - digamma(alpha + beta))
    else
        error("x is outside of support: $x")
    end
    (deriv_x, deriv_alpha, deriv_beta)
end

function random(::Beta, alpha::Real, beta::Real)
    rand(Distributions.Beta(alpha, beta))
end
is_discrete(::Beta) = false

(::Beta)(alpha, beta) = random(Beta(), alpha, beta)

has_output_grad(::Beta) = true
has_argument_grads(::Beta) = (true, true)

export beta


text:
function _precompile_()
    ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
    Base.precompile(Tuple{typeof(RecipesBase.create_kw_body),Expr})
    Base.precompile(Tuple{typeof(RecipesBase.get_function_def),Expr,Array{Any,1}})
    Base.precompile(Tuple{typeof(map),Function,Array{AbstractLayout,1}})
    Base.precompile(Tuple{typeof(map),Function,Array{AbstractLayout,2}})
end

_precompile_()


text:
using Pkg; Pkg.activate("..")
using PlotAxes, VegaLite, AxisArrays

x = cumsum(randn(100))
vlplot_axes(x)

x = rand(100,100)
vlplot_axes(x)

x = rand(100,100,3)
vlplot_axes(x)

myexception = nothing
try
  using RCall
catch e
  global myexception
  myexception = e
end


text:
# Height of the screen
HEIGHT = 200
# Width of the screen
WIDTH = 400
# Global variables to store a range of colors.
# The Colors package is always imported into a game
colors = range(colorant"black", colorant"white")
current_color, color_state = iterate(colors)

# The draw function is called once per frame to render objects to the screen.
# In our game, we only define the `draw` function, it's called by the engine.
# Within the function, we draw the individual elements, in this case line, rectangles and circles
function draw()
    fill(current_color)
    draw(Line(50, 100, 350, 100), colorant"white")
    draw(Rect(50, 100, 20, 50), colorant"red", fill=true)
    draw(Rect(50, 100, 20, 50), colorant"white")
    draw(Circle(330, 80, 20), colorant"red", fill=true)
    draw(Circle(330, 80, 20), colorant"white")

    draw(Circle(0,0,50), colorant"red", fill=true)
    draw(Circle(WIDTH,HEIGHT,50), colorant"green", fill=true)

end

# The update function is called once per frame by the game engine, and should be used
# to change the game state. In this case, we iterate through the color range, and store
# the current color in a global variable. The global value is then used in the `draw`
# function to render the screen background
function update()
    global current_color
    global color_state
    i = iterate(colors, color_state)
    if !isnothing(i)
        current_color, color_state = i
    else
        reverse!(colors)
        current_color, color_state = iterate(colors)
    end
end


text:
module DataAndPlotting

using HDF5
using Plots		#; pyplot() - default backend is gr()

# Wave params x & y directions
const ki = 3
const kj = 3

# define 20x30 array - used for storing standing waves
const ni = 300
const nj = 200

export init
function init()
	arr = zeros(Float64, ni, nj)
	for j in 1:nj, i in 1:ni
		x = index2coord(i, ni)
		y = index2coord(j, nj)
		arr[i, j] = sin(2pi*ki*x) * sin(2pi*kj*y)
	end
	arr
end

# i ranges from 1 to ni
# i - 1 ranges from 0 to ni - 1
# x ranges from 0 to 1
function index2coord(i, ni)
	@assert ni > 1
	@assert 1 <= i <= ni
	(i - 1) / (ni - 1)
end

#write data to file
export output
function output(arr::Array{Float64,2}, filename::String)
	h5write(filename, "data",arr)
end

export input
function input(filename::String)
	arr = h5read(filename, "data") #, (1:ni, 1:nj))
end

#Create Plots
export makeplot
function makeplot(arr::Array{Float64,2}, filename::String)
	heatmap(arr, clim=(-1.0, +1.0), color=:viridis)
	savefig(filename)
end

end # module