text:
function ueberpruefeSatzvervollstaendigung1(c,d)

if cmp(c,"Zeilen") == 0 #Null bedeutet richtig, stimmt überein
    if cmp(d,"Spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    elseif cmp(d,"spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    else
        println("Eure Antwort für Wort1 ist korrekt, nicht aber die für Wort2. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    end
elseif cmp(c,"zeilen") == 0 #Null bedeutet richtig, stimmt überein
    if cmp(d,"Spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    elseif cmp(d,"spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    else
        println("Eure Antwort für Wort1 ist korrekt, nicht aber die für Wort2. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    end
elseif cmp(c,"Reihen") == 0 #Null bedeutet richtig, stimmt überein
    if cmp(d,"Spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    elseif cmp(d,"spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    else
        println("Eure Antwort für Wort1 ist korrekt, nicht aber die für Wort2. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    end
elseif cmp(c,"reihen") == 0 #Null bedeutet richtig, stimmt überein
    if cmp(d,"Spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    elseif cmp(d,"spalten") == 0
        println("Eure Antwort für Wort1 und Wort2 sind beide korrekt!");
    else
        println("Eure Antwort für Wort1 ist korrekt, nicht aber die für Wort2. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    end
else
    if cmp(d,"Spalten") == 0
        println("Eure Antwort für Wort2 ist korrekt, nicht aber die für Wort1. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    elseif cmp(d,"spalten") == 0
        println("Eure Antwort für Wort2 ist korrekt, nicht aber die für Wort1. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    else
        println("Eure Antworten für Wort1 und Wort2 sind noch nicht korrekt. Lest euch noch einmal den Satz durch und überprüft eure Rechtschreibung.");
    end
end

end


text:
using GoogleCodeSearch
using Test
using HTTP
using JSON
using Sockets

const test_data = [
(path="path1", file="file1", content="""
path1, file1, line1
path1, file1, line2
"""),
(path="path1", file="file2", content="""
path1, file2, line1
path1, file2, line2
"""),
(path="path2", file="file1", content="""
path2, file1, line1
path2, file1, line2
"""),
(path="path2", file="file2", content="""
path2, file2, line1
path2, file2, line2
"""),
(path="path22", file="file1", content="""
path22, file1, line1
path22, file1, line2
"""),
(path="path22", file="file2", content="""
path22, file2, line1
path22, file2, line2
""")
]

# split index on the last alphabet of path component of test_data
test_index_resolver(ctx::Ctx, inpath::String) = joinpath(ctx.store, "index" * rsplit(inpath, '/'; limit=2)[end][end])
test_default_resolver(ctx::Ctx, inpath::String) = joinpath(ctx.store, "index")

function test_noindices(ctx::Ctx)
    @test isempty(paths_indexed(ctx))
    @test isempty(indices(ctx))
    @test isempty(search(ctx, ".*"; ignorecase=true))

    # clear_indices should not error out if there are no indices
    @test clear_indices(ctx) === nothing
    nothing
end

function test_indexing(ctx::Ctx, datadir::String)
    paths = Set(joinpath(datadir, item.path) for item in test_data)
    # paths must be in sorted order during indexing, otherwise indexer will remove a longer path when it encounters a path that is a substring of that
    for path in sort(collect(paths))
        @info("indexing $path")
        @test index(ctx, path)
        @test !isempty(indices(ctx))
        @test any(occursin(x, path) for x in paths_indexed(ctx))
    end
    all_paths = paths_indexed(ctx)
    @test !("/nosuchpath" in all_paths)
    # test that `path2` subsumes `path22`
    @test !any(occursin("path22", x) for x in all_paths)
    nothing
end

function test_search(ctx::Ctx, datadir::String)
    # test case sensitiveness
    @test isempty(search(ctx, "File"))
    @test !isempty(search(ctx, "File"; ignorecase=true))

    # test pathfilter
    for ignorecase in (true, false)
        res = search(ctx, "Line1"; ignorecase=ignorecase, pathfilter=".*/path1/.*")
        if ignorecase
            @test length(res) == 2
            for item in res
                @test item.line == 1
                @test endswith(strip(item.text), "line1")
                @test occursin("path1", item.file)
            end
        else
            @test isempty(res)
        end
    end

    # test split indices
    for ignorecase in (true, false)
        res = search(ctx, "Line2"; ignorecase=ignorecase)
        if ignorecase
            @test length(res) == length(test_data)
            for item in res
                @test item.line == 2
                @test endswith(strip(item.text), "line2")
                @test occursin("path", item.file)
            end
        else
            @test isempty(res)
        end
    end
    nothing
end

function create_test_data(datadir::String)
    for item in test_data
        dir = joinpath(datadir, item.path)
        mkpath(dir)
        open(joinpath(dir, item.file), "w") do f
            println(f, item.content)
        end
    end
end

function test_apis(testdir, resolver)
    storedir = joinpath(testdir, "store")
    datadir = joinpath(testdir, "data")
    create_test_data(datadir)
    mkpath(storedir)

    ctx = Ctx(store=storedir, resolver=resolver)
    test_noindices(ctx)
    test_indexing(ctx, datadir)
    test_search(ctx, datadir)

    @test clear_indices(ctx) === nothing
    test_noindices(ctx)

    nothing
end

function test_http_service(testdir)
    storedir = joinpath(testdir, "store")
    datadir = joinpath(testdir, "data")
    create_test_data(datadir)
    mkpath(storedir)

    ctx = Ctx(store=storedir)
    srvr = @async run_http(ctx)
    wait_for_httpsrvr()

    paths = Set(joinpath(datadir, item.path) for item in test_data)
    # paths must be in sorted order during indexing, otherwise indexer will remove a longer path when it encounters a path that is a substring of that
    for path in sort(collect(paths))
        resp = HTTP.request("POST", "http://127.0.0.1:5555/index"; body="{\"path\":\"$path\"}")
        @test resp.status == 200
        json_resp = JSON.parse(String(resp.body))
        @test json_resp["success"]
    end

    resp = HTTP.request("POST", "http://127.0.0.1:5555/search"; body="{\"pattern\":\"line1\"}")
    @test resp.status == 200
    json_resp = JSON.parse(String(resp.body))
    @test json_resp["success"]
    data = json_resp["data"]
    @test length(data) == 6
    for item in data
        @test item["line"] == 1
        @test endswith(strip(item["text"]), "line1")
    end
end

function wait_for_httpsrvr()
    while true
        try
            sock = connect("127.0.0.1", 5555)
            close(sock)
            return
        catch
            @info("waiting for httpserver to come up at port 5555...")
            sleep(5)
        end
    end
end

for (resolver_name,resolver) in ("split_index"=>test_index_resolver, "single_index"=>test_default_resolver)
    println("testing with $resolver_name")
    mktempdir() do testdir
        test_apis(testdir, resolver)
    end
end

println("testing HTTP service")
mktempdir() do testdir
    test_http_service(testdir)
end

ext: jl


text:
# Autogenerated wrapper script for PDAL_jll for armv7l-linux-musleabihf-cxx03
export libpdal_base, libpdal_plugin_kernel_fauxplugin, libpdal_util, pdal

using GDAL_jll
using libgeotiff_jll
using LASzip_jll
JLLWrappers.@generate_wrapper_header("PDAL")
JLLWrappers.@declare_library_product(libpdal_base, "libpdal_base.so.13")
JLLWrappers.@declare_library_product(libpdal_plugin_kernel_fauxplugin, "libpdal_plugin_kernel_fauxplugin.so.13")
JLLWrappers.@declare_library_product(libpdal_util, "libpdal_util.so.13")
JLLWrappers.@declare_executable_product(pdal)
function __init__()
    JLLWrappers.@generate_init_header(GDAL_jll, libgeotiff_jll, LASzip_jll)
    JLLWrappers.@init_library_product(
        libpdal_base,
        "lib/libpdal_base.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpdal_plugin_kernel_fauxplugin,
        "lib/libpdal_plugin_kernel_fauxplugin.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpdal_util,
        "lib/libpdal_util.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_executable_product(
        pdal,
        "bin/pdal",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
abstract type QueueType end
struct QueueCompute <: QueueType end
struct QueueGraphics <: QueueType end
struct QueueTransfer <: QueueType end
struct QueueSparseBinding <: QueueType end

@enum COMMAND_TYPE CREATE=1 DESTROY ALLOCATE FREE COMMAND ENUMERATE
@enum STRUCT_TYPE CREATE_INFO=1 ALLOCATE_INFO GENERIC_INFO DATA PROPERTY
@enum PARAM_REQUIREMENT OPTIONAL=1 REQUIRED POINTER_OPTIONAL POINTER_REQUIRED

PARAM_REQUIREMENT(node::EzXML.Node) = !haskey(node, "optional") || node["optional"] == "false" ? REQUIRED : PARAM_REQUIREMENT(findfirst(node["optional"] .== ["true", "false", "true,false", "false,true"]))

queue_maps = OrderedDict(
    "compute" => QueueCompute,
    "graphics" => QueueGraphics,
    "transfer" => QueueTransfer,
    "sparse_binding" => QueueSparseBinding,
)

function queue(x)
    x ∈ keys(queue_maps) && return queue_maps[x]()
    error("Unknown queue type $x")
end

abstract type RenderPassRequirement end
struct RenderPassInside <: RenderPassRequirement end
struct RenderPassOutside <: RenderPassRequirement end

function queue_compatibility(node)
    !haskey(node, "queues") && return nothing
    queues = split(node["queues"], ",")
    queue.(queues)
end

render_pass_compatibility(node) = (nr = node["renderpass"]; nr == "both" ? [RenderPassInside(), RenderPassOutside()] : nr == "inside" ? [RenderPassInside()] : nr == "outside" ? [RenderPassOutside()] : error("Unknown render pass compatibility"))

function externsync(node)
    !haskey(node, "externsync") && return false
    node["externsync"] ≠ "false"
end

function fetch_parameters()
    nodes = findall("//command[not(@name)]", xroot)
    df = DataFrame([String, String, String, Bool, Bool, PARAM_REQUIREMENT, Union{Nothing, String}, Array{String, 1}], [:parent, :name, :type, :constant, :externsync, :param_requirement, :len, :arglen])
    for node ∈ nodes
        for x ∈ findall("./param", node)
            push!(df, (parent_name(x), extract_identifier(x), extract_type(x), is_constant(x), externsync(x), PARAM_REQUIREMENT(x), len(x), arglen(x)))
        end
    end
    df
end

function fetch_struct_fields()
    nodes = findall("//type[@category='union' or @category='struct']", xroot)
    df = DataFrame([String, String, String, Bool, Bool, PARAM_REQUIREMENT, Union{Nothing, String}, Array{String, 1}], [:parent, :name, :type, :constant, :externsync, :param_requirement, :len, :arglen])
    for node ∈ nodes
        for x ∈ findall("./member", node)
            id = extract_identifier(x)
            push!(df, (parent_name(x), id == "module" ? "_module" : id, extract_type(x), is_constant(x), externsync(x), PARAM_REQUIREMENT(x), len(x), arglen(x, neighbor_type="member")))
        end
    end
    df
end

function len(node)
    haskey(node, "altlen") && return node["altlen"]
    val = getattr(node, "len")
    isnothing(val) && return val
    val_arr = filter(x -> x ≠ "null-terminated", split(val, ","))
    @assert length(val_arr) <= 1
    isempty(val_arr) ? nothing : first(val_arr)
end

function arglen(node; neighbor_type="param")
    neighbors = findall("../$neighbor_type", node)
    name = findfirst("./name", node).content
    arglens = String[]
    for node ∈ neighbors
        len(node) == name ? push!(arglens, findfirst("./name", node).content) : nothing
    end
    arglens
end

function fetch_types()
    nodes = findall("//type[@category and @category != 'include' and @category != 'define' and (@name or @category = 'handle' or @category='bitmask')]", xroot)
    df = DataFrame([String, String, Union{String, Nothing}], [:name, :category, :alias])
    for node ∈ nodes
        name = haskey(node, "name") ? node["name"] : findfirst("./name", node).content
        push!(df, (name, node["category"], getattr(node, "alias")))
    end
    df
end

function fetch_structs()
    nodes = findall("//type[(@category='union' or @category='struct')]", xroot)
    df = DataFrame([String, STRUCT_TYPE, Bool, Union{Nothing, Array{String, 1}}], [:name, :type, :returnedonly, :extends])
    for node ∈ nodes
        name = getattr(node, "name")
        @assert !isnothing(name) "Found the following structure without name:\n    $node"
        returnedonly = haskey(node, "returnedonly")
        type = returnedonly ? PROPERTY : occursin("CreateInfo", name) ? CREATE_INFO : occursin("AllocateInfo", name) ? ALLOCATE_INFO : occursin("Info", name) ? GENERIC_INFO : DATA
        push!(df, (name, type, returnedonly, (haskey(node, "structextends") ? split(node["structextends"], ",") : nothing)))
    end
    df
end

function is_struct_returnedonly(t)
    match = findfirst(x -> x == t, vulkan_structs.name)
    isnothing(match) ? false : vulkan_structs.returnedonly[match]
end

function fetch_handles()
    nodes = findall("//type[@category='handle' and not(@alias)]", xroot)
    df = DataFrame([String, Union{Nothing, String}, Bool], [:name, :parent, :dispatchable])
    for node ∈ nodes
        is_dispatchable = findfirst("./type", node).content == "VK_DEFINE_HANDLE"
        name = findfirst("./name", node).content
        push!(df, (name, getattr(node, "parent"), is_dispatchable))
    end
    df
end

function fetch_functions()
    nodes = findall("//command[not(@name) or (@name and @alias)]", xroot)
    df = DataFrame([String, Union{Nothing, COMMAND_TYPE}, Union{Nothing, String}, Union{Nothing, Array{RenderPassRequirement, 1}}, Union{Nothing, Array{QueueType, 1}}, Union{Nothing, String}], [:name, :type, :return_type, :render_pass_compatibility, :queue_compatibilty, :alias])
    for node ∈ nodes
        alias = getattr(node, "alias")
        name = !isnothing(alias) ? node["name"] : command_name(node)
        types = extract_type.(findall("./param", node), include_pointer=false)
        names = extract_identifier.(findall("./param", node))
        is_enum = any(is_struct_returnedonly(t) for t ∈ types) || any(is_count_to_be_filled.(names, name))
        queues = queue_compatibility(node)
        rp_reqs = getattr(node, "renderpass")
        if !isnothing(rp_reqs)
            rp_reqs = render_pass_compatibility(node)
        end
        ctype = findfirst(startswith.(name, ["vkCreate", "vkDestroy", "vkAllocate", "vkFree", "vkCmd"]))
        ctype = is_enum ? ENUMERATE : isnothing(ctype) ? nothing : COMMAND_TYPE(ctype)
        return_type = isnothing(alias) ? findfirst("./proto/type", node).content : nothing
        push!(df, (name, ctype, return_type, rp_reqs, queues, alias))
    end
    df
end

function fetch_creation_info()
    nodes = findall("//command/proto[contains(./child::name, 'vkCreate') or contains(./child::name, 'vkAllocate')]", xroot)
    df = DataFrame([String, String, String, Array{String, 1}, Array{String, 1}], [:name, :create_function, :identifier, :create_info_structs, :create_info_identifiers])
    vulkan_create_info_structs = (vulkan_structs |> @filter(_.type ∈ [CREATE_INFO, ALLOCATE_INFO]) |> DataFrame).name
    for node ∈ nodes
        create_el_node = findlast("../param", node)
        name = findfirst("./type", create_el_node).content
        create_fun = findfirst("./name", node).content
        create_info_nodes = filter!(x -> findfirst("./type", x).content ∈ vulkan_create_info_structs, findall("../param", node))
        create_info_structs = getproperty.(findfirst.("./type", create_info_nodes), :content)
        create_info_identifiers = getproperty.(findfirst.("./name", create_info_nodes), :content)
        identifier = findfirst("./name", create_el_node).content
        push!(df, (name, create_fun, identifier, create_info_structs, create_info_identifiers))
    end
    df
end

function fetch_destruction_info()
    nodes = findall("//command/proto[contains(./child::name, 'vkDestroy') or contains(./child::name, 'vkFree')]", xroot)
    df = DataFrame([String, String, String], [:name, :destroy_function, :identifier])
    for node ∈ nodes
        destroy_fun = findfirst("./name", node).content
        destroy_el_node = findlast("../param[@externsync]", node)
        name = findfirst("./type", destroy_el_node).content
        identifier = findfirst("./name", destroy_el_node).content
        push!(df, (name, destroy_fun, identifier))
    end
    df
end

is_command(name) = startswith(name, "vk")
is_struct(name) = startswith(name, "Vk")

info_df(sname) = is_command(sname) ? grouped_vulkan_params : grouped_vulkan_fields
info(name, sname) = dfmatch(info_df(sname)[(parent=sname,)], :name, name)

function is_count_to_be_filled(name, fname)
    group = info_df(fname)[(parent=fname,)]
    row = info(name, fname)
    !row.constant && row.param_requirement == POINTER_REQUIRED && any(name .== group.len)
end

const vulkan_params = fetch_parameters()
const vulkan_fields = fetch_struct_fields()
const grouped_vulkan_fields = groupby(vulkan_fields, :parent)
const grouped_vulkan_params = groupby(vulkan_params, :parent)
const vulkan_structs = fetch_structs()
const vulkan_functions = fetch_functions()
const vulkan_types = fetch_types()
const vulkan_handles = fetch_handles()
const vulkan_creation_info = fetch_creation_info()
const vulkan_destruction_info = fetch_destruction_info()

is_handle(type) = follow_alias(type) ∈ (vulkan_handles.name..., "HANDLE")
is_handle_with_create_info(type) = type ∈ vulkan_creation_info.name && !any(isempty.(getproperty.(dfmatches(vulkan_creation_info, :name, type), :create_info_structs)))
is_handle_destructible(type) = type ∈ vulkan_destruction_info.name
function is_handle_with_multiple_create_info(type)
    index = findall(vulkan_creation_info.name .== type)
    !isnothing(index) && length(index) > 1
end

is_category(type, cat) = type ∈ (vulkan_types |> @filter(_.category == cat) |> DataFrame).name
is_enum(type) = is_category(type, "enum")
is_bitmask(type) = is_category(type, "bitmask")

is_command_type(fname, val::COMMAND_TYPE) = dfmatch(vulkan_functions, :name, fname).type == val
is_array_variable(name, sname) = !isnothing(info(name, sname).len)
is_count_variable(name, sname) = !isempty(info(name, sname).arglen)

"""
    associated_array_variables(count_var_name, sname)

# Examples
```
julia> associated_array_variables("enabledLayerCount", "VkInstanceCreateInfo")
1-element Array{String,1}:
 "ppEnabledLayerNames"
```
"""
associated_array_variables(count_var_name, sname) = info(count_var_name, sname).arglen

"""
    associated_count_variable(array_var_name, sname)

# Examples
```
julia> associated_count_variable("ppEnabledLayerNames", "VkInstanceCreateInfo")
"enabledLayerCount"
```
"""
associated_count_variable(array_var_name, sname) = info(array_var_name, sname).len
function enumeration_command_count_variable(fname)
    group = grouped_vulkan_params[(parent=fname,)]
    index = findfirst(x -> is_count_to_be_filled(x, fname), group.name)
    isnothing(index) && error("$fname has no count argument to be filled")
    group[index, :]
end

function enumeration_command_array_variable(fname)
    group = grouped_vulkan_params[(parent=fname,)]
    arglen = first(enumeration_command_count_variable(fname).arglen)
    group[findfirst(x -> x == arglen, group.name), :]
end

is_optional_parameter(name, sname) = name == "pNext" || info(name, sname).param_requirement ∈ [OPTIONAL, POINTER_OPTIONAL]

function default(name, type)
    (is_handle(type) || startswith(name, "p") || startswith(type, "Ptr{")) && return "C_NULL"
    "0"
end
has_count_to_be_filled(fname) = any(is_count_to_be_filled(row.name, fname) for row ∈ eachrow(grouped_vulkan_params[(parent=fname,)]))


text:
# Julia wrapper for header: /usr/include/scip/cons_benderslp.h
# Automatically generated using Clang.jl wrap_c

function SCIPincludeConshdlrBenderslp(scip)
    ccall((:SCIPincludeConshdlrBenderslp, libscip), SCIP_RETCODE, (Ptr{SCIP_},), scip)
end


text:
# Pulse

mutable struct Pulse
  enabled::Bool
  channel::UInt8
  lengthEnabled::Bool
  lengthValue::UInt8
  timerPeriod::UInt16
  timerValue::UInt16
  dutyMode::UInt8
  dutyValue::UInt8
  sweepReload::Bool
  sweepEnabled::Bool
  sweepNegate::Bool
  sweepShift::UInt8
  sweepPeriod::UInt8
  sweepValue::UInt8
  envelopeEnabled::Bool
  envelopeLoop::Bool
  envelopeStart::Bool
  envelopePeriod::UInt8
  envelopeValue::UInt8
  envelopeVolume::UInt8
  constantVolume::UInt8

  Pulse(channel) = new(false, channel, false, 0, 0, 0, 0, 0, false, false,
                       false, 0, 0, 0, false, false, false, 0, 0, 0, 0)
end

const LengthTable = [
  0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
  0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
  0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
  0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
]

const DutyTable = [
  [0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00];
  [0x00 0x01 0x01 0x00 0x00 0x00 0x00 0x00];
  [0x00 0x01 0x01 0x01 0x01 0x00 0x00 0x00];
  [0x01 0x00 0x00 0x01 0x01 0x01 0x01 0x01]
]

function writecontrol!(pulse::Pulse, val::UInt8)
  pulse.dutyMode = val >> 6
  pulse.lengthEnabled = ((val >> 5) & 0x01) == 0x00
  pulse.envelopeLoop = ((val >> 5) & 0x01) == 0x01
  pulse.envelopeEnabled = ((val >> 4) & 0x01) == 0x00
  pulse.envelopePeriod = val & 0x0F
  pulse.constantVolume = val & 0x0F
  pulse.envelopeStart = true
end

function writesweep!(pulse::Pulse, val::UInt8)
  pulse.sweepEnabled = (val >> 7) == 0x01
  pulse.sweepPeriod = ((val >> 4) & 0x07) + 0x01
  pulse.sweepNegate = ((val >> 3) & 0x01) == 0x01
  pulse.sweepShift = val & 0x07
  pulse.sweepReload = true
end

function writetimerlow!(pulse::Pulse, val::UInt8)
  pulse.timerPeriod = (pulse.timerPeriod & 0xFF00) | val
end

function writetimerhigh!(pulse::Pulse, val::UInt8)
  pulse.lengthValue = LengthTable[(val >> 3) + 1]
  pulse.timerPeriod = (pulse.timerPeriod & 0x00FF) | (UInt16(val & 0x07) << 8)
  pulse.envelopeStart = true
  pulse.dutyValue = 0
end

function steptimer!(pulse::Pulse)
  if pulse.timerValue == 0
    pulse.timerValue = pulse.timerPeriod
    pulse.dutyValue = (pulse.dutyValue + 0x01) & 0x07
  else
    pulse.timerValue -= 1
  end
end

function stepenvelope!(pulse::Pulse)
  if pulse.envelopeStart
    pulse.envelopeVolume = 15
    pulse.envelopeValue = pulse.envelopePeriod
    pulse.envelopeStart = false
  elseif pulse.envelopeValue > 0
    pulse.envelopeValue -= 1
  else
    if pulse.envelopeVolume > 0
      pulse.envelopeVolume -= 1
    elseif pulse.envelopeLoop
      pulse.envelopeVolume = 15
    end
    pulse.envelopeValue = pulse.envelopePeriod
  end
end

function stepsweep!(pulse::Pulse)
  if pulse.sweepReload
    if pulse.sweepEnabled && pulse.sweepValue == 0
      sweep!(pulse)
    end
    pulse.sweepValue = pulse.sweepPeriod
    pulse.sweepReload = false
  elseif pulse.sweepValue > 0
    pulse.sweepValue -= 1
  else
    if pulse.sweepEnabled
      sweep!(pulse)
    end
    pulse.sweepValue = pulse.sweepPeriod
  end
end

function steplength!(pulse::Pulse)
  if pulse.lengthEnabled && pulse.lengthValue > 0
    pulse.lengthValue -= 1
  end
end

function sweep!(pulse::Pulse)
  delta = pulse.timerPeriod >> pulse.sweepShift
  if pulse.sweepNegate
    pulse.timerPeriod -= delta
    if pulse.channel == 1
      pulse.timerPeriod -= 1
    end
  else
    pulse.timerPeriod += delta
  end
end

function output!(pulse::Pulse)::UInt8
  if !pulse.enabled
    return 0x00
  end
  if pulse.lengthValue == 0
    return 0x00
  end
  if DutyTable[pulse.dutyMode + 1, pulse.dutyValue + 1] == 0
    return 0x00
  end
  if pulse.timerPeriod < 8 || pulse.timerPeriod > 0x7FF
    return 0x00
  end
  # if !sweepNegate && timerPeriod+(timerPeriod>>sweepShift) > 0x7FF
  #   return 0
  # }
  if pulse.envelopeEnabled
    return pulse.envelopeVolume
  else
    return pulse.constantVolume
  end
end

# Triangle

mutable struct Triangle
  enabled::Bool
  lengthEnabled::Bool
  lengthValue::UInt8
  timerPeriod::UInt16
  timerValue::UInt16
  dutyValue::UInt8
  counterPeriod::UInt8
  counterValue::UInt8
  counterReload::Bool

  Triangle() = new(false, false, 0, 0, 0, 0, 0, 0, false)
end

const TriangleTable = [
  0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
  0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
]

function writecontrol!(triangle::Triangle, val::UInt8)
  triangle.lengthEnabled = ((val >> 7) & 0x01) == 0x00
  triangle.counterPeriod = val & 0x7F
end

function writetimerlow!(triangle::Triangle, val::UInt8)
  triangle.timerPeriod = (triangle.timerPeriod & 0xFF00) | val
end

function writetimerhigh!(triangle::Triangle, val::UInt8)
  triangle.lengthValue = LengthTable[(val >> 3) + 1]
  triangle.timerPeriod = (triangle.timerPeriod & 0x00FF) | (UInt16(val & 0x07) << 8)
  triangle.timerValue = triangle.timerPeriod
  triangle.counterReload = true
end

function steptimer!(triangle::Triangle)
  if triangle.timerValue == 0
    triangle.timerValue = triangle.timerPeriod
    if triangle.lengthValue > 0 && triangle.counterValue > 0
      triangle.dutyValue = (triangle.dutyValue + 0x01) & 0x1F
    end
  else
    triangle.timerValue -= 1
  end
end

function steplength!(triangle::Triangle)
  if triangle.lengthEnabled && triangle.lengthValue > 0
    triangle.lengthValue -= 1
  end
end

function stepcounter!(triangle::Triangle)
  if triangle.counterReload
    triangle.counterValue = triangle.counterPeriod
  elseif triangle.counterValue > 0
    triangle.counterValue -= 1
  end
  if triangle.lengthEnabled
    triangle.counterReload = false
  end
end

function output!(triangle::Triangle)::UInt8
  if !triangle.enabled
    return 0x00
  end
  if triangle.lengthValue == 0
    return 0x00
  end
  if triangle.counterValue == 0
    return 0x00
  end
  TriangleTable[triangle.dutyValue + 1]
end

# Noise

mutable struct Noise
  enabled::Bool
  mode::Bool
  shiftRegister::UInt16
  lengthEnabled::Bool
  lengthValue::UInt8
  timerPeriod::UInt16
  timerValue::UInt16
  envelopeEnabled::Bool
  envelopeLoop::Bool
  envelopeStart::Bool
  envelopePeriod::UInt8
  envelopeValue::UInt8
  envelopeVolume::UInt8
  constantVolume::UInt8

  Noise() = new(false, false, 1, false, 0, 0, 0, false, false, false, 0, 0,
                  0, 0)
end

const NoiseTable = [
  0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0,
  0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4
]

function writecontrol!(noise::Noise, val::UInt8)
  noise.lengthEnabled = ((val >> 5) & 0x01) == 0x00
  noise.envelopeLoop = ((val >> 5) & 0x01) == 0x01
  noise.envelopeEnabled = ((val >> 4) & 0x01) == 0x00
  noise.envelopePeriod = val & 0x0F
  noise.constantVolume = val & 0x0F
  noise.envelopeStart = true
end

function writeperiod!(noise::Noise, val::UInt8)
  noise.mode = (val & 0x80) == 0x80
  noise.timerPeriod = NoiseTable[(val & 0x0F) + 1]
end

function writelength!(noise::Noise, val::UInt8)
  noise.lengthValue = LengthTable[(val >> 3) + 1]
  noise.envelopeStart = true
end

function steptimer!(noise::Noise)
  if noise.timerValue == 0
    noise.timerValue = noise.timerPeriod
    shift = noise.mode ? 0x06 : 0x01
    b1 = noise.shiftRegister & 0x01
    b2 = (noise.shiftRegister >> shift) & 0x01
    noise.shiftRegister >>= 1
    noise.shiftRegister |= (b1 ⊻ b2) << 14
  else
    noise.timerValue -= 1
  end
end

function stepenvelope!(noise::Noise)
  if noise.envelopeStart
    noise.envelopeVolume = 15
    noise.envelopeValue = noise.envelopePeriod
    noise.envelopeStart = false
  elseif noise.envelopeValue > 0
    noise.envelopeValue -= 1
  else
    if noise.envelopeVolume > 0
      noise.envelopeVolume -= 1
    elseif noise.envelopeLoop
      noise.envelopeVolume = 15
    end
    noise.envelopeValue = noise.envelopePeriod
  end
end

function steplength!(noise::Noise)
  if noise.lengthEnabled && noise.lengthValue > 0
    noise.lengthValue -= 1
  end
end

function output!(noise::Noise)::UInt8
  if !noise.enabled
    return 0x00
  end
  if noise.lengthValue == 0
    return 0x00
  end
  if (noise.shiftRegister & 0x01) == 0x01
    return 0x00
  end
  if noise.envelopeEnabled
    noise.envelopeVolume
  else
    noise.constantVolume
  end
end

mutable struct DMC
  enabled::Bool
  value::UInt8
  sampleAddress::UInt16
  sampleLength::UInt16
  currentAddress::UInt16
  currentLength::UInt16
  shiftRegister::UInt8
  bitCount::UInt8
  tickPeriod::UInt8
  tickValue::UInt8
  loop::Bool
  irq::Bool

  DMC() = new(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, false)
end

const DMCTable = [
  0xD6, 0xBE, 0xAA, 0xA0, 0x8F, 0x7F, 0x71, 0x6B,
  0x5F, 0x50, 0x47, 0x40, 0x35, 0x2A, 0x24, 0x1B
]

function writecontrol!(dmc::DMC, val::UInt8)
  dmc.irq = (val & 0x80) == 0x80
  dmc.loop = (val & 0x40) == 0x40
  dmc.tickPeriod = DMCTable[(val & 0x0F) + 1]
end

function writevalue!(dmc::DMC, val::UInt8)
  dmc.value = val & 0x7F
end

function writeaddress!(dmc::DMC, val::UInt8)
  # Sample address = %11AAAAAA.AA000000
  dmc.sampleAddress = 0xC000 | (UInt16(val) << 6)
end

function writelength!(dmc::DMC, val::UInt8)
  # Sample length = %0000LLLL.LLLL0001
  dmc.sampleLength = (UInt16(val) << 4) | 0x01
end

function restart!(dmc::DMC)
  dmc.currentAddress = dmc.sampleAddress
  dmc.currentLength = dmc.sampleLength
end

function stepshifter!(dmc::DMC)
  if dmc.bitCount == 0
    return
  end
  if (dmc.shiftRegister & 0x01) == 0x01
    if dmc.value <= 125
      dmc.value += 2
    end
  else
    if dmc.value >= 2
      dmc.value -= 2
    end
  end
  dmc.shiftRegister >>= 1
  dmc.bitCount -= 1
end

function output!(dmc::DMC)::UInt8
  dmc.value
end

# Filter

mutable struct Filter
  B0::Float32
  B1::Float32
  A1::Float32
  prevX::Float32
  prevY::Float32

  Filter(b0::Float32, b1::Float32, a1::Float32) = new(b0, b1, a1, 0.0, 0.0)
  Filter() = Filter(0.0f0, 0.0f0, 0.0f0)
end

function highpassfilter(sampleRate::Float32, cutoffFreq::Float32)::Filter
  c = sampleRate / π / cutoffFreq
  a0i = 1.0f0 / (1.0f0 + c)
  Filter(c * a0i, -c * a0i, (1.0f0 - c) * a0i)
end

function lowpassfilter(sampleRate::Float32, cutoffFreq::Float32)::Filter
  c = sampleRate / π / cutoffFreq
  a0i = 1.0f0 / (1.0f0 + c)
  Filter(a0i, a0i, (1.0f0 - c) * a0i)
end

function step!(f::Filter, x::Float32)::Float32
  y = f.B0 * x + f.B1 * f.prevX - f.A1 * f.prevY
  f.prevY = y
  f.prevX = x
  y
end

# FilterChain

mutable struct FilterChain
  filter1::Filter
  filter2::Filter
  filter3::Filter
  initialized::Bool

  FilterChain() = new(Filter(), Filter(), Filter(), false)
end

function setsamplerate!(fc::FilterChain, sampleRate::Float32)
  if sampleRate == 0.0f0
    fc.filter1 = Filter()
    fc.filter2 = Filter()
    fc.filter3 = Filter()
    fc.initialized = false
  else
    fc.filter1 = highpassfilter(sampleRate, 90.0f0)
    fc.filter2 = highpassfilter(sampleRate, 440.0f0)
    fc.filter3 = lowpassfilter(sampleRate, 14000.0f0)
    fc.initialized = true
  end
end

function step!(fc::FilterChain, x::Float32)::Float32
  if fc.initialized
    x = step!(fc.filter1, x)
    x = step!(fc.filter2, x)
    x = step!(fc.filter3, x)
  end
  x
end

# AudioChannel

mutable struct AudioChannel
  values::Vector{Float32}
  position::UInt32

  AudioChannel(length::Integer) = new(zeros(Float32, length), 0)
end

function write!(ac::AudioChannel, val::Float32)
  if ac.position < length(ac.values)
    ac.values[ac.position + 1] = val
    ac.position += 1
  end
end

function reset!(ac::AudioChannel)
  ac.position = 0
end


text:
"""
    select(
        bibliography::DataStructures.OrderedDict{String,Entry},
        selection::Vector{String};
        complementary::Bool = false
        )
Select a part of a bibliography based on a given selection set of keys. If complementary is true, selection designates which entries will not be kept. By default, complementary is set to false.
"""
function select(
    bibliography::DataStructures.OrderedDict{String,Entry},
    selection::Vector{String};
    complementary::Bool=false,
)
    selected_bib = DataStructures.OrderedDict{String,Entry}()
    old_keys = keys(bibliography)
    new_keys = complementary ? setdiff(old_keys, selection) : intersect(old_keys, selection)

    for key in new_keys
        selected_bib[key] = bibliography[key]
    end

    return selected_bib
end


text:
mutable struct Heuristic

    alloc::Bool
    ptr::Ptr{Void}
    net::Network

    function Function(a, ptr, n)
        this = new(a, ptr, n)
        finalizer(this, dealloc)
        this
    end
end

function Heuristic(name::String, net::Network)

    if name == "PVPQ switching"
        Function(true, ccall((:HEUR_PVPQ_new, libpfnet), Ptr{Void}, (Ptr{Void},), net.ptr), net)
    else
        throw(ArgumentError("invalid heuristic name"))
    end
end

function dealloc(heur::Heuristic)
    if heur.alloc
        ccall((:HEUR_del, libpfnet), Void, (Ptr{Void},), heur.ptr)
    end
    heur.alloc = false
    heur.ptr = C_NULL
end


text:
# @syms 𝑛 taylor(x,a,eq) b(n) D(x,eq)

is_taylor(x) = istree(x) && (operation(x)==taylor || any(is_taylor.(arguments(x))))
is_not_taylor(x) = !is_taylor(x)

taylor(x, 𝑛, a₀, eq) = a₀ + sum([x^i * substitute(eq, Dict(𝑛 => Float64(i))) for i=1:5])

tay_sum1 = @acrule taylor(~x,~a1,~eq1) + taylor(~x,~a2,~eq2) => taylor(~x, ~a1 + ~a2,~eq1 + ~eq2)
tay_sum2 = @acrule ~z::is_not_taylor + taylor(~x,~a,~eq) => taylor(~x,~z + ~a,~eq)
tay_sum3 = @acrule taylor(~x,~a1,~eq1) + ~u2::is_not_taylor * taylor(~x,~a2,~eq2) => taylor(~x,~a1 + ~a2*~u2,~eq1 + ~eq2*~u2)
tay_sum4 = @acrule ~u1::is_not_taylor * taylor(~x,~a1,~eq1) + ~u2::is_not_taylor * taylor(~x,~a2,~eq2) => taylor(~x,~a1*~u1 + ~a2*~u2,~eq1*~u1 + ~eq2*~u2)
tay_mul = @acrule ~z::is_not_taylor * taylor(~x,~a,~eq) => taylor(~x, ~a*~z,~eq*~z)
tay_div = @acrule taylor(~x,~a,~eq) / ~z::is_not_taylor => taylor(~x, ~a/~z,~eq/~z)

tay_diff1 = @rule D(~x, taylor(~x,~a,~eq)) => taylor(~x,D(~x,~a) + substitute(~eq, Dict(𝑛 => 1)), substitute(𝑛 * ~eq, Dict(𝑛 => 𝑛+1)))
tay_diff2 = @rule D(~x, b(~n)) => 0
tay_diff3 = @rule D(~x, ~y::is_not_taylor) => Differential(~x)(~y)

tay_rules = Chain([tay_mul, tay_div, tay_sum1, tay_sum2, tay_sum3, tay_sum4])
tay_diffs = Chain([tay_diff1, tay_diff2, tay_diff3])

function simplify_taylor(eq, order)
    w = z
    for i = 1:order
        w = Prewalk(PassThrough(tay_diffs))(w)
        w = expand_derivatives(w)
        w = Fixpoint(tay_rules)(w)
    end
    w
end

function realize_taylor(t, N)
    if istree(t) && operation(t)==taylor
        x, a, eq = arguments(t)

        eqs = [substitute(a, Dict(𝑛 => 0))]
        for i=1:N
            push!(eqs, substitute(eq, Dict(𝑛 => i)))
        end

        subs = Dict(b(i) => c[i+1] for i=0:10)
        eqs = [substitute(eq, subs) for eq in eqs]

        return eqs
    end
end

function apply_initial_values(eqs, u0)
    [substitute(eq,u0) for eq in eqs]
end

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

@syms BAdd(x,y) BMul(x,y)

to_BAdd(x) = x
to_BAdd(x, y...) = BAdd(x, to_BAdd(y...))

to_BMul(x) = x
to_BMul(x, y...) = BMul(x, to_BMul(y...))

BAdd_rule = @rule +(~~xs) => to_BAdd(~~xs...)
BMul_rule = @rule *(~~xs) => to_BMul(~~xs...)

binary_op_rules = Chain([BAdd_rule, BMul_rule])

function convert_binary_op(x)
    x = Prewalk(PassThrough(BMul_rule))(x)
    Prewalk(PassThrough(BAdd_rule))(x)
end

iverson(x, y) = exp(-(x-y)^2/1e-8)

sum_rule1 = @rule BAdd(taylor(~x,~a1,~eq1), taylor(~x,~a2,~eq2)) => taylor(~x, ~a1 + ~a2, ~eq1 + ~eq2)
sum_rule2 = @rule BAdd(taylor(~x,~a,~eq), ~u::is_not_taylor) => taylor(~x, ~a + ~u, ~eq)
sum_rule3 = @rule BAdd(~u::is_not_taylor, taylor(~x,~a,~eq)) => taylor(~x, ~a + ~u, ~eq)
mul_rule1 = @rule BMul(taylor(~x,~a,~eq), ~u::is_not_taylor) => taylor(~x, ~a * ~u, ~eq * ~u)
mul_rule2 = @rule BMul(~u::is_not_taylor, taylor(~x,~a,~eq)) => taylor(~x, ~a * ~u, ~eq * ~u)
div_rule = @rule taylor(~x,~a,~eq) / ~u::is_not_taylor => taylor(~x, ~a / ~u, ~eq / ~u)
neg_rule = @rule -taylor(~x,~a,~eq) => taylor(~x, -~a, ~eq)

tay_diff1 = @rule D(~x, taylor(~x,~a,~eq)) => taylor(~x,D(~x,~a) + substitute(~eq, Dict(𝑛 => 1)), substitute(𝑛 * ~eq, Dict(𝑛 => 𝑛+1)))
tay_diff2 = @rule D(~x, b(~n)) => 0
tay_diff3 = @rule D(~x, ~y::is_not_taylor) => Differential(~x)(~y)

tay_rules = Chain([sum_rule1, sum_rule2, sum_rule3, mul_rule1, mul_rule2, div_rule, neg_rule])
tay_diffs = Chain([tay_diff1, tay_diff2, tay_diff3])

function simplify_taylor(eq, order)
    w = z
    for i = 1:order
        w = Prewalk(PassThrough(tay_diffs))(w)
        w = expand_derivatives(w)
    end
    for i = 1:order
        w = Prewalk(PassThrough(tay_rules))(w)
    end
    w
end

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

diff_vars(n) = diff_vars(n, 0)

function diff_vars(n, x₀)
    y = def_taylor(x - x₀, a[2:n], a[1])
    D = Differential(x)
    x, y, D, n
end

function eqs_linear_diffeq(p, eq)
    eq = expand_derivatives(eq)
    collect_powers(eq, p.x, 0:p.n)
end

function solve_linear_diffeq(p, eq, order)
    eqs = eqs_linear_diffeq(p, eq)
    vars = solve_coef(eqs, a[order+1:p.n])
    return vars
end

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

@syms G(x)

g_rule1 = @rule G(~x) + G(~y) => G(~x + ~y)
g_rule2 = @acrule G(~x) + ~y => G(~x + ~y)
g_rule3 = @rule G(~x) * G(~y) => G(~x * ~y)
g_rule4 = @acrule G(~x) * ~y => G(~x * ~y)
g_rule5 = @rule G(~x) / G(~y) => G(~x // ~y)
g_rule6 = @rule G(~x) / ~y => G(~x // ~y)
g_rule7 = @rule ~x / G(~y) => G(~x // ~y)
g_rule8 = @rule ^(G(~x), ~y) => G(^(~x, ~y))
g_rule9 = @rule ^(G(~x), G(~y)) => G(^(~x, ~y))

g_rules = Chain([
    g_rule1,
    g_rule2,
    g_rule3,
    g_rule4,
    g_rule5,
    g_rule6,
    g_rule7,
    g_rule8,
    g_rule9,
])

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


text:
mutable struct NothingExerciseValue <: MarketModelExerciseValue
  numberOfExercises::Int
  rateTimes::Vector{Float64}
  isExerciseTime::BitArray{1}
  evolution::EvolutionDescription
  currentIndex::Int
  cf::MarketModelCashFlow
end

function NothingExerciseValue(rateTimes::Vector{Float64}, isExerciseTime::BitArray{1} = BitArray{1}())
  currentIndex = 1
  check_increasing_times(rateTimes)
  length(rateTimes) >= 2 || error("Rate times must contain at least two values")

  cf = MarketModelCashFlow(-1, 0.0)
  evolutionTimes = rateTimes[1:end-1]
  evolution = EvolutionDescription(rateTimes, evolutionTimes)

  if isempty(isExerciseTime)
    isExerciseTime = trues(isempty(rateTimes) ? 0 : length(rateTimes) - 1)
  else
    length(isExerciseTime) == isempty(rateTimes) ? 0 : length(rateTimes) - 1 || error("isExerciseTime must have same size as rateTimes minus 1")
  end

  numberOfExercises = 0
  @inbounds @simd for i in eachindex(isExerciseTime)
    if isExerciseTime[i]
      numberOfExercises += 1
    end
  end

  return NothingExerciseValue(numberOfExercises, rateTimes, isExerciseTime, evolution, currentIndex, cf)
end

possible_cash_flow_times(nev::NothingExerciseValue) = nev.rateTimes
get_value(nev::NothingExerciseValue, ::CurveState) = clone(nev.cf)
reset!(nev::NothingExerciseValue) = nev.currentIndex = 1

function next_step!(nev::NothingExerciseValue, ::CurveState)
  nev.cf.timeIndex = nev.currentIndex
  nev.currentIndex += 1

  return nev
end

clone(nev::NothingExerciseValue) = NothingExerciseValue(nev.numberOfExercises, copy(nev.rateTimes), copy(nev.isExerciseTime), clone(nev.evolution),
                                    nev.currentIndex, clone(nev.cf))


text:
"""
    plan_prioritized_paths!(env::CoordinatedMAPFEnv, agent_ordering::Vector{Int64}, initial_states::Vector{MS}, cnr::Type{CNR}, pr::Type{PR})

Run the Prioritized Planning algorithm. Given an ordering over agents, plan a path for each of them
one-by-one and impose constraints derived from the path on subsequent agents.
"""
function plan_prioritized_paths!(env::CoordinatedMAPFEnv, agent_ordering::Vector{Int64}, initial_states::Vector{MS}, cnr::Type{CNR}, pr::Type{PR}) where {MS <: MAPFState, CNR <: MAPFConstraints, PR <: PlanResult}

    running_constraint = MultiAgentPathFinding.get_empty_constraint(cnr)
    paths = pr[]

    # Iterate over agent ordering
    for id in agent_ordering

        # Run low level search for each agent
        id_path = low_level_pp_search!(env, id, initial_states[id], running_constraint)

        # Update constraints
        add_constraint_from_pp_search!(env, running_constraint, id, id_path)

        # Add to path set
        push!(paths, id_path)

    end # id in agent_ordering
    return paths
end

## Just implement the various functions for aerial and ground here
function low_level_pp_search!(env::CoordinatedMAPFEnv, ground_agent_id::Int64, s::GroundMAPFState, constraint::GroundMAPFConstraint)

    env.ground_mapf_graph = SimpleVListGraph{GroundMAPFState}()
    empty!(env.unique_gmapf_states)

    Graphs.add_vertex!(env.ground_mapf_graph, s)
    env.unique_gmapf_states[s] = GMAPFStateInfo(1, 0.0, Set{Int64}())

    road_vtx_goal = env.ground_task_list[ground_agent_id].dest
    admissible_heuristic(u) = ground_mapf_heuristic(env, road_vtx_goal, u)
    edge_wt_fn(u, v) = get_ground_mapf_edge_weight(env, u, v)

    vis = GroundMAPFGoalVisitor(env, road_vtx_goal, constraint.avoid_edge_copy_set)

    start_idx = 1
    env.next_gmapfg_goal_idx = 0

    a_star_states = a_star_implicit_shortest_path!(env.ground_mapf_graph, edge_wt_fn, start_idx, vis, admissible_heuristic)

    @assert haskey(a_star_states.parent_indices, env.next_gmapfg_goal_idx)

    states = Tuple{GroundMAPFState,Float64}[]
    actions = Tuple{GroundMAPFAction,Float64}[]

    sp_cost = a_star_states.dists[env.next_gmapfg_goal_idx]

    sp_idxs = shortest_path_indices(a_star_states.parent_indices, env.ground_mapf_graph, start_idx, env.next_gmapfg_goal_idx)

    push!(states, (s, 0.0))

    for i = 2:length(sp_idxs)

        u_idx, v_idx =  (sp_idxs[i-1], sp_idxs[i])

        new_state = deepcopy(env.ground_mapf_graph.vertices[v_idx])
        push!(states, (new_state, a_star_states.dists[v_idx]))

        push!(actions, (get_mapf_ground_action(env, env.ground_mapf_graph.vertices[u_idx], env.ground_mapf_graph.vertices[v_idx]), a_star_states.dists[v_idx] - a_star_states.dists[u_idx]))
    end

    return PlanResult{GroundMAPFState,GroundMAPFAction,Float64}(states, actions, sp_cost, sp_cost)
end

function add_constraint_from_pp_search!(env::CoordinatedMAPFEnv, constraint::GroundMAPFConstraint, ground_id::Int64, id_path::PlanResult)

    for (id_action, _) in id_path.actions

        # Just add the aerial IDs for the edge
        if !isempty(id_action.edge_aerial_ids)
            if ~haskey(constraint.avoid_edge_copy_set, id_action.edge_id)
                constraint.avoid_edge_copy_set[id_action.edge_id] = Set{Int64}()
            end
            union!(constraint.avoid_edge_copy_set[id_action.edge_id], id_action.edge_aerial_ids)
        end
    end
end

function low_level_pp_search!(env::CoordinatedMAPFEnv, aerial_agent_id::Int64, s::AerialMAPFState, constraint::GroundTransitConstraint)

    env.aerial_mapf_graph = SimpleVListGraph{AerialMAPFState}()
    Graphs.add_vertex!(env.aerial_mapf_graph, s)

    env.gtg_idx_gval =  Dict{Int64,Float64}()

    edge_wt_fn(u, v) = amapfg_edge_wt_fn(env, u, v)
    admissible_heuristic(u) = direct_flight_time_heuristic(env, u)

    # Set start and goal
    start_idx = 1   # Always one since graph reinitialized
    env.next_gtg_goal_idx = env.gtg_drone_start_goal_idxs[aerial_agent_id][2]
    env.next_amapfg_goal_idx = 0

    vis = GroundTransitGoalVisitor(env, constraint.avoid_gid_vertex_set)

    a_star_states = a_star_implicit_shortest_path!(env.aerial_mapf_graph,
                                                    edge_wt_fn,
                                                    start_idx,
                                                    vis,
                                                    admissible_heuristic
                                                    )

    # Get solution from a_star_eps_states
    # NOTE: It should always have a solution because drone can just fly
    @assert haskey(a_star_states.parent_indices, env.next_amapfg_goal_idx)

    # Get path cost
    states = Tuple{AerialMAPFState,Float64}[]
    actions = Tuple{GroundTransitAction, Float64}[]

    sp_cost = a_star_states.dists[env.next_amapfg_goal_idx]

    # Get path
    sp_idxs = shortest_path_indices(a_star_states.parent_indices, env.aerial_mapf_graph, start_idx, env.next_amapfg_goal_idx)

    push!(states, (s, 0.0))

    # Traverse sp_idxs and set up states and actions
    for i = 2:length(sp_idxs)

        u, v =  (sp_idxs[i-1], sp_idxs[i])

        # NOTE: Create new state because we are going to delete graph later
        new_state = deepcopy(env.aerial_mapf_graph.vertices[v])
        push!(states, (new_state, a_star_states.dists[v]))

        # All derived info but can be useful
        push!(actions, (get_mapf_action(env, u, v), a_star_states.dists[v] - a_star_states.dists[u]))
    end

    return PlanResult{AerialMAPFState,GroundTransitAction,Float64}(states, actions, sp_cost, sp_cost)
end

function add_constraint_from_pp_search!(env::CoordinatedMAPFEnv, constraint::GroundTransitConstraint, aerial_id::Int64, id_path::PlanResult)

    for ((si, _), (sip, _)) in zip(id_path.states[1:end-1], id_path.states[2:end])
        si_gtg = env.ground_transit_graph.vertices[si.ground_transit_idx]
        sip_gtg = env.ground_transit_graph.vertices[sip.ground_transit_idx]
        if si_gtg.ground_agent_id !=0 && si_gtg.ground_agent_id == sip_gtg.ground_agent_id
            if ~haskey(env.gtg_idx_to_aerial_ids, si_gtg.idx)
                env.gtg_idx_to_aerial_ids[si_gtg.idx] = Int64[]
            end
            push!(env.gtg_idx_to_aerial_ids[si_gtg.idx], aerial_id)
        end
    end

    for (gtgidx, aerial_ids) in env.gtg_idx_to_aerial_ids
        if length(aerial_ids) > env.car_capacity
            gid = env.ground_transit_graph.vertices[gtgidx].ground_agent_id
            union!(constraint.avoid_gid_vertex_set, gid)
        end
    end

end

struct GroundTransitVertexGoalVisitor <: Graphs.AbstractDijkstraVisitor
    env::CoordinatedMAPFEnv
    avoid_gtg_vertex_set::Set{Int64}
end

function Graphs.include_vertex!(vis::GroundTransitVertexGoalVisitor, u::AerialMAPFState, v::AerialMAPFState, d::Float64, nbrs::Vector{Int64})

    env = vis.env
    avoid_gtg_vertex_set = vis.avoid_gtg_vertex_set
    gtg_weights = env.ground_transit_weights

    # When you expand goal, return
    if v.ground_transit_idx == env.next_gtg_goal_idx
        env.next_amapfg_goal_idx = v.idx
        return false
    end

    # Now lookup where drone is on the GTG
    ugtg = env.ground_transit_graph.vertices[u.ground_transit_idx]
    vgtg = env.ground_transit_graph.vertices[v.ground_transit_idx]
    idx_ctr = Graphs.num_vertices(env.aerial_mapf_graph)

    # Case 1: Drone start
    if vgtg.ground_agent_id == 0

        ddiff = gtg_weights[(v.ground_transit_idx, env.next_gtg_goal_idx)]
        tdiff = ddiff / AvgSpeed

        idx_ctr += 1
        new_goal_vert = AerialMAPFState(idx=idx_ctr, ground_transit_idx=env.next_gtg_goal_idx, timeval=v.timeval+tdiff, distval=v.distval+ddiff)

        # Add to graph and add neighbor
        Graphs.add_vertex!(env.aerial_mapf_graph, new_goal_vert)
        push!(nbrs, idx_ctr)

        env.gtg_idx_gval[env.next_gtg_goal_idx] = env.alpha_weight_distance*new_goal_vert.distval + (1.0 - env.alpha_weight_distance)*new_goal_vert.timeval

        # Now add other car connections while accounting for time
        # First get non-dominated gtg indices and then add aerial mapf state versions of them
        # For timeval use the max of gtg time and drone timeval
        for (_, (gstart, gend)) in enumerate(env.gtg_ground_path_start_ends)
            gts_non_dom = get_non_dominated_transit_points(env, gstart, gend, avoid_gtg_vertex_set, v, false)

            # Add AerialMAPFState versions of them
            for gts_tup in gts_non_dom

                idx_ctr += 1
                new_vert = AerialMAPFState(idx=idx_ctr, ground_transit_idx=gts_tup.idx, timeval=gts_tup.timeval, distval=gts_tup.distval)

                Graphs.add_vertex!(env.aerial_mapf_graph, new_vert)
                push!(nbrs, idx_ctr)

                env.gtg_idx_gval[gts_tup.idx] = env.alpha_weight_distance*new_vert.distval + (1.0 - env.alpha_weight_distance)*new_vert.timeval
            end
        end

    else
        # Either first transfer to new ground point OR along the way
        if vgtg.ground_agent_id != ugtg.ground_agent_id

            new_considered_cars = union(v.considered_cars, Set{Int64}([vgtg.ground_agent_id]))

            next_gtg = env.ground_transit_graph.vertices[vgtg.idx + 1]

            idx_ctr += 1
            # IMP NOTE: Timeval may be greater than vgtg.timeval (probably will be)
            # So the new timeval should be v.timeval + car_time_diff (next_gtg.timeval - vgtg.timeval)
            tval = v.timeval + (next_gtg.timeval - vgtg.timeval)
            new_vert = AerialMAPFState(idx=idx_ctr, ground_transit_idx=next_gtg.idx, timeval=tval, distval=v.distval, considered_cars=new_considered_cars)

            Graphs.add_vertex!(env.aerial_mapf_graph, new_vert)
            push!(nbrs, idx_ctr)

        else
            # Ongoing car route vertex. First try to add goal as usual
            ddiff = gtg_weights[(v.ground_transit_idx, env.next_gtg_goal_idx)]
            tdiff = ddiff / AvgSpeed
            goal_gval = env.alpha_weight_distance*(v.distval+ddiff) + (1.0 - env.alpha_weight_distance)*(v.timeval + tdiff)

            if goal_gval < env.gtg_idx_gval[env.next_gtg_goal_idx]

                idx_ctr += 1
                new_goal_vert = AerialMAPFState(idx=idx_ctr, ground_transit_idx=env.next_gtg_goal_idx, timeval=v.timeval+tdiff, distval=v.distval+ddiff, considered_cars=v.considered_cars)

                # Add to graph and add neighbor
                Graphs.add_vertex!(env.aerial_mapf_graph, new_goal_vert)
                push!(nbrs, idx_ctr)

                env.gtg_idx_gval[env.next_gtg_goal_idx] = goal_gval
            end

            # Then add next along car route IF not last
            if vgtg.idx < env.gtg_ground_path_start_ends[vgtg.ground_agent_id][2]
                next_gtg = env.ground_transit_graph.vertices[vgtg.idx + 1]
                # @assert next_gtg.ground_agent_id == vgtg.ground_agent_id && next_gtg.vertex_along_path == vgtg.vertex_along_path+1

                idx_ctr += 1
                # IMP NOTE: Timeval may be greater than vgtg.timeval (probably will be)
                # So the new timeval should be v.timeval + car_time_diff (next_gtg.timeval - vgtg.timeval)
                tval = v.timeval + (next_gtg.timeval - vgtg.timeval)
                new_vert = AerialMAPFState(idx=idx_ctr, ground_transit_idx=next_gtg.idx, timeval=tval, distval=v.distval, considered_cars=v.considered_cars)

                Graphs.add_vertex!(env.aerial_mapf_graph, new_vert)
                push!(nbrs, idx_ctr)

            end

            for (gid, (gstart, gend)) in enumerate(env.gtg_ground_path_start_ends)

                if gid != vgtg.ground_agent_id && ~(gid in v.considered_cars)
                    gts_non_dom = get_non_dominated_transit_points(env, gstart, gend, avoid_gtg_vertex_set, v, true)

                    # Add AerialMAPFState versions of them
                    for gts_tup in gts_non_dom

                        # At least here compute the real thing, the true SP between transit connections
                        # bidir_sp = compute_bidir_astar_euclidean(env.road_graph, vgtg.road_vtx_id, env.ground_transit_graph.vertices[gts_tup.idx].road_vtx_id, env.road_graph_wts, env.location_list)

                        true_timeval = gts_tup.timeval
                        true_distval = gts_tup.distval

                        # Compute the true connection cost between them
                        # if length(bidir_sp.path) > 1
                        #     true_distval = v.distval + bidir_sp.dist
                        #     true_timeval = max(v.timeval + bidir_sp.dist/AvgSpeed, gts_tup.timeval)
                        # end

                        temp_gval = env.alpha_weight_distance*true_distval + (1.0 - env.alpha_weight_distance)*true_timeval

                        if ~haskey(env.gtg_idx_gval, gts_tup.idx) || temp_gval < env.gtg_idx_gval[gts_tup.idx]

                            idx_ctr += 1
                            new_vert = AerialMAPFState(idx=idx_ctr, ground_transit_idx=gts_tup.idx, timeval=true_timeval, distval=true_distval, considered_cars=v.considered_cars)

                            Graphs.add_vertex!(env.aerial_mapf_graph, new_vert)
                            push!(nbrs, idx_ctr)

                            env.gtg_idx_gval[gts_tup.idx] = temp_gval
                        end
                    end
                end
            end # (gstart, gend) in gtg_ground_path_start_ends
        end # vgtg.ground_agent_id != ugtg.ground_agent_id
    end # vgtg.ground == or != 0

    return true
end

function low_level_pp_search!(env::CoordinatedMAPFEnv, aerial_agent_id::Int64, s::AerialMAPFState, constraint::GroundTransitVertexConstraint)

    env.aerial_mapf_graph = SimpleVListGraph{AerialMAPFState}()
    Graphs.add_vertex!(env.aerial_mapf_graph, s)

    env.gtg_idx_gval =  Dict{Int64,Float64}()

    edge_wt_fn(u, v) = amapfg_edge_wt_fn(env, u, v)
    admissible_heuristic(u) = direct_flight_time_heuristic(env, u)

    start_idx = 1   # Always one since graph reinitialized
    env.next_gtg_goal_idx = env.gtg_drone_start_goal_idxs[aerial_agent_id][2]
    env.next_amapfg_goal_idx = 0

    vis = GroundTransitVertexGoalVisitor(env, constraint.avoid_gtg_vertex_set)
    a_star_states = a_star_implicit_shortest_path!(env.aerial_mapf_graph,
                                                    edge_wt_fn,
                                                    start_idx,
                                                    vis,
                                                    admissible_heuristic
                                                    )

    @assert haskey(a_star_states.parent_indices, env.next_amapfg_goal_idx)

    # Get path cost
    states = Tuple{AerialMAPFState,Float64}[]
    actions = Tuple{GroundTransitAction, Float64}[]

    # Get path cost
    sp_cost = a_star_states.dists[env.next_amapfg_goal_idx]

    # Get path
    sp_idxs = shortest_path_indices(a_star_states.parent_indices, env.aerial_mapf_graph, start_idx, env.next_amapfg_goal_idx)

    push!(states, (s, 0.0))

    # Traverse sp_idxs and set up states and actions
    for i = 2:length(sp_idxs)

        u, v =  (sp_idxs[i-1], sp_idxs[i])

        # NOTE: Create new state because we are going to delete graph later
        new_state = deepcopy(env.aerial_mapf_graph.vertices[v])
        push!(states, (new_state, a_star_states.dists[v]))

        # All derived info but can be useful
        push!(actions, (get_mapf_action(env, u, v), a_star_states.dists[v] - a_star_states.dists[u]))
    end

    return PlanResult{AerialMAPFState,GroundTransitAction,Float64}(states, actions, sp_cost, sp_cost)
end # function low_level_search

function add_constraint_from_pp_search!(env::CoordinatedMAPFEnv, constraint::GroundTransitVertexConstraint, aerial_id::Int64, id_path::PlanResult)

    for ((si, _), (sip, _)) in zip(id_path.states[1:end-1], id_path.states[2:end])
        si_gtg = env.ground_transit_graph.vertices[si.ground_transit_idx]
        sip_gtg = env.ground_transit_graph.vertices[sip.ground_transit_idx]
        if si_gtg.ground_agent_id !=0 && si_gtg.ground_agent_id == sip_gtg.ground_agent_id
            if ~haskey(env.gtg_idx_to_aerial_ids, si_gtg.idx)
                env.gtg_idx_to_aerial_ids[si_gtg.idx] = Int64[]
            end
            push!(env.gtg_idx_to_aerial_ids[si_gtg.idx], aerial_id)
        end
    end

    for (gtgidx, aerial_ids) in env.gtg_idx_to_aerial_ids
        if length(aerial_ids) > env.car_capacity
            union!(constraint.avoid_gtg_vertex_set, gtgidx)
        end
    end
end


text:
# # Monitoring performance
#
# ## Setting up
#
# ### Pinning the Julia threads
#
# It's highly recommended to pin the Julia threads to specific cores.
# We'll use [ThreadPools.jl](https://github.com/tro3/ThreadPools.jl)
# to operate with only a few of the available Julia threads.

## we'll consider the first `NUM_THREADS` Julia threads
using ThreadPools
const NUM_THREADS = 3;

# !!! note
#     If you want to use all Julia threads, replace the parallel blocks, i.e.
#     `tmap(1:NUM_THREADS) do threadid`, by `Threads.@threads :static for threadid in 1:NUM_THREADS`.

using Base.Threads: nthreads #src
@assert NUM_THREADS ≤ nthreads() #src

# Let's pin the first `NUM_THREADS` threads to the first `NUM_THREADS` cores.
using LIKWID
cores = 0:NUM_THREADS-1
tmap(i->LIKWID.pinthread(cores[i]), 1:NUM_THREADS)
tmap(i->LIKWID.get_processor_id(), 1:NUM_THREADS) # check

# ### Environment variables

# We use the `LIKWID.LIKWID_*` functions to set environment variables to configure LIKWID for our
# monitoring.
## use the following threads
cpustr = join(collect(0:NUM_THREADS-1), ",")
LIKWID.LIKWID_THREADS(cpustr)
## the location the marker file will be stored
LIKWID.LIKWID_FILEPATH(joinpath(@__DIR__, "likwid_marker.out"))
## Use the access daemon
LIKWID.LIKWID_MODE(1)
## Overwrite registers (if they are in use)
LIKWID.LIKWID_FORCE(true)
## Debug level
LIKWID.LIKWID_DEBUG(0)
## Events to measure
LIKWID.LIKWID_EVENTS("FLOPS_DP|L2|INSTR_RETIRED_ANY:FIXC0");

# ### Initialize LIKWID modules
using LIKWID: PerfMon
Marker.init_nothreads()
PerfMon.init()

# ## Measurement

# First we register the marker region. While this is not required
# it is strongly recommended as it reduces overhead of `Marker.startregion`
# and prevents wrong counts in short regions.
#
# Note that there must be a barrier between registering a region and starting that
# region. Typically these are done in separate parallel blocks, relying on
# the implicit barrier at the end of the parallel block. Usually there is
# a parallel block for initialization and a parallel block for execution.
tmap(1:NUM_THREADS) do threadid
    Marker.registerregion("Total")
    Marker.registerregion("calc_flops")

    ## To demonstrate that registering regions is optional, we do not register
    ## the "copy" region, which we'll use later.
end;

# Let's get to the actual performance monitoring.
# We will measure a single region, get the results
# and reset the region so that these results
# do not affect (potential) later measurements.
#
# But first, we'll need to define the computation
# that we want to analyze.
## simple function designed to do floating point computations
function do_flops(a, b, c, num_flops)
    for _ in 1:num_flops
        c = a * b + c
    end
    return c
end

# Let's run the computation and monitor the performance. For good measure,
# we put everything in a function.
function monitor_do_flops(NUM_FLOPS = 100_000_000)
    a = 1.8
    b = 3.2
    c = 1.0
    tmap(1:NUM_THREADS) do threadid
        ## Notice that only the first group specified, `FLOPS_DP`, will be measured.
        ## See further below for how to measure multiple groups.
        Marker.startregion("calc_flops")
        c = do_flops(c, a, b, NUM_FLOPS)
        Marker.stopregion("calc_flops")
    end
    return nothing
end
monitor_do_flops()

# ## Analysis
#
# To query basic information about the region from all threads
# we use [`Marker.getregion`](@ref).
tmap(1:NUM_THREADS) do threadid
    nevents, events, time, count = Marker.getregion("calc_flops")
    gid = PerfMon.get_id_of_active_group()
    group_name = PerfMon.get_name_of_group(gid)
    ## print basic info
    println("Thread $(threadid): group $(group_name), $(nevents) events, runtime $(time) s, and call count $(count)")
end;

# The tools from the `LIKWID.PerfMon` module can be used to get more detailed information,
# such as the event and metric results.
using PrettyTables
using Printf
_zeroifnothing(x::Nothing) = 0.0
_zeroifnothing(x) = x

## extract event and metric results
gid = PerfMon.get_id_of_active_group()
nevents = PerfMon.get_number_of_events(gid)
nmetrics = PerfMon.get_number_of_metrics(gid)
events = Matrix(undef, nevents, NUM_THREADS+1)
metrics = Matrix(undef, nmetrics, NUM_THREADS+1)

for tid in 0:NUM_THREADS-1
    for eid in 0:nevents-1
        events[eid+1,1] = PerfMon.get_name_of_event(gid, eid)
        events[eid+1,tid+2] = _zeroifnothing(PerfMon.get_result(gid, eid, tid))
    end
    for mid in 0:nmetrics-1
        metrics[mid+1,1] = PerfMon.get_name_of_metric(gid, mid)
        metrics[mid+1,tid+2] = _zeroifnothing(PerfMon.get_metric(gid, mid, tid))
    end
end

## printing
theader = ["Thread $(i)" for i in 0:NUM_THREADS-1]
pretty_table(events; header=vcat(["Event"], theader))
pretty_table(metrics; header=vcat(["Metric"], theader))


text:
"""
    compose(g::Function,f...)

Returns a new field obtained by composition of function `g` and the fields
`f`. The value of the resulting field at a vector of points `x` is numerically equivalent to

    fx = evaluate_fields(f,x)
    apply_kernel(bcast(g), fx...)

The gradient of the resulting field evaluated at a vector of points `x` is equivalent to

    fx = evaluate_fields(f,x)
    apply_kernel(bcast(gradient(g)), fx...)

Note that it is needed to overload `gradient(::typeof(g))` for the given function `g`
in order to be able to compute the gradient.

As in function [`apply_kernel_to_field`](@ref) if any of the inputs in `f` is a number or an array
instead of a field it will be treated as a "constant field".
"""
function compose(g::Function,f...)
  k = Comp(g)
  apply_kernel_to_field(k,f...)
end

struct Comp{F} <: Kernel
  e::BCasted{F}
  @inline Comp(f::Function) = new{typeof(f)}(BCasted(f))
end

@inline apply_kernel!(cache,k::Comp,x...) = apply_kernel!(cache,k.e,x...)

kernel_cache(k::Comp,x...) = kernel_cache(k.e,x...)

kernel_return_type(k::Comp,x...) = kernel_return_type(k.e,x...)

function apply_kernel_gradient(k::Comp,f...)
  g = gradient(k.e.f)
  compose(g,f...)
end

"""
    compose(g::Function,f::AbstractArray...)

Returns an array of fields numerically equivalent to

    map( (x...)->compose(g,x...), f...)

"""
function compose(g::Function,f::AbstractArray...)
  k = Comp(g)
  apply_to_field_array(k,f...)
end

"""
    compose_fields(g,f)
"""
function compose_fields(g,f)
  k = CompField(g)
  apply_kernel_to_field(k,f)
end

"""
    compose(g::Field,f::Field)
"""
function compose(g::Field,f::Field)
  compose_fields(g,f)
end

struct CompField{G} <: Kernel
  g::G
end

kernel_cache(k::CompField,fx) = field_cache(k.g,fx)

@inline apply_kernel!(cache,k::CompField,fx) = evaluate_field!(cache,k.g,fx)

kernel_return_type(k::CompField,fx) = field_return_type(k.g,fx)

function apply_kernel_gradient(k::CompField,f)
  g = field_gradient(k.g)
  compose_fields(g,f)
end

"""
    compose_field_arrays(g,f)
"""
function compose_field_arrays(g,f)
  k = CompFieldArray()
  apply(k,g,f)
end

"""
    compose(g::AbstractArray{<:Field},f::AbstractArray{<:Field})
"""
function compose(g::AbstractArray{<:Field},f::AbstractArray{<:Field})
  compose_field_arrays(g,f)
end

struct CompFieldArray <: Kernel end

kernel_cache(k::CompFieldArray,gi,fi) = nothing

@inline apply_kernel!(cache,k::CompFieldArray,gi,fi) = compose_fields(gi,fi)

function kernel_evaluate(k::CompFieldArray,x,g,f)
  fx = evaluate_field_array(f,x)
  gx = evaluate_field_array(g,fx)
  gx
end

function apply_gradient(k::CompFieldArray,g,f)
  ∇g = field_array_gradient(g)
  compose_field_arrays(∇g,f)
end


text:
using Test, GroupedArrays

p1 = repeat(1:5, inner = 2)

g1 = GroupedArray(p1)
@test eltype(g1) <: Int
@test size(g1) == (10,)
@test length(g1) == 10
@test g1[1] == 1
@test g1[1:2] == [1, 1]
@test g1[g1.<=1] == g1[[1, 1]]
@test_throws ArgumentError g1[1] = 0
g1[1] = 10
@test g1.ngroups == 10

p1_missing = repeat([missing, 1, 2, 3, 4], inner = 2)
g1 = GroupedArray(p1_missing)
@test eltype(g1) == Union{Int, Missing}
@test size(g1) == (10,)
@test length(g1) == 10
@test g1[1] === missing
@test g1.groups[1] === 0
@test g1.ngroups == 4

p1_missing = repeat([missing, -5, -10, 100, -300], inner = 2)
g1 = GroupedArray(p1_missing)
@test eltype(g1) == Union{Int, Missing}
@test size(g1) == (10,)
@test length(g1) == 10
@test g1[1] === missing
@test g1.groups[1] === 0
@test g1[end] === g1[end-1]

p1_missing = repeat([missing, "a", "b", "c", "c"], inner = 2)
g1 = GroupedArray(p1_missing)
@test eltype(g1) == Union{Int, Missing}
@test size(g1) == (10,)
@test length(g1) == 10
@test g1[1] === missing
@test g1.groups[1] === 0

g1 = GroupedArray(p1_missing; coalesce = true)
@test g1[1] === 1
@test eltype(g1) <: Int

p2 = repeat(1:5, outer = 2)
g = GroupedArray(p1_missing, p2)
@test ismissing(g[1])
g[3] = missing
@test ismissing(g[3])

p3 = [1,2]
@test_throws DimensionMismatch GroupedArray(p1, p3)

p = [1 2; 1 2; 2 1]
g = GroupedArray(p)

using CategoricalArrays
g = GroupedArray(categorical(p1_missing), categorical(p2))
@test ismissing(g[1])

using PooledArrays
g = GroupedArray(PooledArray(p1_missing), PooledArray(p2))
@test ismissing(g[1])

using DataAPI
g = GroupedArray(PooledArray(p1_missing), p2)
@test g[1] === missing
refs = DataAPI.refarray(g)
pools = DataAPI.refpool(g)
invrefpools = DataAPI.invrefpool(g)
@test all(pools[refs] .=== g)
@test all(DataAPI.refvalue(g, refs[i]) === g[i] for i in 1:length(g))
@test allunique(pools)
@test size(pools) == (9,)
for x in eachindex(pools)
	@test invrefpools[pools[x]] == x
end
@test get(invrefpools, missing, -1) == 0

@test ismissing(pools[invrefpools[missing]])
for ix in 1:g.ngroups
	pools[invrefpools[ix]] == ix
end

g = GroupedArray(p2)
invrefpools = DataAPI.invrefpool(g)
@test get(invrefpools, missing, -1) == -1

# all missings
g = GroupedArray([missing, missing, missing])
@test all(ismissing(x) for x in g)

max_stars_repo_name: matthieugomez/GroupedArrays

max_issues_repo_name: matthieugomez/GroupedArrays


text:
"""
    LogEvent

A struct for representing an event in an AWS Batch job log.
"""
struct LogEvent
    id::String
    ingestion_time::DateTime  # in UTC
    timestamp::DateTime       # in UTC
    message::String
end

function Base.convert(::Type{LogEvent}, d::AbstractDict)
    LogEvent(
        d["eventId"],
        Dates.unix2datetime(d["ingestionTime"] / 1000),
        Dates.unix2datetime(d["timestamp"] / 1000),
        d["message"],
    )
end

function Base.print(io::IO, event::LogEvent)
    print(io, rpad(event.timestamp, 23), " ", event.message)
end

function Base.print(io::IO, log_events::Vector{LogEvent})
    for event in log_events
        println(io, event)
    end
end

"""
    log_events(log_group, log_stream) -> Union{Vector{LogEvent}, Nothing}

Fetches the CloudWatch log from the specified log group and stream as a `Vector` of
`LogEvent`s. If the log stream does not exist then `nothing` will be returned.
"""
function log_events(log_group::AbstractString, log_stream::AbstractString;
                    aws_config::AbstractAWSConfig=global_aws_config())
    events = LogEvent[]

    curr_token = nothing
    next_token = nothing

    # We've hit the end of the stream if the next token matches the current one.
    while next_token != curr_token || next_token === nothing
        response = try
            @mock Cloudwatch_Logs.get_log_events(
                log_group, log_stream,
                Dict("nextToken"=>next_token);
                aws_config=aws_config,
            )
        catch e
            # The specified log stream does not exist. Specifically, this can occur when
            # a batch job has a reference to a log stream but the stream has not yet been
            # created.
            if (
                e isa AWSExceptions.AWSException &&
                e.cause.status == 400 &&
                e.info["message"] == "The specified log stream does not exist."
            )
                return nothing
            end

            rethrow()
        end

        append!(events, convert.(LogEvent, response["events"]))

        curr_token = next_token
        next_token = response["nextForwardToken"]
    end

    return events
end


text:
# Sun 30 Dec 2012 09:44:00 PM EST
#
# NeHe Tut 19 - Particle engine (triangle strips)
#
# Q - quit
# L - turn lights on/off
# F - change texture filter (linear, nearest, mipmap)
# PageUp/Down - move camera closer/further away
# Up/Down - increase/decrease x-rotation speed
# Left/Right - increase/decrease y-rotation speed
# Home/End - increase/decrease the amount of "friction" on particles
# Enter - turn rainbow effect on/off
# Space - step through colors for rainbow effect

# load necessary GL/SDL routines

using OpenGL
@OpenGL.version "1.0"
@OpenGL.load
using SDL

# initialize variables

bpp       = 16
wintitle  = "NeHe Tut 19"
icontitle = "NeHe Tut 19"
width     = 640
height    = 480

colors = [1.0  0.5  0.5;
          1.0  0.75 0.5;
          1.0  1.0  0.5;
          0.75 1.0  0.5;
          0.5  1.0  0.5;
          0.5  1.0  0.75;
          0.5  1.0  1.0;
          0.5  0.75 1.0;
          0.5  0.5  1.0;
          0.75 0.5  1.0;
          1.0  0.5  1.0;
          1.0  0.5  0.75]

rainbow       = true
slowdown      = 2.0
xspeed        = 0
yspeed        = 0
zoom          = -40.0
color         = 1
delay         = 0

MAX_PARTICLES = 1000

type particle
    active::Bool
    life::Float64
    fade::Float64
    red::Float64
    green::Float64
    blue::Float64
    xPos::Float64
    yPos::Float64
    zPos::Float64
    xSpeed::Float64
    ySpeed::Float64
    zSpeed::Float64
    xGrav::Float64
    yGrav::Float64
    zGrav::Float64
end

particles = [particle(true,                   # Julia doesn't like it when you try to initialize an empty array of
                      1.0,                    # a composite type and try to fill it afterwards, so we
                      rand(1:100)/1000+0.003,  # start with a 1-element vector and tack on values
                      colors[1,1],
                      colors[1,2],
                      colors[1,3],
                      0.0,
                      0.0,
                      0.0,
                      (rand(1:50)-26.0)*10.0,
                      (rand(1:50)-26.0)*10.0,
                      (rand(1:50)-26.0)*10.0,
                      0.0,
                      -0.8,
                      0.0)]

for loop = 2:MAX_PARTICLES
    active = true
    life   = 1.0
    fade   = rand(1:100)/1000+0.003
    red    = colors[loop%size(colors,1)+1,1]
    green  = colors[loop%size(colors,1)+1,2]
    blue   = colors[loop%size(colors,1)+1,3]
    xPos   = 0.0
    yPos   = 0.0
    zPos   = 0.0
    xSpeed = (rand(1:50)-26.0)*10.0
    ySpeed = (rand(1:50)-26.0)*10.0
    zSpeed = (rand(1:50)-26.0)*10.0
    xGrav  = 0.0
    yGrav  = -0.8
    zGrav  = 0.0
    particles = push!(particles, particle(active,life,fade,red,green,blue,xPos,yPos,zPos,xSpeed,ySpeed,zSpeed,xGrav,yGrav,zGrav))
end

keystate_checked   = false
lastkeycheckTime   = 0
key_repeatinterval = 75 #ms

# open SDL window with an OpenGL context

SDL_Init(SDL_INIT_VIDEO)
#videoInfo = SDL_GetVideoInfo()
videoFlags = (SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE | SDL_RESIZABLE)
#if videoInfo.hw_available
    videoFlags = (videoFlags | SDL_HWSURFACE)
#else
    #videoFlags = (videoFlags | SDL_SWSURFACE)
#end
#if videoInfo.blit_hw
    videoFlags = (videoFlags | SDL_HWACCEL)
#end
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
SDL_SetVideoMode(width, height, bpp, videoFlags)
SDL_WM_SetCaption(wintitle, icontitle)

glViewport(0, 0, width, height)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glDisable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()

gluPerspective(45.0,width/height,0.1,200.0)

glMatrixMode(GL_MODELVIEW)

# load textures from images

tex   = Array(Uint32,1) # generating 1 texture

img, w, h = glimread(expanduser("~/.julia/SDL/Examples/NeHe/tut19/Particle.bmp"))

glGenTextures(1,tex)
glBindTexture(GL_TEXTURE_2D,tex[1])
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img)

# enable texture mapping & blending

glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE)
glColor(1.0, 1.0, 1.0, 0.5)

glBindTexture(GL_TEXTURE_2D,tex[1])

# main drawing loop

while true
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()

    for loop = 1:MAX_PARTICLES
        if particles[loop].active
            x = particles[loop].xPos
            y = particles[loop].yPos
            z = particles[loop].zPos+zoom
            glColor(particles[loop].red, particles[loop].green, particles[loop].blue, particles[loop].life)
            glBegin(GL_TRIANGLE_STRIP)
                glTexCoord(1, 1)
                glVertex(x+0.5, y+0.5, z)
                glTexCoord(0, 1)
                glVertex(x-0.5, y+0.5, z)
                glTexCoord(1, 0)
                glVertex(x+0.5, y-0.5, z)
                glTexCoord(0, 0)
                glVertex(x-0.5, y-0.5, z)
            glEnd()
            particles[loop].xPos   += particles[loop].xSpeed/(slowdown*1000)
            particles[loop].yPos   += particles[loop].ySpeed/(slowdown*1000)
            particles[loop].zPos   += particles[loop].zSpeed/(slowdown*1000)
            particles[loop].xSpeed += particles[loop].xGrav
            particles[loop].ySpeed += particles[loop].yGrav
            particles[loop].zSpeed += particles[loop].zGrav
            particles[loop].life   -= particles[loop].fade
            if particles[loop].life < 0.0
                particles[loop].life   = 1.0
                particles[loop].fade   = (rand(1:100))/1000+0.003
                particles[loop].xPos   = 0.0
                particles[loop].yPos   = 0.0
                particles[loop].zPos   = 0.0
                particles[loop].xSpeed = xspeed+rand(1:60)-32.0
                particles[loop].ySpeed = yspeed+rand(1:60)-30.0
                particles[loop].zSpeed = rand(1:60)-30.0
                particles[loop].red    = colors[color,1]
                particles[loop].green  = colors[color,2]
                particles[loop].blue   = colors[color,3]
            end
            if saved_keystate == true
                if keystate[SDLK_8] == true && particles[loop].yGrav < 1.5
                    particles[loop].yGrav +=0.01
                elseif keystate[SDLK_2] == true && particles[loop].yGrav > -1.5
                    particles[loop].yGrav -=0.01
                elseif keystate[SDLK_4] == true && particles[loop].xGrav < 1.5
                    particles[loop].xGrav +=0.01
                elseif keystate[SDLK_6] == true && particles[loop].xGrav > -1.5
                    particles[loop].xGrav -=0.01
                elseif keystate[SDLK_TAB] == true
                    particles[loop].xPos   = 0.0
                    particles[loop].yPos   = 0.0
                    particles[loop].zPos   = 0.0
                    particles[loop].xSpeed = (rand(1:52)-26.0)*10
                    particles[loop].ySpeed = (rand(1:50)-25.0)*10
                    particles[loop].zSpeed = (rand(1:50)-25.0)*10
                end
            end
        end
    end

    delay +=1

    SDL_GL_SwapBuffers()

    SDL_PumpEvents()
    if SDL_GetTicks() - lastkeycheckTime >= key_repeatinterval
        keystate         = SDL_GetKeyState()
        keystate_checked = true
        lastkeycheckTime = SDL_GetTicks()
    end

    # Sampling rates for event processing are so fast that a single key press
    # lasts through several iterations of this loop.  This means that one press
    # can be seen as 50 or more presses by the SDL event system.  To correct
    # for this, we only check keypresses every 75ms.

    if keystate_checked == true
        if keystate[SDLK_q] == true
            break
        elseif keystate[SDLK_HOME] == true
            slowdown -=0.01
        elseif keystate[SDLK_END] == true
            slowdown +=0.01
        elseif keystate[SDLK_PAGEUP] == true
            zoom +=0.1
        elseif keystate[SDLK_PAGEDOWN] == true
            zoom -=0.1
        elseif keystate[SDLK_UP] == true
            yspeed +=1.0
        elseif keystate[SDLK_DOWN] == true
            yspeed -=1.0
        elseif keystate[SDLK_LEFT] == true
            xspeed +=1.0
        elseif keystate[SDLK_RIGHT] == true
            xspeed -=1.0
        elseif keystate[SDLK_RETURN] == true
            rainbow = (rainbow ? false : true)
        elseif keystate[SDLK_SPACE] == true || (rainbow && delay > 25)
            delay = 0
            color +=1
            if color > 12
                color = 1
            end
        end
        keystate_checked = false
    end
end


text:
#### Lagrange Interpolation Formula

using DelimitedFiles
using LinearAlgebra
using Plots; pyplot()

to = time()

function lagrange_interpolate(X,Y,t)
    C = ones(length(X))
    d = 0.0
    for i = 1:length(X)
        for j = [1:i-1;i+1:length(X)]
            C[i] = C[i]*(t-X[j])/(X[i]-X[j])
        end
        d = d + Y[i]*C[i]
    end
    return d
end

function lagrange_interpolate_plus(X,Y,t)
    idxs = eachindex(X)
    sum(Y[i] * prod((t-X[j])/(X[i]-X[j]) for j in idxs if j != i) for i in idxs)
end

A = readdlm("Data/dataSim.dat")
X = view(A,:,1)
Y = view(A,:,2)
T = 1.0:0.1:2.0
U = [lagrange_interpolate_plus(X, Y, t) for t in T]

td = time() - to
println("The time interval is $td s.")

plot(fontfamily=("Serif"),dpi=512)
plot!(X,Y,color="#32B432",marker=(:circle,10,Plots.stroke(:white)),label="Source")
plot!(T,U,color="#00B4DC",marker=(:cross,10,Plots.stroke(:white)),label="Foreast")

savefig("L.pdf")

#### Shanks Transform

using DelimitedFiles
using LinearAlgebra
using Plots; pyplot()

"""
L[i] = (S[i]^2-S[i-1]*S[i+1])/(2*S[i]-S[i-1]-S[i+1])
"""

A = readdlm("Data/Tr.dat")
X = view(A,:,1)
Y = view(A,:,2)

count = length(X)

L1 = ones(count)
for i in range(1; stop = count)
    if i in 2:count-1
        L1[i] = (Y[i]^2-Y[i-1]*Y[i+1])/(2*Y[i]-Y[i-1]-Y[i+1])
    else
        L1[i] = 0
    end
    i = i + 1
end

L2 = ones(count)
for i in range(1; stop = count)
    if i in 2:count-1
        L2[i] = (L1[i]^2-L1[i-1]*L1[i+1])/(2*L1[i]-L1[i-1]-L1[i+1])
    else
        L2[i] = 0
    end
    i = i + 1
end

L3 = ones(count)
for i in range(1; stop = count)
    if i in 2:count-1
        L3[i] = (L2[i]^2-L2[i-1]*L2[i+1])/(2*L2[i]-L2[i-1]-L2[i+1])
    else
        L3[i] = 0
    end
    i = i + 1
end

L4 = ones(count)
for i in range(1; stop = count)
    if i in 2:count-1
        L4[i] = (L3[i]^2-L3[i-1]*L3[i+1])/(2*L3[i]-L3[i-1]-L3[i+1])
    else
        L4[i] = 0
    end
    i = i + 1
end

plot(fontfamily=("Serif"),dpi=512)
scatter!(X,Y, color="#B4B4B4",marker=(:circle,10,Plots.stroke(:white)),label="Source")
plot!(X[2:count-1],L1[2:count-1],color="#FF1E14",label="Foreast")
plot!(X[3:count-2],L2[3:count-2],color="#FFC814",label="Foreast")
plot!(X[4:count-3],L3[4:count-3],color="#1978F0",label="Foreast")
plot!(X[5:count-4],L4[5:count-4],color="#A064DC",label="Foreast")

#### Richardson Extrapolation

"""
Sn = 1 + 1/2^2 + 1/3^2 + 1/4^2 + ... + 1/n^2
lim(n->∞)Sn = π^2/6 ≈ 1.6449340668482264

R1(n) = ((n+1)*S(n+1)-n*S(n))/factorial(1)
R2(n) = ((n+2)^2*S(n+1)-2*(n+1)^2*S(n+1)+n^2*S(n))/factorial(2)
……
"""

to = time()

A = readdlm("Data/Td.dat")
X = view(A,:,1)
Y = view(A,:,2)

count = length(X)

function RE1(Y)
    R1 = ones(count)
    for i in range(1; stop=count)
        if i in 1:count - 1
            R1[i] = ((i+1)*Y[i+1]-i*Y[i])/factorial(1)
        else
            R1[i] = 0
        end
        i = i + 1
    end
    return R1
end

function RE2(Y)
    R2 = ones(count)
    for i in range(1; stop=count)
        if i in 1:count - 2
            R2[i] = ((i+2)^2*Y[i+2]-2*(i+1)^2*Y[i+1]+i^2*Y[i])/factorial(2)
        else
            R2[i] = 0
        end
        i = i + 1
    end
    return R2
end

U1 = RE1(Y)
U2 = RE2(Y)

td = time() - to
println("The time interval is $td s.")

plot(fontfamily=("Serif"),dpi=512)
scatter!(X,Y, color="#B4B4B4",marker=(:circle,10,Plots.stroke(:white)),label="Source")
plot!(X[1:count-1],U1[1:count-1],color="#FF1E14",label="Foreast")
plot!(X[1:count-2],U2[1:count-2],color="#1978F0",label="Foreast")

savefig("R.pdf")

max_stars_repo_name: ConAntares/Photonica

max_issues_repo_name: ConAntares/Photonica

max_forks_repo_name: ConAntares/Photonica


text:
module GaussianBeams

include("system.jl")
include("plotting.jl")
include("utils.jl")

end # module


text:
# f(x)=elu(x)=x*[x>0] + (exp(x)-1)*[x<0]

alpha=1.0
function Felu(x)
    y=zeros(size(x))
    for i=1:length(x)
        if x[i]>0
            y[i]=x[i]
        else
            y[i]=alpha*(exp(x[i])-1.0)
        end
    end
    return (y,nothing)
end

function Felu_inplace(value::Array,auxvalue,x) # inplace
    for i=1:length(x)
        if x[i]>0
            value[i]=x[i]
        else
            value[i]=alpha*(exp(x[i])-1.0)
        end
    end
end

function Delu(derivativeIDX,f_c,faux_c,grad_c,grad_n,x)
    tmp=ones(size(x))
    for i=1:length(x)
        if x[i]<0
            tmp[i]=f_c[i]+alpha
        end
    end
    axpy!(1.0,tmp.*grad_c,grad_n)
end

if 1==0 # TODO GPU version

    function Felu_inplace(value::CudaArray,auxvalue,x::CudaArray...) # inplace
    end

    function Delu(derivativeIDX,f_c,faux_c,grad_c,grad_n,x::CudaArray...)
    end

end

Derivative[Felu]=Delu # Define dictionary lookup
Inplace[Felu]=Felu_inplace

elu(n::ADnode)=ADnode(Felu,n)

###elu(A::ADtrans)=transpose(elu(node[A.parent])) # elu(A')=(elu(A))' TODO:check

export elu

max_stars_repo_name: davidbarber/AutoDiff

max_issues_repo_name: davidbarber/AutoDiff

max_forks_repo_name: davidbarber/AutoDiff


text:
module CryptoMiniSat

import Base.convert
import Base.show
import Base.~

global const cmsat_lib = Sys.isunix() ? "libcryptominisat5" : "cryptominisat5"

export SATSolver
export Lit, LBool
export Slice_Lit, Slice_lbool
export cmsat_new, cmsat_free, nvars, add_clause, add_xor_clause, new_vars, solve,
       get_model, get_conflict, print_stats, set_num_threads, set_verbosity

# structs taken from:
# https://github.com/msoos/cryptominisat/blob/master/src/cryptominisat_c.h.in
struct C_Lit
    x::UInt32
end

struct C_lbool
    x::UInt8
end

struct Slice_Lit
    vals::Ptr{C_Lit}
    num_vals::Csize_t
end

struct Slice_lbool
    vals::Ptr{C_lbool}
    num_vals::Csize_t
end

const l_true = C_lbool(0)
const l_false = C_lbool(1)
const l_undef = C_lbool(2)

const Csatsolver = Ptr{Nothing}
const LBool = Union{Bool, Nothing}

function convert(::Type{LBool}, b::C_lbool)::LBool
    if b == l_undef
        return nothing
    else
        return b.x == 0
    end
end

# adapted from:
# https://github.com/msoos/cryptominisat/blob/master/rust/src/lib.rs
#=
function C_Lit(var::T, negated::Bool) where {T <: Unsigned}
    @assert var < (1 << 31)

    return C_Lit(var << 1 | negated)
end
=#

struct Lit
    var::Int
    negated::Bool
end

Lit(var::Int) = Lit(var, false)
~(l::Lit) = Lit(l.var, ~l.negated)

function Base.show(io::IO, l::Lit)
    if l.negated
        print(io, "¬")
    end
    show(io, l.var)
end

function convert(::Type{C_Lit}, l::Lit)::C_Lit
    @assert l.var < (1 << 31)
    @assert l.var >= 0

    return C_Lit(l.var << 1 | l.negated)
end

function convert(::Type{Lit}, l::C_Lit)::Lit
    return Lit(l.x >> 1, l.x & 0x1)
end

mutable struct SATSolver
    hnd::Csatsolver
    function SATSolver(hnd::Csatsolver)
        a = new(hnd)
        finalizer(cmsat_free, a)
    end
end

SATSolver() = SATSolver(cmsat_new())

function cmsat_free(sat_solver::SATSolver)
    ccall((:cmsat_free, cmsat_lib),
        Nothing,
        (Csatsolver,),
        sat_solver.hnd)
end

function cmsat_new()::Csatsolver
    solver = ccall((:cmsat_new, cmsat_lib), Csatsolver, ())
    return solver
end

function nvars(sat_solver::SATSolver)
    return ccall((:cmsat_nvars, cmsat_lib),
                 Cuint,
                 (Csatsolver,),
                 sat_solver.hnd)
end

function add_clause(sat_solver::SATSolver, clause::Vector{Lit})::Bool
    return ccall((:cmsat_add_clause, cmsat_lib),
                 Bool,
                 (Csatsolver, Ref{C_Lit}, Csize_t),
                 sat_solver.hnd, convert(Vector{C_Lit}, clause), length(clause))
end

function add_xor_clause(sat_solver::SATSolver, vars::Vector{T}, rhs::Bool)::Bool where {T <: Unsigned}
    return ccall((:cmsat_add_xor_clause, cmsat_lib),
          Bool,
          (Csatsolver, Ref{Cuint}, Csize_t, Bool),
          sat_solver.hnd, convert(Vector{Cuint}, vars), length(vars), rhs)
end

function new_vars(sat_solver::SATSolver, n_vars::T) where {T <: Integer}
    ccall((:cmsat_new_vars, cmsat_lib),
           Nothing,
           (Csatsolver, Cuint),
           sat_solver.hnd, n_vars)
end

function solve(sat_solver::SATSolver)::Tuple{LBool,Vector{LBool}}
    sat = ccall((:cmsat_solve, cmsat_lib),
                 C_lbool,
                 (Csatsolver,),
                 sat_solver.hnd)
    return sat, get_model(sat_solver)
end

function solve(sat_solver::SATSolver, assumptions::Vector{Lit})::Tuple{LBool,Vector{LBool}}
    sat = ccall((:cmsat_solve_with_assumptions, cmsat_lib),
                 C_lbool,
                 (Csatsolver, Ref{C_Lit}, Csize_t),
                 sat_solver.hnd, convert(Array{C_Lit}, assumptions), length(assumptions))
    return sat, get_model(sat_solver)
end

function get_model(sat_solver::SATSolver)::Vector{LBool}
    c_mdl = ccall((:cmsat_get_model, cmsat_lib),
                  Slice_lbool,
                  (Csatsolver,),
                  sat_solver.hnd)
    return unsafe_wrap(Array{C_lbool}, c_mdl.vals, c_mdl.num_vals)
end

function get_conflict(sat_solver::SATSolver)::Vector{Lit}
    c_conflict = ccall((:cmsat_get_conflict, cmsat_lib),
                       Slice_Lit,
                       (Csatsolver,),
                       sat_solver.hnd)
    clits = unsafe_wrap(Array{C_Lit}, c_conflict.vals, c_conflict.num_vals)
    return clits
end

function print_stats(sat_solver::SATSolver)
    ccall((:cmsat_print_stats, cmsat_lib),
          Nothing,
          (Csatsolver,),
          sat_solver.hnd)
end

function set_num_threads(sat_solver::SATSolver, n_threads::T) where {T <: Integer}
    ccall((:cmsat_set_num_threads, cmsat_lib),
          Nothing,
          (Csatsolver, Cuint),
          sat_solver.hnd, n_threads)
end

function set_verbosity(sat_solver::SATSolver, n::T) where {T <: Integer}
    ccall((:cmsat_set_verbosity, cmsat_lib),
          Nothing,
          (Csatsolver, Cuint),
          sat_solver.hnd, n)
end

end


text:
function iterative_first_improvement(tuning_run::Run,
                                     reference::RemoteChannel;
                                     cutoff::Integer = 10_000)
    name               = "Iterative First Improvement"
    iteration          = 1
    cost_calls         = tuning_run.cost_evaluations
    stopping_criterion = @task tuning_run.stopping_criterion(tuning_run.duration)
    stop               = consume(stopping_criterion)

    while !stop
        iteration                 += 1
        result                     = first_improvement(tuning_run, cutoff = cutoff)
        cost_calls                += result.cost_calls
        result.cost_calls          = cost_calls
        result.start               = tuning_run.starting_point
        result.technique           = name
        result.iterations          = iteration
        result.current_iteration   = iteration
        tuning_run.starting_point  = result.minimum
        tuning_run.starting_cost   = result.cost_minimum
        stop                       = consume(stopping_criterion)
        put!(reference, result)
    end
end