text:
"""
    dim_str(diag)

Get `dim` as subscript string.
"""
function dim_str(diag)
    sub_digits = ("₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉")
    if hasproperty(diag, :dim)
        return join(reverse(sub_digits[digits(dim(diag)) .+ 1]))
    else
        return "ₓ"
    end
end
function clamp_death(int::PersistenceInterval, t_max)
    return isfinite(int) ? death(int) : t_max
end
function clamp_persistence(int::PersistenceInterval, t_max)
    return isfinite(int) ? persistence(int) : t_max
end

struct InfinityLine
    vertical::Bool
end

@recipe function f(::Type{InfinityLine}, infline::InfinityLine)
    if haskey(plotattributes, :infinity)
        if infline.vertical
            seriestype := :vline
        else
            seriestype := :hline
        end
        primary := false
        seriescolor := :grey
        line := :dot
        label := "∞"
        linewidth := 1
        markershape := :none

        [plotattributes[:infinity]]
    else
        label := ""
        [NaN]
    end
end

struct ZeroPersistenceLine end

@recipe function f(::Type{ZeroPersistenceLine}, ::ZeroPersistenceLine)
    primary := false
    seriescolor := :black
    markershape := :none

    if get(plotattributes, :persistence, false)
        seriestype := :hline
        [0]
    else
        seriestype := :line
        identity
    end
end

@recipe function f(::Type{D}, diag::D) where {D<:AbstractArray{<:PersistenceInterval}}
    if plotattributes[:letter] == :x
        return birth.(diag)
    elseif get(plotattributes, :persistence, false)
        return clamp_persistence.(diag, get(plotattributes, :infinity, Inf))
    else
        return clamp_death.(diag, get(plotattributes, :infinity, Inf))
    end
end

@recipe function f(::Type{Val{:persistencediagram}}, x, y, z)
    seriestype := :scatter
    markerstrokecolor --> :auto
    markeralpha --> 0.5
    return x, y
end

function limits(diags, infinity=nothing)
    xs = map.(birth, diags)
    ys = map.(death, diags)

    # Use reduce insted of maximum/minimum because it handles empty collections
    t_lo = reduce(min, t for t in Iterators.flatten((xs..., ys...)); init=0.0)
    t_hi = reduce(max, t for t in Iterators.flatten((xs..., ys...)) if t < Inf; init=0.0)

    threshes = filter(
        isfinite, threshold.(Iterators.filter(d -> hasproperty(d, :threshold), diags))
    )
    if isnothing(infinity)
        if !isempty(threshes)
            infinity = maximum(threshes)
        else
            infinity = t_hi * 1.25
        end
    end
    if any(!isfinite, Iterators.flatten(ys))
        t_hi = infinity
    end

    return t_lo, t_hi, infinity
end

function set_default!(d, key, value)
    d[key] = get(d, key, value)
    return d
end

function setup_diagram_plot!(d, diags)
    set_default!(d, :persistence, false)
    t_lo, t_hi, infinity = limits(diags, get(d, :infinity, nothing))
    # Zero persistence line messes up the limits, so we attempt to reset them here.
    gap = (t_hi - t_lo) * 0.05
    if gap > 0
        set_default!(d, :xlims, (t_lo - gap, t_hi + gap))
        set_default!(d, :ylims, (t_lo - gap, t_hi + gap))
        set_default!(d, :aspect_ratio, 1)
    end
    set_default!(d, :infinity, infinity)

    set_default!(d, :xguide, "birth")
    set_default!(d, :yguide, d[:persistence] ? "persistence" : "death")
    set_default!(d, :legend, d[:persistence] ? :topright : :bottomright)
    return set_default!(d, :title, "Persistence Diagram")
end

@recipe function f(
    diags::Union{
        NTuple{<:Any,PersistenceDiagram},
        AbstractArray{PersistenceDiagram},
        PersistenceDiagram,
    },
)
    seriescolor --> 1:length(diags)
    if diags isa PersistenceDiagram
        diags = (diags,)
    end
    setup_diagram_plot!(plotattributes, diags)

    # Only plot these if this was constructed with plot, not plot!
    if get(plotattributes, :plot_object, (n=0,)).n == 0
        @series begin
            ZeroPersistenceLine()
        end
        @series begin
            InfinityLine(false)
        end
    end

    different_dims = all(d -> hasproperty(d, :dim), diags) && allunique(dim.(diags))
    for (i, diag) in enumerate(diags)
        @series begin
            series_cols = plotattributes[:seriescolor]
            if different_dims
                idx = dim(diag) + 1
                label --> "H$(dim_str(diag))"
            else
                idx = i
                label --> "H$(dim_str(diag)) ($i)"
            end

            seriestype := :persistencediagram
            seriescolor := series_cols[idx]
            linecolor --> get(plotattributes, :linecolor, series_cols)[idx]
            fillcolor --> get(plotattributes, :fillcolor, series_cols)[idx]
            markercolor --> get(plotattributes, :markercolor, series_cols)[idx]

            diag, diag
        end
    end
end

@recipe function f(match::Matching)
    seriescolor --> [1, 2, 3]
    left = match.left
    right = match.right
    setup_diagram_plot!(plotattributes, (left, right))
    inf = plotattributes[:infinity]

    @series begin
        label --> "matching"
        seriescolor := plotattributes[:seriescolor][1]

        xs = Float64[]
        ys = Float64[]
        for (l, r) in
            matching(match; bottleneck=get(plotattributes, :bottleneck, match.bottleneck))
            append!(xs, (birth(l), birth(r), NaN))
            if plotattributes[:persistence]
                append!(ys, (clamp_persistence(l, inf), clamp_persistence(r, inf), NaN))
            else
                append!(ys, (clamp_death(l, inf), clamp_death(r, inf), NaN))
            end
        end
        xs, ys
    end

    @series begin
        InfinityLine(false)
    end
    @series begin
        ZeroPersistenceLine()
    end
    @series begin
        seriestype := :persistencediagram
        seriescolor := plotattributes[:seriescolor][2]
        label --> "left"
        left, left
    end
    @series begin
        seriestype := :persistencediagram
        seriescolor := plotattributes[:seriescolor][3]
        label --> "right"
        right, right
    end
end

struct Barcode
    diags::NTuple{<:Any,PersistenceDiagram}
end

Barcode(diag::PersistenceDiagram) = Barcode((diag,))
Barcode(diags::AbstractArray{PersistenceDiagram}) = Barcode(tuple(diags...))
Barcode(diags::Vararg{PersistenceDiagram}) = Barcode(diags)

@recipe function f(bc::Barcode)
    diags = bc.diags

    t_lo, t_hi, infinity = limits(diags)
    infinity --> infinity
    yticks --> []
    xguide --> "t"
    legend --> :outertopright
    title --> "Persistence Barcode"

    _bar_offset --> 0
    bar_offset = plotattributes[:_bar_offset]
    # Infinity line series messes up the limits.
    n_intervals = mapreduce(length, +, diags; init=0) + bar_offset
    ylims --> (1 - n_intervals * 0.05, n_intervals * 1.05)

    @series begin
        InfinityLine(true)
    end

    different_dims = all(d -> hasproperty(d, :dim), diags) && allunique(dim.(diags))
    for (i, diag) in enumerate(diags)
        @series begin
            seriestype := :path
            linewidth --> 1
            if different_dims
                label --> "H$(dim_str(diag))"
                markercolor --> dim(diag) + 1
            else
                label --> "H$(dim_str(diag)) ($i)"
            end

            xs = Float64[]
            ys = Float64[]
            for int in diag
                bar_offset += 1
                b, d = birth(int), clamp_death(int, t_hi)
                append!(xs, (b, d, NaN))
                append!(ys, (bar_offset, bar_offset, NaN))
            end
            xs, ys
        end
    end
end

"""
    barcode(diagram)

Plot the barcode plot of persistence diagram or multiple diagrams in a collection. The
`infinity` keyword argument determines where the infinity line is placed. If unset, the
function tries to use `threshold(diagram)`, or guess a good position to place the line at.
"""
barcode(args...; kwargs...) = RecipesBase.plot(Barcode(args...); kwargs...)


text:
@testset "Test special accessors" begin
    cdmsys = PSB.build_system(PSITestSystems, "test_RTS_GMLC_sys")
    th = first(get_components(ThermalStandard, cdmsys))
    re = first(get_components(RenewableDispatch, cdmsys))

    @test get_max_active_power(th) == get_active_power_limits(th).max
    @test get_max_active_power(re) <= get_rating(re)
    @test isa(get_max_reactive_power(re), Float64)

    @test_throws ArgumentError get_max_active_power(TestDevice("foo"))
    @test_throws ArgumentError get_max_active_power(TestRenDevice("foo"))
end

ext: jl


text:
using Test

#=
    91.2    Basic Unit Tests
=#

foo(x) = length(x)^2

@test foo("hey") == 9
@test foo("there") > 20

@test_throws BoundsError [1,2,3][4]

@test_throws DimensionMismatch [1,2,3] + [1,2]

# @test_throws BoundsError [1,2,3][3]
@test π ≈ 3.14 atol=0.01

result = @test ≈(π, 3.14, atol=0.01)

@show result.test_type
@show result
@show result.value
@show result.orig_expr
@show typeof(result.orig_expr)
@show result.data

# This throws exception MethodError
# @test foo(:cat) == 1
@test_throws MethodError foo(:cat)

#=
    91.3    Working With Test Sets
=#

result = @testset "My Description of this test set" begin
    @test foo("hello") == 25
    @test foo("bar") == 9
end

@show result
@show result.description
@show result.results
@show result.n_passed
@show result.anynonpass

# r = @test_throws TestSetException begin
#         @testset "My Description of this test set" begin
#             @test foo("hello") == 25
#             @test foo("bar") == 9
#             @test foo("bars") == 9
#         end
# end;
# @show r

try
    @testset "My new testset description" begin
        @test foo("hello") == 25
        @test foo("bar") == 9
        @test foo("bars") == 16
    end
catch e
    println(e);
    #rethrow(e)
    nothing
finally
    println("Finally!")
    nothing
end

# Nested testsets

result = @testset "Foo Tests" begin
    @testset "Animals" begin
        @test foo("cat") == 9
        @test foo("dog") == foo("cat")
    end
    @testset "Arrays $i" for i in 1:3
        @test foo(zeros(i)) == i^2
        @test foo(fill(1.0, i)) == i^2
    end
end;

@show result;

@show result.description

for (i,r) in enumerate(result.results)
    println(i, ": ", r)
end

@show result.anynonpass

@testset "Foo Tests" begin
    @testset "Animals" begin
        @testset "Felines" begin
            @test foo("cat") == 9
        end
        @testset "Canines" begin
            @test foo("dog") == 9
        end
    end
    @testset "Arrays" begin
        @test foo(zeros(2)) == 4
        @test foo(fill(1.0, 4)) == 16
    end
end


text:
# Julia wrapper for source: julia/src/llvm-api.cpp

# initialization functions

function LLVMInitializeAllTargetInfos()
    @apicall(:LLVMExtraInitializeAllTargetInfos,Cvoid,())
end

function LLVMInitializeAllTargets()
    @apicall(:LLVMExtraInitializeAllTargets,Cvoid,())
end

function LLVMInitializeAllTargetMCs()
    @apicall(:LLVMExtraInitializeAllTargetMCs,Cvoid,())
end

function LLVMInitializeAllAsmPrinters()
    @apicall(:LLVMExtraInitializeAllAsmPrinters,Cvoid,())
end

function LLVMInitializeAllAsmParsers()
    @apicall(:LLVMExtraInitializeAllAsmParsers,Cvoid,())
end

function LLVMInitializeAllDisassemblers()
    @apicall(:LLVMExtraInitializeAllDisassemblers,Cvoid,())
end

function LLVMInitializeNativeTarget()
    @apicall(:LLVMExtraInitializeNativeTarget,LLVMBool,())
end

function LLVMInitializeNativeAsmPrinter()
    @apicall(:LLVMExtraInitializeNativeAsmPrinter,LLVMBool,())
end

function LLVMInitializeNativeAsmParser()
    @apicall(:LLVMExtraInitializeNativeAsmParser,LLVMBool,())
end

function LLVMInitializeNativeDisassembler()
    @apicall(:LLVMExtraInitializeNativeDisassembler,LLVMBool,())
end

# infrastructure for writing LLVM passes in Julia

mutable struct LLVMOpaquePass
end

const LLVMPassRef = Ptr{LLVMOpaquePass}

function LLVMAddPass(PM::LLVMPassManagerRef, P::LLVMPassRef)
    @apicall(:LLVMExtraAddPass,Cvoid,
        (LLVMPassManagerRef, LLVMPassRef),
        PM, P)
end

function LLVMCreateModulePass(Name, Callback)
    @apicall(:LLVMExtraCreateModulePass,LLVMPassRef,
        (Cstring, Any),
        Name, Callback)
end

function LLVMCreateFunctionPass(Name, Callback)
    @apicall(:LLVMExtraCreateFunctionPass,LLVMPassRef,
        (Cstring, Any),
        Name, Callback)
end

function LLVMCreateBasicBlockPass(Name, Callback)
    @apicall(:LLVMExtraCreateBasicBlockPass,LLVMPassRef,
        (Cstring, Any),
        Name, Callback)
end

# various missing functions

function LLVMAddInternalizePassWithExportList(PM::LLVMPassManagerRef, ExportList, Length)
    @apicall(:LLVMExtraAddInternalizePassWithExportList,Cvoid,(LLVMPassManagerRef,Ptr{Cstring},Csize_t), PM, ExportList, Length)
end

function LLVMAddTargetLibraryInfoByTriple(Triple, PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddTargetLibraryInfoByTiple,Cvoid,(Cstring, LLVMPassManagerRef), Triple, PM)
end

if VERSION < v"1.2.0-DEV.531"

function LLVMAddNVVMReflectPass(PM::LLVMPassManagerRef, smversion)
    @apicall(:LLVMExtraAddMVVMReflectPass,Cvoid,(LLVMPassManagerRef,), PM)
end

else

if libllvm_version < v"8.0"

function LLVMAddNVVMReflectPass(PM::LLVMPassManagerRef, smversion)
    @apicall(:LLVMExtraAddNVVMReflectPass,Cvoid,(LLVMPassManagerRef,), PM)
end

else

function LLVMAddNVVMReflectPass(PM::LLVMPassManagerRef, smversion)
    @apicall(:LLVMExtraAddNVVMReflectFunctionPass,Cvoid,(LLVMPassManagerRef, Cuint), PM, smversion)
end

end

function LLVMAddAllocOptPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddAllocOptPass,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddBarrierNoopPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddBarrierNoopPass,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddGCInvariantVerifierPass(PM::LLVMPassManagerRef, Strong)
    @apicall(:LLVMExtraAddGCInvariantVerifierPass,Cvoid,(LLVMPassManagerRef,LLVMBool), PM, Strong)
end

function LLVMAddLowerExcHandlersPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddLowerExcHandlersPass,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddCombineMulAddPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddCombineMulAddPass,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddMultiVersioningPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddMultiVersioningPass,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddPropagateJuliaAddrspaces(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddPropagateJuliaAddrspaces,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddLowerPTLSPass(PM::LLVMPassManagerRef, imaging_mode)
    @apicall(:LLVMExtraAddLowerPTLSPass,Cvoid,(LLVMPassManagerRef,LLVMBool), PM, imaging_mode)
end

function LLVMAddLowerSimdLoopPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddLowerSimdLoopPass,Cvoid,(LLVMPassManagerRef,), PM)
end

function LLVMAddLateLowerGCFramePass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddLateLowerGCFramePass,Cvoid,(LLVMPassManagerRef,), PM)
end

end

if VERSION >= v"1.3.0-DEV.95"

function LLVMAddFinalLowerGCPass(PM::LLVMPassManagerRef)
    @apicall(:LLVMExtraAddFinalLowerGCPass,Cvoid,(LLVMPassManagerRef,), PM)
end

end # v"1.3.0-DEV.95"

function LLVMGetValueContext(V::LLVMValueRef)
    @apicall(:LLVMExtraGetValueContext,LLVMContextRef,(LLVMValueRef,),V)
end

if VERSION >= v"0.7.0-alpha.37"

function LLVMGetSourceLocation(V::LLVMValueRef, index, Name, Filename, Line, Column)
    @apicall(:LLVMExtraGetSourceLocation,Cint,(LLVMValueRef,Cint,Ptr{Cstring},Ptr{Cstring},Ptr{Cuint},Ptr{Cuint}), V, index, Name, Filename, Line, Column)
end

end

if libllvm_version >= v"8.0"
    @cenum(LLVMDebugEmissionKind,
        LLVMDebugEmissionKindNoDebug = 0,
        LLVMDebugEmissionKindFullDebug = 1,
        LLVMDebugEmissionKindLineTablesOnly = 2,
        LLVMDebugEmissionKindDebugDirectivesOnly = 3,
    )
end


text:
module SquareLatticeModel
##export ParamDict, getNeighbors, genPoly

mutable struct ParamDict
    nrows::Int
    ncols::Int
    N::Int
end

function genFunctor(d)
    d = d

    function roundN(n, N)
        m = (n - 1 + N) % N
        m + 1
    end

    function getXY(k, d=d)
        x = roundN(k, d.ncols)
        y = roundN(div(k-1, d.ncols)+1, d.nrows)
        (x, y)
    end

    function getK( x, y, d=d)
        # must round each x, y
        xn = (x - 1 + d.ncols) % d.ncols + 1
        yn = (y - 1 + d.nrows) % d.nrows + 1
        #
        k = (yn - 1) * d.ncols + xn
    end

    function getNeighbors( k, d=d)
        (x, y) = getXY(k,d)
        [getK(x-1, y, d), getK(x, y-1, d), getK(x+1, y, d), getK(x, y+1, d)]
    end

    # polygon for each cell
    function genPoly(k, d)
        (x, y) = getXY(k, d)
        w = 0.5
        h = 0.5
        (x .+ [-w,  w, w, -w, -w],
         y .+ [-h, -h, h,  h, -h])
    end

    return (getNeighbors, genPoly)
end # of functor

end # of module


text:
__precompile__(true)

module ES

	using HTTP
	using JSON
	import Base64: base64encode
	export Esinfo,cat_aliases,cat_allocation,cat_count,cat_fielddata,cat_health,cat_master,cat_nodeattrs,cat_pending_tasks,
		cat_plugins,cat_recovery,cat_repositories,cat_segments,cat_shards,cat_snapshots,cat_tasks,cat_templates,cat_thread_pool,
		es_indices_shrink,es_indices_split,es_indices_stats,es_indices_update_aliases,es_indices_upgrade,es_indices_validate_query,
		es_ingest_get_pipeline,es_ingest_processor_grok,es_ingest_put_pipeline,es_ingest_simulate,es_mtermvectors,es_nodes_hot_threads,
		es_nodes_info,es_nodes_reload_secure_settings,es_nodes_stats,es_nodes_usage,es_ping,es_put_script,es_rank_eval,es_reindex,
		es_reindex_rethrottle,es_render_search_template,es_scripts_painless_execute,es_scroll,es_scroll_clear,es_search,
		es_search_shards,es_searchs,es_snapshot_create,es_snapshot_create_repository,es_snapshot_get,es_snapshot_get_repository,
		es_snapshot_restore,es_snapshot_status,es_snapshot_verify_repository,es_tasks_cancel,es_tasks_get,es_tasks_list,
		es_update,es_update_by_query,es_update_by_query_rethrottle,xpack_sql,xpack_sql_close,xpack_translate,es_bulk_create,
		es_bulk_del,es_bulk_index,es_bulk_script,es_bulk_update,es_cluster_allocation_explain,es_cluster_get_settings,
		es_cluster_health,es_cluster_pending_tasks,es_cluster_put_settings,es_cluster_remote_info,es_cluster_reroute,
		es_cluster_state,es_cluster_stats,es_count,es_create,es_delete,es_delete_by_query,es_delete_by_query_rethrottle,
		es_exists,es_exists_source,es_explain,es_field_caps,es_get,es_getscript,es_getsource,es_index,es_indices_analyze,
		es_indices_clear_cache,es_indices_close,es_indices_create,es_indices_delete,es_indices_delete_alias,
		es_indices_delete_template,es_indices_exists,es_indices_exists_alias,es_indices_exists_template,
		es_indices_flush,es_indices_flush_synced,es_indices_forcemerge,es_indices_get,es_indices_get_alias,
		es_indices_get_field_mapping,es_indices_get_mapping,es_indices_get_settings,es_indices_get_template,es_indices_get_upgrade,
		es_indices_open,es_indices_put_alias,es_indices_put_mapping,es_indices_put_settings,es_indices_put_template,
		es_indices_recovery,es_indices_refresh,es_indices_rollover,es_indices_segments,es_indices_shard_stores

	export @esexport ,@query, @filter, @must, @must_not ,@should  ,@nested ,@has_child, @has_parent ,@fulltext,@smi,@comm,@extra
	export BulkLength

	struct Esinfo
		host::AbstractString
		port::AbstractString
		transport::AbstractString
		base64::AbstractString
		url::AbstractString
		jheader::Vector{Pair{String,String}}
		nheader::Vector{Pair{String,String}}
		conf::NamedTuple

		function Esinfo(; host::AbstractString ,port::AbstractString ,user::AbstractString  ,pwd::AbstractString , transport::AbstractString= "https")
			ur = string(transport, "://", host, ":", port)
			basetmp = base64encode( user , ":", pwd)
			jheader= ["content-type" => "application/json", "Authorization" => string( "Basic" , " ", basetmp ) ]
			nheader= ["content-type" => "application/x-ndjson", "Authorization" => string( "Basic" , " ", basetmp ) ]
			conf = ( require_ssl_verification = false, basic_authorization = true)
			new( host,  port,  transport , basetmp , ur, jheader, nheader, conf )
		end

		function Esinfo(host::AbstractString ,port::AbstractString , transport::AbstractString="http")
			ur = string(transport, "://", host, ":", port)
			jheader= ["content-type" => "application/json" ]
			nheader= ["content-type" => "application/x-ndjson"  ]
			new( host,  port,  transport, "", ur , jheader, nheader, NamedTuple() )
		end

		function Esinfo(host::AbstractString ,port::AbstractString ,base64::AbstractString ,
			 						jheader::Vector{Pair{String,String}},
									nheader::Vector{Pair{String,String}},
									conf::NamedTuple,
									transport::AbstractString= "https")
			ur = string(transport, "://", host, ":", port)
			new( host,  port,  transport , base64 , ur, jheader, nheader, conf )
		end

	end

	Esinfo(host::AbstractString) = Esinfo(host, "9200")

	include("macro.jl")
	include("transformation.jl")
	include("api.jl")
	include("cat.jl")
	include("cluster_indices.jl")
	include("plugins.jl")
	include("other.jl")

end

max_stars_repo_name: frankill/ES

max_issues_repo_name: frankill/ES

max_forks_repo_name: frankill/ES


text:
#  NOTE:  This only needs to be run when the binaries change or need to be modified.
using ArtifactUtils, Pkg.Artifacts, VarianceComponentsHDFE, CSV
artifacts_path = joinpath(pkgdir(VarianceComponentsHDFE), "benchmark/Artifacts.toml")
url_prefix = "https://vchdfe.s3-us-west-2.amazonaws.com"
add_artifact!(
            artifacts_path,
           "medium_nocontrols_pruned",
           "$url_prefix/medium_pruned_main.tar.gz",
           force=true,
           lazy = true
       )

add_artifact!(
    artifacts_path,
    "medium_controls_pruned",
    "$url_prefix/medium_controls_pruned_main.tar.gz",
    force=true,
    lazy = true
)

add_artifact!(
    artifacts_path,
    "large_nocontrols_pruned",
    "$url_prefix/large_pruned_main.tar.gz",
    force=true,
    lazy =true
)

add_artifact!(
    artifacts_path,
    "large_controls_pruned",
    "$url_prefix/large_controls_pruned_main.tar.gz",
    force=true,
    lazy = true
)

add_artifact!(
    artifacts_path,
    "huge_nocontrols_pruned",
    "$url_prefix/huge_pruned_main.tar.gz",
    force=true,
    lazy = true
)

#Ensure Installation of the files

ensure_artifact_installed("medium_nocontrols_pruned",artifacts_path)
ensure_artifact_installed("medium_controls_pruned",artifacts_path)
ensure_artifact_installed("large_nocontrols_pruned",artifacts_path)
ensure_artifact_installed("large_controls_pruned",artifacts_path)
ensure_artifact_installed("huge_nocontrols_pruned",artifacts_path)

# Testing
med_csv_hash = artifact_hash("medium_nocontrols_pruned",artifacts_path)
med_csv = CSV.read(joinpath(artifact_path(med_csv_hash), "medium_nocontrols_pruned.csv"))

lar_csv_hash = artifact_hash("large_nocontrols_pruned",artifacts_path)
lar_csv = CSV.read(joinpath(artifact_path(lar_csv_hash), "large_nocontrols_pruned.csv"))


text:
module Caching
export @cache
"""
    @cache name[::type] [< store]

Generate a wrapper around the function name which stores the returned value
for every input combination. That way already calculated parameter combinations
do not need to be recomputed.
The type parameter after the function name is optional. In case it's provided
the storage dictionary is initalized with this type instead of `Any`. Furthermore
if a storage name is defined by `< store` the dictionary is brought into current
scope and is accessbile, otherwise it's only in the scope of the new function.

Let `f(x)` be a function with a costly calculation procedure.

```
julia> _f = @cache f::Float64 < storage

julia> _f(2.4)
  1.006197 seconds (12 allocations: 560 bytes)
0.675463180551151

julia> _f(2.4)
  0.000004 seconds (7 allocations: 208 bytes)
0.675463180551151
```
"""
macro cache(ex)
    # Seperate the function name, the return type and the store name
    # Return type and storename default to :Any and nothing
    func = typeof(ex) == Symbol ? ex : ex.args[1] == :< ? ex.args[2] : ex
    dtype = :(Any)
    if typeof(func) == Expr && func.head == :(::)
        dtype = func.args[2]
        func = func.args[1]
    end

    # If cached is used directly on a function obtain the type from the
    # function definition
    if typeof(ex) != Symbol && ex.head == :function && typeof(ex.args[1]) == Expr && ex.args[1].head == :(::)
        dtype = ex.args[1].args[2]
    end

    store = typeof(ex) == Symbol ? nothing : ex.args[1] == :< ? ex.args[3] : nothing
    func, dtype, store

    # Generate function call to generate anonymous function which passes the
    # input to the underlying function after checking for previously made
    # calculations.
    ex2 = Expr(:call,(Expr(:->,Expr(:tuple,:(store=Dict{UInt,$dtype}())),
                           Expr(:block, :((args...)-> get!(store,Caching.hashkeys(args)) do
                                          $func(args...)
                                          end
                                         )
                               )
                          )
                     )
              )

    # In case a dictionary for store was defined add it to the function.
    if store != nothing
        # Push variable
        push!(ex2.args,store)
        ex2 = Expr(:block,:($store = Dict{UInt,$dtype}()),ex2)
    end

    # Call was done directly during function definition -> override function
    # name
    if typeof(ex) != Symbol && ex.head == :function
        # find function name
        fn_name = ex
        while typeof(fn_name) == Expr
            fn_name = fn_name.args[1]
        end
        ex2 = quote
            $(fn_name) = ($ex2)
        end
    end

    esc(ex2)
end

function hashkeys(tup::Tuple)
    hash(map(tup) do xi
        hash(xi)
    end)
end

end


text:
let
    typedef = Typedef("ends_during", "ends_during", "external", "RO:0002093")
    @test isa(typedef, Typedef)
end


text:
using ChainRulesCore: backing
using TemporalGPs:
    Gaussian,
    harmonise,
    Forward,
    Reverse,
    GaussMarkovModel,
    LGSSM,
    ordering,
    SmallOutputLGC,
    posterior_and_lml,
    predict,
    conditional_rand,
    AbstractLGC,
    dim_out,
    dim_in

# Make FiniteDifferences work with some of the types in this package. Shame this isn't
# automated...

import FiniteDifferences: to_vec

function to_vec(x::Fill)
    x_vec, back_vec = to_vec(FillArrays.getindex_value(x))
    function Fill_from_vec(x_vec)
        return Fill(back_vec(x_vec), length(x))
    end
    return x_vec, Fill_from_vec
end

function to_vec(x::Union{Zeros, Ones})
    return Vector{eltype(x)}(undef, 0), _ -> x
end

function to_vec(::Missing)
    Missing_from_vec(::Any) = missing
    return Bool[], Missing_from_vec
end

# I'M OVERRIDING FINITEDIFFERENCES DEFINITION HERE. THIS IS BAD.
function to_vec(x::Diagonal)
    v, diag_from_vec = to_vec(x.diag)
    Diagonal_from_vec(v) = Diagonal(diag_from_vec(v))
    return v, Diagonal_from_vec
end

function to_vec(x::T) where {T<:NamedTuple}
    isempty(fieldnames(T)) && throw(error("Expected some fields. None found."))
    vecs_and_backs = map(name->to_vec(getfield(x, name)), fieldnames(T))
    vecs, backs = first.(vecs_and_backs), last.(vecs_and_backs)
    x_vec, back = to_vec(vecs)
    function namedtuple_to_vec(x′_vec)
        vecs′ = back(x′_vec)
        x′s = map((back, vec)->back(vec), backs, vecs′)
        return (; zip(fieldnames(T), x′s)...)
    end
    return x_vec, namedtuple_to_vec
end

function to_vec(x::T) where {T<:StaticArray}
    x_dense = collect(x)
    x_vec, back_vec = to_vec(x_dense)
    function StaticArray_to_vec(x_vec)
        return T(back_vec(x_vec))
    end
    return x_vec, StaticArray_to_vec
end

function to_vec(x::Adjoint{<:Any, T}) where {T<:StaticVector}
    x_vec, back = to_vec(Matrix(x))
    Adjoint_from_vec(x_vec) = Adjoint(T(conj!(vec(back(x_vec)))))
    return x_vec, Adjoint_from_vec
end

function to_vec(x::Tuple{})
    empty_tuple_from_vec(v) = x
    return Bool[], empty_tuple_from_vec
end

function to_vec(x::StructArray{T}) where {T}
    x_vec, x_fields_from_vec = to_vec(getfield(x, :components))
    function StructArray_from_vec(x_vec)
        x_field_vecs = x_fields_from_vec(x_vec)
        return StructArray{T}(Tuple(x_field_vecs))
    end
    return x_vec, StructArray_from_vec
end

# Fallback method for `to_vec`. Won't always do what you wanted, but should be fine a decent
# chunk of the time.
to_vec(x) = generic_struct_to_vec(x)

function generic_struct_to_vec(x::T) where {T}
    Base.isstructtype(T) || throw(error("Expected a struct type"))

    val_vecs_and_backs = map(name -> to_vec(getfield(x, name)), fieldnames(T))
    vals = first.(val_vecs_and_backs)
    backs = last.(val_vecs_and_backs)
    v, vals_from_vec = to_vec(vals)

    function structtype_from_vec(v::Vector{<:Real})
        val_vecs = vals_from_vec(v)
        vals = map((b, v) -> b(v), backs, val_vecs)
        return T(vals...)
    end
    return v, structtype_from_vec
end

to_vec(x::TemporalGPs.RectilinearGrid) = generic_struct_to_vec(x)

function to_vec(f::GP)
    gp_vec, t_from_vec = to_vec((f.mean, f.kernel))
    function GP_from_vec(v)
        (m, k) = t_from_vec(v)
        return GP(m, k)
    end
    return gp_vec, GP_from_vec
end

function to_vec(X::BlockDiagonal)
    Xs = blocks(X)
    Xs_vec, Xs_from_vec = to_vec(Xs)

    function BlockDiagonal_from_vec(Xs_vec)
        Xs = Xs_from_vec(Xs_vec)
        return BlockDiagonal(Xs)
    end

    return Xs_vec, BlockDiagonal_from_vec
end

function to_vec(::typeof(identity))
    Identity_from_vec(v) = identity
    return Bool[], Identity_from_vec
end

function to_vec(x::RegularSpacing)
    RegularSpacing_from_vec(v) = RegularSpacing(v[1], v[2], x.N)
    return [x.t0, x.Δt], RegularSpacing_from_vec
end

to_vec(::Nothing) = Bool[], _ -> nothing

# Ensure that to_vec works for the types that we care about in this package.
@testset "custom FiniteDifferences stuff" begin
    @testset "NamedTuple" begin
        a, b = 5.0, randn(2)
        t = (a=a, b=b)
        nt_vec, back = to_vec(t)
        @test nt_vec isa Vector{Float64}
        @test back(nt_vec) == t
    end
    @testset "Fill" begin
        @testset "$(typeof(val))" for val in [5.0, randn(3)]
            x = Fill(val, 5)
            x_vec, back = to_vec(x)
            @test x_vec isa Vector{Float64}
            @test back(x_vec) == x
        end
    end
    @testset "Zeros{T}" for T in [Float32, Float64]
        x = Zeros{T}(4)
        x_vec, back = to_vec(x)
        @test x_vec isa Vector{eltype(x)}
        @test back(x_vec) == x
    end
    @testset "gaussian" begin
        @testset "Gaussian" begin
            x = TemporalGPs.Gaussian(randn(3), randn(3, 3))
            x_vec, back = to_vec(x)
            @test back(x_vec) == x
        end
    end
    @testset "to_vec(::SmallOutputLGC)" begin
        A = randn(2, 2)
        a = randn(2)
        Q = randn(2, 2)
        model = SmallOutputLGC(A, a, Q)
        model_vec, model_from_vec = to_vec(model)
        @test model_vec isa Vector{<:Real}
        @test model_from_vec(model_vec) == model
    end
    @testset "to_vec(::GaussMarkovModel)" begin
        N = 11
        A = [randn(2, 2) for _ in 1:N]
        a = [randn(2) for _ in 1:N]
        Q = [randn(2, 2) for _ in 1:N]
        H = [randn(3, 2) for _ in 1:N]
        h = [randn(3) for _ in 1:N]
        x0 = TemporalGPs.Gaussian(randn(2), randn(2, 2))
        gmm = TemporalGPs.GaussMarkovModel(Forward(), A, a, Q, x0)

        gmm_vec, gmm_from_vec = to_vec(gmm)
        @test gmm_vec isa Vector{<:Real}
        @test gmm_from_vec(gmm_vec) == gmm
    end
    @testset "StructArray" begin
        x = StructArray([Gaussian(randn(2), randn(2, 2)) for _ in 1:10])
        x_vec, x_from_vec = to_vec(x)
        @test x_vec isa Vector{<:Real}
        @test x_from_vec(x_vec) == x
    end
    @testset "to_vec(::LGSSM)" begin
        N = 11

        # Build GaussMarkovModel.
        A = [randn(2, 2) for _ in 1:N]
        a = [randn(2) for _ in 1:N]
        Q = [randn(2, 2) for _ in 1:N]
        x0 = Gaussian(randn(2), randn(2, 2))
        gmm = GaussMarkovModel(Forward(), A, a, Q, x0)

        # Build LGSSM.
        H = [randn(3, 2) for _ in 1:N]
        h = [randn(3) for _ in 1:N]
        Σ = [randn(3, 3) for _ in 1:N]
        model = TemporalGPs.LGSSM(gmm, StructArray(map(SmallOutputLGC, H, h, Σ)))

        model_vec, model_from_vec = to_vec(model)
        @test model_from_vec(model_vec) == model
    end
    @testset "to_vec(::BlockDiagonal)" begin
        Ns = [3, 5, 1]
        Xs = map(N -> randn(N, N), Ns)
        X = BlockDiagonal(Xs)

        X_vec, X_from_vec = to_vec(X)
        @test X_vec isa Vector{<:Real}
        @test X_from_vec(X_vec) == X
    end
end

my_zero(x) = zero(x)
my_zero(x::AbstractArray{<:Real}) = zero(x)
my_zero(x::AbstractArray) = map(my_zero, x)
my_zero(x::Tuple) = map(my_zero, x)

# My version of isapprox
function fd_isapprox(x_ad::Nothing, x_fd, rtol, atol)
    return fd_isapprox(x_fd, my_zero(x_fd), rtol, atol)
end
function fd_isapprox(x_ad::AbstractArray, x_fd::AbstractArray, rtol, atol)
    return all(fd_isapprox.(x_ad, x_fd, rtol, atol))
end
function fd_isapprox(x_ad::Real, x_fd::Real, rtol, atol)
    return isapprox(x_ad, x_fd; rtol=rtol, atol=atol)
end
function fd_isapprox(x_ad::NamedTuple, x_fd, rtol, atol)
    f = (x_ad, x_fd)->fd_isapprox(x_ad, x_fd, rtol, atol)
    return all([f(getfield(x_ad, key), getfield(x_fd, key)) for key in keys(x_ad)])
end
function fd_isapprox(x_ad::Tuple, x_fd::Tuple, rtol, atol)
    return all(map((x, x′)->fd_isapprox(x, x′, rtol, atol), x_ad, x_fd))
end
function fd_isapprox(x_ad::Dict, x_fd::Dict, rtol, atol)
    return all([fd_isapprox(get(()->nothing, x_ad, key), x_fd[key], rtol, atol) for
        key in keys(x_fd)])
end
function fd_isapprox(x::Gaussian, y::Gaussian, rtol, atol)
    return isapprox(x.m, y.m; rtol=rtol, atol=atol) &&
        isapprox(x.P, y.P; rtol=rtol, atol=atol)
end
function fd_isapprox(x::Real, y::Zero, rtol, atol)
    return fd_isapprox(x, zero(x), rtol, atol)
end
fd_isapprox(x::Zero, y::Real, rtol, atol) = fd_isapprox(y, x, rtol, atol)

function fd_isapprox(x_ad::T, x_fd::T, rtol, atol) where {T<:NamedTuple}
    f = (x_ad, x_fd)->fd_isapprox(x_ad, x_fd, rtol, atol)
    return all([f(getfield(x_ad, key), getfield(x_fd, key)) for key in keys(x_ad)])
end

function fd_isapprox(x::T, y::T, rtol, atol) where {T}
    if !isstructtype(T)
        throw(ArgumentError("Non-struct types are not supported by this fallback."))
    end

    return all(n -> fd_isapprox(getfield(x, n), getfield(y, n), rtol, atol), fieldnames(T))
end

function adjoint_test(
    f, ȳ, x::Tuple, ẋ::Tuple;
    rtol=1e-9,
    atol=1e-9,
    fdm=central_fdm(5, 1),
    test=true,
    check_infers=true,
    context=NoContext(),
    kwargs...,
)
    # Compute <Jᵀ ȳ, ẋ> = <x̄, ẋ> using Zygote.
    y, pb = Zygote._pullback(context, f, x...)

    # Check type inference if requested.
    if check_infers
        # @descend only works if you `using Cthulhu`.
        # @descend Zygote._pullback(context, f, x...)
        # @descend pb(ȳ)

        # @code_warntype Zygote._pullback(context, f, x...)
        # @code_warntype pb(ȳ)
        @inferred Zygote._pullback(context, f, x...)
        @inferred pb(ȳ)
    end

    x̄ = pb(ȳ)[2:end]

    # @show x̄
    # @show harmonise(Zygote.wrap_chainrules_input(x̄), ẋ)[1]
    inner_ad = dot(harmonise(Zygote.wrap_chainrules_input(x̄), ẋ)...)

    # Approximate <ȳ, J ẋ> = <ȳ, ẏ> using FiniteDifferences.
    # @show harmonise(j′vp(fdm, f, ȳ, x...), ẋ)[1]
    # @show typeof(j′vp(fdm, f, ȳ, x...))
    ẏ = jvp(fdm, f, zip(x, ẋ)...)
    inner_fd = dot(harmonise(Zygote.wrap_chainrules_input(ȳ), ẏ)...)

    @show inner_fd - inner_ad

    # Check that Zygote didn't modify the forwards-pass.
    test && @test fd_isapprox(y, f(x...), rtol, atol)

    # Check for approximate agreement in "inner-products".
    test && @test fd_isapprox(inner_ad, inner_fd, rtol, atol)

    return x̄
end

function adjoint_test(f, input::Tuple; kwargs...)
    Δoutput = rand_zygote_tangent(f(input...))
    return adjoint_test(f, Δoutput, input; kwargs...)
end

function adjoint_test(f, Δoutput, input::Tuple; kwargs...)
    ∂input = map(rand_tangent, input)
    return adjoint_test(f, Δoutput, input, ∂input; kwargs...)
end

function print_adjoints(adjoint_ad, adjoint_fd, rtol, atol)
    @show typeof(adjoint_ad), typeof(adjoint_fd)

    # println("ad")
    # display(adjoint_ad)
    # println()

    # println("fd")
    # display(adjoint_fd)
    # println()

    adjoint_ad, adjoint_fd = to_vec(adjoint_ad)[1], to_vec(adjoint_fd)[1]
    println("atol is $atol, rtol is $rtol")
    println("ad, fd, abs, rel")
    abs_err = abs.(adjoint_ad .- adjoint_fd)
    rel_err = abs_err ./ adjoint_ad
    display([adjoint_ad adjoint_fd abs_err rel_err])
    println()
end

using BenchmarkTools

# Also checks the forwards-pass because it's helpful.
function check_adjoint_allocations(
    f, Δoutput, input::Tuple;
    context=NoContext(),
    max_primal_allocs=0,
    max_forward_allocs=0,
    max_backward_allocs=0,
    kwargs...,
)
    _, pb = _pullback(context, f, input...)

    # primal_allocs = allocs(@benchmark($f($input...); samples=1, evals=1))
    # forward_allocs = allocs(
    #     @benchmark(_pullback($context, $f, $input...); samples=1, evals=1),
    # )
    # backward_allocs = allocs(@benchmark $pb($Δoutput) samples=1 evals=1)

    primal_allocs = allocs(@benchmark($f($input...)))
    forward_allocs = allocs(
        @benchmark(_pullback($context, $f, $input...)),
    )
    backward_allocs = allocs(@benchmark $pb($Δoutput))

    # @show primal_allocs
    # @show forward_allocs
    # @show backward_allocs

    @test primal_allocs <= max_primal_allocs
    @test forward_allocs <= max_forward_allocs
    @test backward_allocs <= max_backward_allocs
end

function check_adjoint_allocations(f, input::Tuple; kwargs...)
    return check_adjoint_allocations(f, rand_zygote_tangent(f(input...)), input; kwargs...)
end

function benchmark_adjoint(f, ȳ, args...; disp=false)
    disp && println("primal")
    primal = @benchmark($f($args...); samples=1, evals=1)
    if disp
        display(primal)
        println()
    end

    disp && println("pullback generation")
    forward_pass = @benchmark(Zygote.pullback($f, $args...); samples=1, evals=1)
    if disp
        display(forward_pass)
        println()
    end

    y, back = Zygote.pullback(f, args...)

    disp && println("pullback evaluation")
    reverse_pass = @benchmark($back($ȳ); samples=1, evals=1)
    if disp
        display(reverse_pass)
        println()
    end

    return primal, forward_pass, reverse_pass
end

function test_interface(
    rng::AbstractRNG, conditional::AbstractLGC, x::Gaussian;
    check_infers=true, check_adjoints=true, check_allocs=true, kwargs...,
)
    x_val = rand(rng, x)
    y = conditional_rand(rng, conditional, x_val)

    @testset "rand" begin
        @test length(y) == dim_out(conditional)
        args = (TemporalGPs.ε_randn(rng, conditional), conditional, x_val)
        check_infers && @inferred conditional_rand(args...)
        if check_adjoints
            adjoint_test(
                conditional_rand, args;
                check_infers=check_infers, kwargs...,
            )
        end
        if check_allocs
            check_adjoint_allocations(conditional_rand, args; kwargs...)
        end
    end

    @testset "predict" begin
        @test predict(x, conditional) isa Gaussian
        check_infers && @inferred predict(x, conditional)
        check_adjoints && adjoint_test(predict, (x, conditional); kwargs...)
        check_allocs && check_adjoint_allocations(predict, (x, conditional); kwargs...)
    end

    conditional isa ScalarOutputLGC || @testset "predict_marginals" begin
        @test predict_marginals(x, conditional) isa Gaussian
        pred = predict(x, conditional)
        pred_marg = predict_marginals(x, conditional)
        @test mean(pred_marg) ≈ mean(pred)
        @test diag(cov(pred_marg)) ≈ diag(cov(pred))
        @test cov(pred_marg) isa Diagonal
    end

    @testset "posterior_and_lml" begin
        args = (x, conditional, y)
        @test posterior_and_lml(args...) isa Tuple{Gaussian, Real}
        check_infers && @inferred posterior_and_lml(args...)
        if check_adjoints
            (Δx, Δlml) = rand_zygote_tangent(posterior_and_lml(args...))
            ∂args = map(rand_tangent, args)
            adjoint_test(posterior_and_lml, (Δx, Δlml), args, ∂args)
            adjoint_test(posterior_and_lml, (Δx, nothing), args, ∂args)
            adjoint_test(posterior_and_lml, (nothing, Δlml), args, ∂args)
            adjoint_test(posterior_and_lml, (nothing, nothing), args, ∂args)
        end
        if check_allocs
            (Δx, Δlml) = rand_zygote_tangent(posterior_and_lml(args...))
            check_adjoint_allocations(posterior_and_lml, (Δx, Δlml), args; kwargs...)
            check_adjoint_allocations(posterior_and_lml, (nothing, Δlml), args; kwargs...)
            check_adjoint_allocations(posterior_and_lml, (Δx, nothing), args; kwargs...)
            check_adjoint_allocations(posterior_and_lml, (nothing, nothing), args; kwargs...)
        end
    end
end

"""
    test_interface(
        rng::AbstractRNG, ssm::AbstractLGSSM;
        check_infers=true, check_adjoints=true, check_allocs=true, kwargs...
    )

Basic consistency tests that any LGSSM should be able to satisfy. The purpose of these tests
is not to ensure correctness of any given implementation, only to ensure that it is self-
consistent and implements the required interface.
"""
function test_interface(
    rng::AbstractRNG, ssm::AbstractLGSSM;
    check_infers=true, check_adjoints=true, check_allocs=true, kwargs...
)
    y_no_missing = rand(rng, ssm)

    @testset "rand" begin
        @test is_of_storage_type(y_no_missing[1], storage_type(ssm))
        @test y_no_missing isa AbstractVector
        @test length(y_no_missing) == length(ssm)
        check_infers && @inferred rand(rng, ssm)
        if check_adjoints
            adjoint_test(
                ssm -> rand(MersenneTwister(123456), ssm), (ssm, );
                check_infers=check_infers, kwargs...,
            )
        end
        if check_allocs
            check_adjoint_allocations(rand, (rng, ssm); kwargs...)
        end
    end

    @testset "basics" begin
        @inferred storage_type(ssm)
        @test length(ssm) == length(y_no_missing)
    end

    @testset "marginals" begin
        xs = marginals(ssm)
        @test is_of_storage_type(xs, storage_type(ssm))
        @test xs isa AbstractVector{<:Gaussian}
        @test length(xs) == length(ssm)
        check_infers && @inferred marginals(ssm)
        if check_adjoints
            adjoint_test(marginals, (ssm, ); check_infers=check_infers, kwargs...)
        end
        if check_allocs
            check_adjoint_allocations(marginals, (ssm, ); kwargs...)
        end
    end

    @testset "$(data.name)" for data in [
        (name="no-missings", y=y_no_missing),
        # (name="with-missings", y=y_missing),
    ]
        _check_infers = data.name == "with-missings" ? false : check_infers

        y = data.y
        @testset "logpdf" begin
            lml = logpdf(ssm, y)
            @test lml isa Real
            @test is_of_storage_type(lml, storage_type(ssm))
            _check_infers && @inferred logpdf(ssm, y)
        end
        @testset "_filter" begin
            xs = _filter(ssm, y)
            @test is_of_storage_type(xs, storage_type(ssm))
            @test xs isa AbstractVector{<:Gaussian}
            @test length(xs) == length(ssm)
            _check_infers && @inferred _filter(ssm, y)
        end
        @testset "posterior" begin
            posterior_ssm = posterior(ssm, y)
            @test length(posterior_ssm) == length(ssm)
            @test ordering(posterior_ssm) != ordering(ssm)
            _check_infers && @inferred posterior(ssm, y)
        end

        # Hack to only run the AD tests if requested.
        @testset "adjoints" for _ in (check_adjoints ? [1] : [])
            adjoint_test(logpdf, (ssm, y); check_infers=_check_infers, kwargs...)
            adjoint_test(_filter, (ssm, y); check_infers=_check_infers, kwargs...)
            adjoint_test(posterior, (ssm, y); check_infers=_check_infers, kwargs...)

            if check_allocs
                check_adjoint_allocations(logpdf, (ssm, y); kwargs...)
                check_adjoint_allocations(_filter, (ssm, y); kwargs...)
                check_adjoint_allocations(posterior, (ssm, y); kwargs...)
            end
        end
    end
end

_diag(x) = diag(x)
_diag(x::Real) = x

function FiniteDifferences.rand_tangent(rng::AbstractRNG, A::StaticArray)
    return map(x -> rand_tangent(rng, x), A)
end

FiniteDifferences.rand_tangent(::AbstractRNG, ::Base.OneTo) = Zero()

# Hacks to make rand_tangent play nicely with Zygote.
rand_zygote_tangent(A) = Zygote.wrap_chainrules_output(FiniteDifferences.rand_tangent(A))

Zygote.wrap_chainrules_output(x::Array) = map(Zygote.wrap_chainrules_output, x)

function Zygote.wrap_chainrules_input(x::Array)
    return map(Zygote.wrap_chainrules_input, x)
end

function LinearAlgebra.dot(A::Composite, B::Composite)
    mutual_names = intersect(propertynames(A), propertynames(B))
    if length(mutual_names) == 0
        return 0
    else
        return sum(n -> dot(getproperty(A, n), getproperty(B, n)), mutual_names)
    end
end


text:
struct OpenTypeFont
    cmap::CharToGlyph
    head::FontHeader
    hhea::HorizontalHeader
    hmtx::HorizontalMetrics
    vhea::VerticalHeader
    vmtx::VerticalMetrics
    maxp::MaximumProfile
    name
    os_2
    post
    glyphs::Vector{Union{Nothing,Glyph}}
end

"""
Parse an OpenType font from `io`.

A specification for OpenType font files is available
at https://docs.microsoft.com/en-us/typography/opentype/spec/otff
"""
function Base.parse(io::IO, ::Type{OpenTypeFont})
    io = correct_endianess(io)
    sfnt = read(io, UInt32)
    sfnt in (0x00010000, 0x4F54544F) || error("Invalid format: unknown SFNT version. Expected 0x00010000 or 0x4F54544F.")
    ntables = read(io, UInt16)
    search_range = read(io, UInt16)
    entry_selector = read(io, UInt16)
    range_shift = read(io, UInt16)
    table_records = map(_ -> parse(TableRecord, io), 1:ntables)
    nav = TableNavigationMap(table_records)
    validate(io, nav)

    # head
    head = read_table(Base.Fix2(parse, FontHeader), io, nav["head"])

    # maximum profile
    maxp = read_table(Base.Fix2(parse, MaximumProfile), io, nav["maxp"])

    # character to glyph map
    cmap = read_table(io -> parse(io, nav, CharToGlyph), io, nav["cmap"])

    # index to location
    loca = read_table(io -> parse(io, IndexToLocation, maxp, head), io, nav["loca"])

    # glyphs
    glyphs = read_table(io -> read_glyphs(io, head, maxp, nav, loca), io, nav["glyf"])

    # metrics
    hhea = read_table(Base.Fix2(parse, HorizontalHeader), io, nav["hhea"])
    vhea = read_table(Base.Fix2(parse, VerticalHeader), io, nav["vhea"])
    hmtx = read_table(io -> parse(io, HorizontalMetrics, hhea, maxp), io, nav["hmtx"])
    vmtx = read_table(io -> parse(io, VerticalMetrics, vhea, maxp), io, nav["vmtx"])

    OpenTypeFont(cmap, head, hhea, hmtx, vhea, vmtx, maxp, nothing, nothing, nothing, glyphs)
end

OpenTypeFont(file::AbstractString) = open(Base.Fix2(parse, OpenTypeFont), file)

Base.getindex(font::OpenTypeFont, char::Char) = Glyph(font, char)

function glyph_index(font::OpenTypeFont, char::Char; pick_table=first)
    tables = values(font.cmap.tables)
    table = pick_table(tables)
    if table isa ByteEncodingTable
        return table.glyph_id_array[UInt8(char)] + 1
    elseif table isa SegmentedCoverage
        offset = 0
        char_uint = UInt32(char)
        for group in table.groups
            if char_uint in group.char_range
                return group.start_glyph_id + (char_uint - group.char_range.start) + 1
            end
        end
    else
        error("Unsupported table type $(typeof(table))")
    end
end

Glyph(font::OpenTypeFont, char::Char; pick_table=first) = font.glyphs[glyph_index(font, char; pick_table)]

function GeometryExperiments.boundingelement(glyph::Glyph)
    Scaling(glyph.header.xmax - glyph.header.xmin, glyph.header.ymax - glyph.header.ymin)(PointSet(HyperCube(0.5), Point{2,Float64}))
end


text:
# This file is a part of ElasticArrays.jl, licensed under the MIT License (MIT).

using Base: @propagate_inbounds
using Base.MultiplicativeInverses: SignedMultiplicativeInverse

"""
    ElasticArray{T,N,M} <: DenseArray{T,N}

An `ElasticArray` can grow/shrink in its last dimension. `N` is the total
number of dimensions, `M == N - 1` the number of non-resizable dimensions.

Constructors:

    ElasticArray{T}(dims::Integer...)
    convert(ElasticArray, A::AbstractArray)
"""
struct ElasticArray{T,N,M} <: DenseArray{T,N}
    kernel_size::NTuple{M,Int}
    kernel_length::SignedMultiplicativeInverse{Int}
    data::Vector{T}

    function ElasticArray{T}(::UndefInitializer, dims::Integer...) where {T}
        kernel_size, size_lastdim = _split_dims(dims)
        kernel_length = prod(kernel_size)
        data = Vector{T}(undef, kernel_length * size_lastdim)
        new{T,length(dims),length(kernel_size)}(
            kernel_size,
            SignedMultiplicativeInverse{Int}(kernel_length),
            data
        )
    end
end

export ElasticArray

ElasticArray{T,N}(A::AbstractArray{U,N}) where {T,N,U} = copyto!(ElasticArray{T}(undef, size(A)...), A)
ElasticArray{T}(A::AbstractArray{U,N}) where {T,N,U} = ElasticArray{T,N}(A)
ElasticArray(A::AbstractArray{T,N}) where {T,N} = ElasticArray{T,N}(A)

Base.convert(::Type{ElasticArray{T,N}}, A::AbstractArray) where {T,N} = ElasticArray{T,N}(A)
Base.convert(::Type{ElasticArray{T}}, A::AbstractArray) where {T} = ElasticArray{T}(A)
Base.convert(::Type{ElasticArray}, A::AbstractArray) = ElasticArray(A)

function _split_resize_dims(A::ElasticArray, dims::NTuple{N,Integer}) where {N}
    kernel_size, size_lastdim = _split_dims(dims)
    kernel_size != A.kernel_size && throw(ArgumentError("Can only resize last dimension of an ElasticArray"))
    kernel_size, size_lastdim
end

import Base.==
(==)(A::ElasticArray, B::ElasticArray) =
    ndims(A) == ndims(B) && A.kernel_size == B.kernel_size && A.data == B.data

Base.parent(A::ElasticArray) = A.data

Base.size(A::ElasticArray) = (A.kernel_size..., div(length(eachindex(A.data)), A.kernel_length))
@propagate_inbounds Base.getindex(A::ElasticArray, i::Integer) = getindex(A.data, i)
@propagate_inbounds Base.setindex!(A::ElasticArray, x, i::Integer) = setindex!(A.data, x, i)
@inline Base.IndexStyle(A::ElasticArray) = IndexStyle(A.data)

Base.length(A::ElasticArray) = length(A.data)

Base.dataids(A::ElasticArray) = Base.dataids(A.data)

@inline function Base.resize!(A::ElasticArray{T,N}, dims::Vararg{Integer,N}) where {T,N}
    kernel_size, size_lastdim = _split_resize_dims(A, dims)
    resize!(A.data, A.kernel_length.divisor * size_lastdim)
    A
end

@inline function Base.sizehint!(A::ElasticArray{T,N}, dims::Vararg{Integer,N}) where {T,N}
    kernel_size, size_lastdim = _split_resize_dims(A, dims)
    sizehint!(A.data, A.kernel_length.divisor * size_lastdim)
    A
end

function Base.append!(dest::ElasticArray, src::AbstractArray)
    rem(length(eachindex(src)), dest.kernel_length) != 0 && throw(DimensionMismatch("Can't append, length of source array is incompatible"))
    append!(dest.data, src)
    dest
end

function Base.prepend!(dest::ElasticArray, src::AbstractArray)
    rem(length(eachindex(src)), dest.kernel_length) != 0 && throw(DimensionMismatch("Can't prepend, length of source array is incompatible"))
    prepend!(dest.data, src)
    dest
end

@inline function _copyto_impl!(dest::ElasticArray, args...)
    copyto!(dest.data, args...)
    dest
end

@inline Compat.copyto!(dest::ElasticArray, doffs::Integer, src::AbstractArray, args::Integer...) = _copyto_impl!(dest, doffs, src, args...)
@inline Compat.copyto!(dest::ElasticArray, src::AbstractArray) = _copyto_impl!(dest, src)

@inline Compat.copyto!(dest::ElasticArray, doffs::Integer, src::ElasticArray, args::Integer...) = _copyto_impl!(dest, doffs, src, args...)
@inline Compat.copyto!(dest::ElasticArray, src::ElasticArray) = _copyto_impl!(dest, src)

@inline Compat.copyto!(dest::AbstractArray, doffs::Integer, src::ElasticArray, args::Integer...) = copyto!(dest, doffs, src.data, args...)
@inline Compat.copyto!(dest::AbstractArray, src::ElasticArray) = copyto!(dest, src.data)

Base.similar(::Type{ElasticArray{T}}, dims::Dims{N}) where {T,N} = ElasticArray{T}(undef, dims...)

Base.unsafe_convert(::Type{Ptr{T}}, A::ElasticArray{T}) where T = Base.unsafe_convert(Ptr{T}, A.data)

Base.pointer(A::ElasticArray, i::Integer) = pointer(A.data, i)


text:
module MetaModule
using  ...Dispatch.Applications
using  ...Dispatch.TopMetaTables
using  ...Dispatch
import Base.Meta: quot
export @metamodule

const MM = Dispatch.Applications.MACROMETHODS
const MF = Dispatch.Applications.METAFUNCTIONS

#-----------------------------------------------------------------------------------
# @metamodule
#-----------------------------------------------------------------------------------

@macromethod metamodule(:R{:import,    *{path}})  esc(importcode(path))
@macromethod metamodule(:R{:importall, *{path}})  esc(importallcode(path))
@macromethod metamodule(:R{:export,    *{names}}) esc(exportcode(names))

@macromethod metamodule(:R{:toplevel}) nothing
@macromethod metamodule(:R{:toplevel,x,*{xs}}) esc(quote
  @metamodule $x
  @metamodule $(Expr(:toplevel, xs...))
end)

#-----------------------------------------------------------------------------------
# Code generation.
#-----------------------------------------------------------------------------------

function importcode(path)
  name = last(path)
  top  = gettable(path[end])
  quote
    import $(path...)
    import $(path[1:end-1]...).($(path[end-1]))
    $(import_metatable!)($top, $(quot(name)), $(path[end-1]))
  end
end

exportcode(names) = quote

  eval(:(export $($names...)))
  for name in $names
    top = $gettable(name)
    $(export_metatable!)(top, name)
  end
end

function importallcode(path)
  M = path[end]
  quote

    eval(Expr(:import, $path..., $(quot(M))))

    metafun_exports  = $metatable_exports($MM, $M)
    macromet_exports = $metatable_exports($MF, $M)

    for exp in $union(metafun_exports, macromet_exports)
      imp = Expr(:import, $vcat($path, exp)...)
      eval($macrocall_ex("@metamodule", imp))
    end
  end
end

#-----------------------------------------------------------------------------------
# Utility functions.
#-----------------------------------------------------------------------------------

gettable(name) =
  startswith(string(name), "@") ? MM : MF

function macrocall_ex(name, args...)
  ex = :(@x $(args...))
  ex.args[1] = Symbol(name)
  ex
end

end


text:
import Base.iterate

abstract type TreeNode end

mutable struct BranchNode <: TreeNode
    children :: Array{BranchNode, 1}
    data::Union{String, LinearClassifier}
    extrainfo::Nothing
end

struct NullNode <: TreeNode
end

struct TreeNodeLeaves{T <: TreeNode}
    node::T
end

const nullnode = NullNode()

function iterate(leaves::TreeNodeLeaves, state::Tuple{Vector{BranchNode}, Vector{Int}})
    function transverse(substate::Tuple{Vector{BranchNode}, Vector{Int}})
        if length(substate[1]) == 0
            return nothing
        end
        current = pop!(substate[1])

        if length(current.children) == 0
            println(current.data)
            println(substate[2])
            return ((current.data, copy(substate[2])), substate)
        end
        for index = eachindex(current.children)
            nextnode = current.children[index]
            push!(substate[1], nextnode)
            push!(substate[2], index)
            transverse(substate)
            pop!(substate[2])
        end
    end
    return transverse(state)
end

# function iterate(leaves::TreeNodeLeaves, state::Tuple{Vector{BranchNode}, Vector{Int}})
#     if length(state[1]) == 0
#         return nothing
#     end
#     current = popfirst!(state[1])

#     if length(current.children) == 0
#         return ((current.data, copy(state[2])), state)
#     end

#     for index = eachindex(current.children)
#         nextnode = current.children[index]
#         push!(state[1], nextnode)
#         push!(state[2], index)
#     end
#     return iterate(leaves, state)
# end

function iterate(leaves::TreeNodeLeaves)
    nodes = Vector{BranchNode}()
    indices = Int[]

    push!(nodes, leaves.node)

    return iterate(leaves, (nodes, indices))
end

function leaves_of(root::TreeNode)
    code = Int[]
    function traverse(node::TreeNode, c::Channel)
        if node == nullnode
            return
        end
        if length(node.children) == 0
            put!(c, (node.data, copy(code)))    # notice that we should copy the current state of code
        end
        for index = eachindex(node.children)
            child = node.children[index]
            push!(code, index)
            traverse(child, c)
            pop!(code)
        end
    end
    return Channel((channel_arg) -> traverse(root, channel_arg))
end

function internal_nodes_of(root::TreeNode)
    function traverse(node::TreeNode)
        if node == nullnode
            return
        end
        if length(node.children) != 0
            produce(node)
        end
        for child in node.children
            traverse(child)
        end
    end
    Task(() -> traverse(root))
end

function average_height(tree::TreeNode)
    (h, c) = (0, 0)
    for (_, path) in leaves_of(tree)
        h += length(path)
        c += 1
    end
    h / c
end


text:
module MLP

import ..FCL.FullyConnectedLayer
import ..TF.sigmoid

import ..@import_interface
@import_interface

#a canned simple multilayer perceptron type
immutable MultilayerPerceptron{F, LayerDescriptor} <: MLAlgorithm{F}
  layers::Array{FullyConnectedLayer{F},1}
end

(mlp::MultilayerPerceptron{F, LD}){F,LD}(v::Array) = ml_call(mlp, v)

################################################################################
# constructors

#empty constructor - fills the MLP with zero transitions.  Useful for generating empty MLPs
(::Type{MultilayerPerceptron{F, LD}}){F, LD}() = MultilayerPerceptron{F, LD}(zeros)

#functions only constructor - fills MLP with zero transitions.
(::Type{MultilayerPerceptron{F, LD}}){F, LD}(fns::Array{Function, 1}) = MultilayerPerceptron{F, LD}(zeros, fns)

# rf - 'randomization function' must have the following property:
#   rf(F, n) -> 1-d array of random float F
#   rf(F, n, m) -> 2-d array of random float F.
# transfers - array of transferfunction.  must take F -> F.  An empty array means use the default layer transition
#   (should be sigmoid)
function (::Type{MultilayerPerceptron{F, LD}}){F, LD}(rf::Function, transfers::Array{Function, 1} = Function[])
  #double check that the transfers array has the same order as layer descriptor
  default_constructor = (length(transfers) == 0)
  if (!default_constructor)
    (length(transfers) != length(LD) - 1) && throw(ArgumentError("transfer function list must match length of the descriptors"))
    #test to make sure the transfer function successfully executes across arrays.
    for tf in transfers; testval = tf.(ones(F, 10)); end
  end

  #create an empty layers object.
  layers = FullyConnectedLayer{F}[]
  for idx = 1:(length(LD) - 1)
    if default_constructor
      push!(layers, FullyConnectedLayer{F, LD[idx], LD[idx + 1], sigmoid}(rf))
    else
      push!(layers, FullyConnectedLayer{F, LD[idx], LD[idx + 1], transfers[idx]}(rf))
    end
  end

  MultilayerPerceptron{F, LD}(layers)
end

include("./storage.jl")
include("./serialization.jl")
include("./evaluation.jl")
include("./backpropagation.jl")

end

max_stars_repo_name: interplanetary-robot/GenML

max_issues_repo_name: interplanetary-robot/GenML

max_forks_repo_name: interplanetary-robot/GenML


text:
include("entity.jl")
include("grid.jl")
include("assetgen.jl")
include("qt_utils.jl")

const MAXVTHETA = 15 * Float32(pi) / 180
const MIXRATEROT = Float32(0.5)

const POS_EPS = Float32(-0.001)

const RENDER_EPS = Float32(0.02)

const USE_ASSET_THRESHOLD = 100
const MAX_ASSETS = USE_ASSET_THRESHOLD
const MAX_IMAGE_THEMES = 10

abstract type AbstractBasicAbstractGame <: AbstractGame end

mutable struct BasicAbstractGame <: AbstractBasicAbstractGame
    game::Game

    grid_size::Int

    agent::Entity
    entities::Vector{Entity}
    basic_assets::Vector{Union{Image, Nothing}}
    basic_reflections::Vector{Union{Image, Nothing}}
    main_bg_images_ptr::Union{Vector{Image}, Nothing}

    asset_aspect_ratios::Vector{Float32}
    asset_num_themes::Vector{Int}

    use_procgen_background::Bool
    background_index::Int
    bg_tile_ratio::Float32
    bg_pct_x::Float32

    last_move_action::Int
    move_action::Int
    special_action::Int
    mixrate::Float32
    maxspeed::Float32

    action_vx::Float32
    action_vy::Float32
    action_vrot::Float32

    center_x::Float32
    center_y::Float32

    random_agent_start::Bool
    step_rand_int::Int

    asset_rand_gen::RandGen

    main_width::Int
    main_height::Int
    out_of_bounds_object::Int

    unit::Float32
    view_dim::Float32
    x_off::Float32
    y_off::Float32
    visibility::Float32
    min_visibility::Float32

    grid::Grid{Int}

    function BasicAbstractGame(name::String)

        game = Game(name)
        game.default_action = 4

        grid_size = 0

        agent = Entity()
        entities = []
        basic_assets = []
        basic_reflections = []
        main_bg_images_ptr = nothing

        asset_aspect_ratios = []
        asset_num_themes = []

        use_procgen_background = false
        background_index = 0
        bg_tile_ratio = 0.0
        bg_pct_x = 0.0

        last_move_action = 7
        move_action = 0
        special_action = 0
        mixrate = 0.5
        maxspeed = 0.5

        action_vx = 0.0
        action_vy = 0.0
        action_vrot = 0.0

        center_x = 0.0
        center_y = 0.0

        random_agent_start = true
        step_rand_int = 0

        asset_rand_gen = RandGen()

        main_width = 0
        main_height = 0
        out_of_bounds_object = Int(INVALID_OBJ)

        unit = 0.0
        view_dim = 0.0
        x_off = 0.0
        y_off = 0.0
        visibility = 16.0
        min_visibility = 0.0

        grid = Grid{Int}()

        new(
            game,
            grid_size,
            agent,
            entities,
            basic_assets,
            basic_reflections,
            main_bg_images_ptr,
            asset_aspect_ratios,
            asset_num_themes,
            use_procgen_background,
            background_index,
            bg_tile_ratio,
            bg_pct_x,
            last_move_action,
            move_action,
            special_action,
            mixrate,
            maxspeed,
            action_vx,
            action_vy,
            action_vrot,
            center_x,
            center_y,
            random_agent_start,
            step_rand_int,
            asset_rand_gen,
            main_width,
            main_height,
            out_of_bounds_object,
            unit,
            view_dim,
            x_off,
            y_off,
            visibility,
            min_visibility,
            grid,
        )
    end
end

function get_basic_game(abstract_basic_game::AbstractBasicAbstractGame)

    return abstract_basic_game
end

function get_game(abstract_basic_game::AbstractBasicAbstractGame)

    return get_basic_game(abstract_basic_game).game
end

function game_init(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game = get_basic_game(abstract_basic_game)

    # Missing Options
    #if !basic_game.game.options.use_generated_assets
        load_background_images(abstract_basic_game)
    #end

    if isnothing(basic_game.main_bg_images_ptr)
        # Missing Code
        basic_game.use_procgen_background = true
    else
        basic_game.use_procgen_background = false
    end

    empty!(basic_game.basic_assets)
    empty!(basic_game.basic_reflections)
    empty!(basic_game.asset_aspect_ratios)
    empty!(basic_game.asset_num_themes)

    resize!(basic_game.basic_assets, USE_ASSET_THRESHOLD * MAX_IMAGE_THEMES)
    resize!(basic_game.basic_reflections, USE_ASSET_THRESHOLD * MAX_IMAGE_THEMES)
    resize!(basic_game.asset_aspect_ratios, USE_ASSET_THRESHOLD * MAX_IMAGE_THEMES)
    resize!(basic_game.asset_num_themes, USE_ASSET_THRESHOLD)

    fill!(basic_game.basic_assets, nothing)
    fill!(basic_game.basic_reflections, nothing)
    fill!(basic_game.asset_aspect_ratios, 0)
    fill!(basic_game.asset_num_themes, 0)
    return nothing
end

function initialize_asset_if_necessary(abstract_basic_game::AbstractBasicAbstractGame, img_idx::Int)

    basic_game = get_basic_game(abstract_basic_game)

    if !isnothing(basic_game.basic_assets[img_idx + OFFSET])
        return nothing
    end

    type = img_idx % MAX_ASSETS
    theme = div(img_idx, MAX_ASSETS)

    theme = mask_theme_if_necessary(abstract_basic_game, theme, type)

    aspect_ratio::Float32 = 0.0
    names::Vector{String} = []

    # Missing Options
    #if !basic_game.game.options.use_generated_assets
        asset_for_type(abstract_basic_game, type, names)

        if length(names) == 0
            reserved_asset_for_type(abstract_basic_game, type, names)
        end
    #end

    if length(names) == 0
        pgen = AssetGen(basic_game.asset_rand_gen)
        seed(basic_game.asset_rand_gen, basic_game.game.fixed_asset_seed + type)

        small_image = Image()
        asset_ptr = small_image
        generate_resource(pgen, asset_ptr, 0, 5, use_block_asset(abstract_basic_game, type))

        num_themes = 1
        aspect_ratio = 1.0
    else
        asset_ptr = get_asset_ptr(names[theme + OFFSET])
        num_themes = length(names)
        aspect_ratio = asset_ptr.width / asset_ptr.height
    end

    basic_game.basic_assets[img_idx + OFFSET] = asset_ptr
    basic_game.asset_aspect_ratios[img_idx + OFFSET] = aspect_ratio
    basic_game.asset_num_themes[type + OFFSET] = num_themes

    reflection_ptr = mirror(asset_ptr)
    basic_game.basic_reflections[img_idx + OFFSET] = reflection_ptr
    return nothing
end

function fill_elem(abstract_basic_game::AbstractBasicAbstractGame, x::Int, y::Int, dx::Int, dy::Int, elem::Int)

    basic_game = get_basic_game(abstract_basic_game)

    for j in 0:(dx - 1)
        for k in 0:(dy - 1)
            set(basic_game.grid, x + j, y + k, elem)
        end
    end
    return nothing
end

function check_grid_collisions(abstract_basic_game::AbstractBasicAbstractGame, ent::Entity)

    ax = ent.x
    ay = ent.y
    arx = ent.rx
    ary = ent.ry

    min_x = trunc(Int, ax - (arx + POS_EPS))
    max_x = trunc(Int, ax + (arx + POS_EPS))
    min_y = trunc(Int, ay - (ary + POS_EPS))
    max_y = trunc(Int, ay + (ary + POS_EPS))

    for x in min_x:max_x
        for y in min_y:max_y
            grid_type = get_obj_from_floats(abstract_basic_game, Float32(x), Float32(y))

            if grid_type != Int(SPACE)
                handle_grid_collision(abstract_basic_game, ent, grid_type, x, y)
            end
        end
    end
    return nothing
end

function get_obj_from_floats(abstract_basic_game::AbstractBasicAbstractGame, i::Float32, j::Float32)

    basic_game = get_basic_game(abstract_basic_game)

    if i < 0
        return basic_game.out_of_bounds_object
    end
    if j < 0
        return basic_game.out_of_bounds_object
    end

    return get_obj(abstract_basic_game, floor(Int, i), floor(Int, j))
end

function get_obj(abstract_basic_game::AbstractBasicAbstractGame, x::Int, y::Int)

    basic_game = get_basic_game(abstract_basic_game)

    if !contains(basic_game.grid, x, y)
        return basic_game.out_of_bounds_object
    end

    return get(basic_game.grid, x, y)
end

function push_obj(abstract_basic_game::AbstractBasicAbstractGame, src::Entity, target::Entity, is_horizontal::Bool, depth::Int)

    rsum = is_horizontal ? (src.rx + target.rx) : (src.ry + target.ry)
    delx = target.x - src.x
    dely = target.y - src.y
    t_vx = 0
    t_vy = 0

    if is_horizontal
        t_vx = src.x + sign(delx) * rsum - target.x
    else
        t_vy = src.y + sign(dely) * rsum - target.y
    end

    block = false

    if depth < 5
        block = sub_step(abstract_basic_game, target, t_vx, t_vy, depth + 1)
    end

    if is_horizontal
        target.vx = 0
    else
        target.vy = 0
    end

    return block
end

function sub_step(abstract_basic_game::AbstractBasicAbstractGame, obj::Entity, _vx::Float32, _vy::Float32, depth::Int)

    basic_game = get_basic_game(abstract_basic_game)

    if obj.will_erase
        return false
    end

    ny = obj.y + _vy
    nx = obj.x + _vx

    margin::Float32 = 0.98

    is_horizontal = _vx != 0

    block = false
    reflect = false

    for i in 0:1
        for j in 0:1
            type2 = get_obj_from_floats(abstract_basic_game, nx + obj.rx * margin * (2 * i - 1), ny + obj.ry * margin * (2 * j - 1))
            block = block || is_blocked(abstract_basic_game, obj, type2, is_horizontal)
            reflect = reflect || will_reflect(abstract_basic_game, obj.type, type2)
        end
    end

    if reflect
        if is_horizontal
            if _vx < 0
                delta = ceil(nx - obj.rx) - (nx - obj.rx)
            else
                delta = floor(nx + obj.rx) - (nx + obj.rx)
            end

            obj.vx = -1 * obj.vx
            nx = nx + 2 * delta
        else
            if _vy < 0
                delta = ceil(ny - obj.ry) - (ny - obj.ry)
            else
                delta = floor(ny + obj.ry) - (ny + obj.ry)
            end

            obj.vy = -1 * obj.vy
            ny = ny + 2 * delta
        end
    elseif block
        if is_horizontal
            if basic_game.game.grid_step
                nx = obj.x
            else
                nx = _vx > 0 ? (floor(nx + obj.rx) - obj.rx) : (ceil(nx - obj.rx) + obj.rx)
            end
        else
            if basic_game.game.grid_step
                ny = obj.y
            else
                ny = _vy > 0 ? (floor(ny + obj.ry) - obj.ry) : (ceil(ny - obj.ry) + obj.ry)
            end
        end
    end

    obj.x = nx
    obj.y = ny

    block2 = false

    for i in length(basic_game.entities):-1:1
        m = basic_game.entities[i]

        if m === obj || m.will_erase
            continue
        end

        curr_block = false

        if has_collision(abstract_basic_game, obj, m, POS_EPS)
            if is_blocked_ents(abstract_basic_game, obj, m, is_horizontal)
                curr_block = true
            elseif will_reflect(abstract_basic_game, obj.type, m.type)
                if is_horizontal
                    delx = m.x - obj.x
                    rsum = m.rx + obj.rx
                    obj.x += _vx > 0 ? -2 * (rsum - delx) : 2 * (rsum + delx)
                    obj.vx = -1 * obj.vx
                else
                    dely = m.y - obj.y
                    rsum = m.ry + obj.ry
                    obj.y += _vy > 0 ? -2 * (rsum - dely) : 2 * (rsum + dely)
                    obj.vy = -1 * obj.vy
                end
            end

            if curr_block
                push_obj(abstract_basic_game, m, obj, is_horizontal, depth)
            end
        end

        block2 = block2 || curr_block
    end

    return block || block2
end

function choose_world_dim(abstract_basic_game::AbstractBasicAbstractGame)

    return nothing
end

function handle_agent_collision(abstract_basic_game::AbstractBasicAbstractGame, obj::Entity)

    return nothing
end

function handle_grid_collision(abstract_basic_game::AbstractBasicAbstractGame, obj::Entity, type::Int, i::Int, j::Int)

    return nothing
end

function handle_collision(abstract_basic_game::AbstractBasicAbstractGame, src::Entity, target::Entity)

    return nothing
end

function use_block_asset(abstract_basic_game::AbstractBasicAbstractGame, type::Int)

    return false
end

function get_tile_aspect_ratio(abstract_basic_game::AbstractBasicAbstractGame, type::Entity)

    return Float32(0)
end

function asset_for_type(abstract_basic_game::AbstractBasicAbstractGame, type::Int, names::Vector{String})

    return nothing
end

function reserved_asset_for_type(abstract_basic_game::AbstractBasicAbstractGame, type::Int, names::Vector{String})

    if type == Int(EXPLOSION)
        push!(names, "misc_assets/explosion1.png")
    elseif type == Int(EXPLOSION2)
        push!(names, "misc_assets/explosion2.png")
    elseif type == Int(EXPLOSION3)
        push!(names, "misc_assets/explosion3.png")
    elseif type == Int(EXPLOSION4)
        push!(names, "misc_assets/explosion4.png")
    elseif type == Int(EXPLOSION5)
        push!(names, "misc_assets/explosion5.png")
    elseif type == Int(TRAIL)
        push!(names, "misc_assets/iconCircle_white.png")
    end
    return nothing
end

function load_background_images(abstract_basic_game::AbstractBasicAbstractGame)

    return nothing
end

function image_for_type(abstract_basic_game::AbstractBasicAbstractGame, type::Int)

    return abs(type)
end

function theme_for_grid_obj(abstract_basic_game::AbstractBasicAbstractGame, type::Int)

    return 0
end

function should_preserve_type_themes(abstract_basic_game::AbstractBasicAbstractGame, type::Int)

    return false
end

function mask_theme_if_necessary(abstract_basic_game::AbstractBasicAbstractGame, theme::Int, type::Int)

    basic_game = get_basic_game(abstract_basic_game)

    if basic_game.game.options.restrict_themes && !should_preserve_type_themes(abstract_basic_game, type)
        return 0
    end
    return theme
end

function color_for_type(abstract_basic_game::AbstractBasicAbstractGame, type::Int, theme::Int)

    basic_game = get_basic_game(abstract_basic_game)

    if basic_game.game.options.use_monochrome_assets
        theme = mask_theme_if_necessary(abstract_basic_game, theme, type)

        k = 4
        kcubed = k * k * k
        chunk = div(256, k)

        p1 = 29
        p2 = 19

        new_type = (p1 * (type + 1)) % kcubed
        new_type = (new_type + p2 * theme) % kcubed

        r = chunk * (div(new_type, k * k) + 1) - 1
        g = chunk * (div(new_type, k) % k + 1) - 1
        b = chunk * (new_type % k + 1) - 1
        setcolor(r / 255, g / 255, b / 255)
    end
    return nothing
end

function is_blocked(abstract_basic_game::AbstractBasicAbstractGame, src::Entity, target::Int, is_horizontal::Bool)

    basic_game = get_basic_game(abstract_basic_game)

    if target == Int(WALL_OBJ)
        return true
    end
    if target == basic_game.out_of_bounds_object
        return true
    end

    return false
end

function is_blocked_ents(abstract_basic_game::AbstractBasicAbstractGame, src::Entity, target::Entity, is_horizontal::Bool)

    return is_blocked(abstract_basic_game, src, target.type, is_horizontal)
end

function will_reflect(abstract_basic_game::AbstractBasicAbstractGame, src::Int, target::Int)

    return false
end

function get_agent_acceleration_scale(abstract_basic_game::AbstractBasicAbstractGame)

    return Float32(1.0)
end

function add_entity(abstract_basic_game::AbstractBasicAbstractGame, x::Float32, y::Float32, vx::Float32, vy::Float32, r::Float32, type::Int)

    basic_game = get_basic_game(abstract_basic_game)

    ent = Entity(x, y, vx, vy, r, r, type)
    push!(basic_game.entities, ent)
    return ent
end

function basic_step_object(abstract_basic_game::AbstractBasicAbstractGame, obj::Entity)

    basic_game = get_basic_game(abstract_basic_game)

    if obj.will_erase
        return nothing
    end

    if basic_game.game.grid_step
        num_sub_steps = 1
    else
        num_sub_steps = trunc(Int, 4 * sqrt(obj.vx * obj.vx + obj.vy * obj.vy))
        if num_sub_steps < 4
            num_sub_steps = 4
        end
    end

    pct = Float32(1.0) / num_sub_steps

    cmp = abs(obj.vx) - abs(obj.vy)

    # Resolve ties randomly
    step_x_first = cmp == 0 ? basic_game.step_rand_int % 2 == 0 : (cmp > 0)

    if obj.type == Int(PLAYER)
        if basic_game.action_vx != 0
            step_x_first = true
        end
        if basic_game.action_vy != 0
            step_x_first = false
        end
    end

    vx_pct::Float32 = 0
    vy_pct::Float32 = 0

    for s in 1:num_sub_steps
        block_x = false
        block_y = false

        if step_x_first
            block_x = sub_step(abstract_basic_game, obj, obj.vx * pct, Float32(0), 0)
            block_y = sub_step(abstract_basic_game, obj, Float32(0), obj.vy * pct, 0)
        else
            block_y = sub_step(abstract_basic_game, obj, Float32(0), obj.vy * pct, 0)
            block_x = sub_step(abstract_basic_game, obj, obj.vx * pct, Float32(0), 0)
        end

        if !block_x
            vx_pct += 1
        end
        if !block_y
            vy_pct += 1
        end

        if block_x && block_y
            break
        end
    end

    vx_pct = vx_pct / num_sub_steps
    vy_pct = vy_pct / num_sub_steps

    obj.vx *= vx_pct
    obj.vy *= vy_pct

    return nothing
end

function set_action_xy(abstract_basic_game::AbstractBasicAbstractGame, move_act::Int)

    basic_game = get_basic_game(abstract_basic_game)

    basic_game.action_vx = div(move_act, 3) - 1
    basic_game.action_vy = move_act % 3 - 1
    basic_game.action_vrot = 0
    return nothing
end

function update_agent_velocity(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game = get_basic_game(abstract_basic_game)

    v_scale = get_agent_acceleration_scale(abstract_basic_game)

    basic_game.agent.vx = (1 - basic_game.mixrate) * basic_game.agent.vx
    basic_game.agent.vy = (1 - basic_game.mixrate) * basic_game.agent.vy

    basic_game.agent.vx += basic_game.mixrate * basic_game.maxspeed * basic_game.action_vx * v_scale
    basic_game.agent.vy += basic_game.mixrate * basic_game.maxspeed * basic_game.action_vy * v_scale

    decay_agent_velocity(abstract_basic_game)
    return nothing
end

function decay_agent_velocity(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game = get_basic_game(abstract_basic_game)

    basic_game.agent.vx = 0.9 * basic_game.agent.vx
    basic_game.agent.vy = 0.9 * basic_game.agent.vy
    return nothing
end

function game_step(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game_step(abstract_basic_game)
end

function basic_game_step(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game = get_basic_game(abstract_basic_game)

    basic_game.step_rand_int = randint(basic_game.game.rand_gen, 0, 1000000)
    basic_game.move_action = basic_game.game.action % 9
    basic_game.special_action = 0

    if basic_game.game.action >= 9
        basic_game.special_action = basic_game.game.action - 8
        basic_game.move_action = 4
    end

    if basic_game.move_action != 4
        basic_game.last_move_action = basic_game.move_action
    end

    basic_game.action_vrot = 0
    basic_game.action_vx = 0
    basic_game.action_vy = 0

    set_action_xy(abstract_basic_game, basic_game.move_action)

    if basic_game.game.grid_step
        basic_game.agent.vx = basic_game.action_vx
        basic_game.agent.vy = basic_game.action_vy
    else
        update_agent_velocity(abstract_basic_game)

        basic_game.agent.vrot = MIXRATEROT * basic_game.agent.vrot
        basic_game.agent.vrot += MIXRATEROT * MAXVTHETA * basic_game.action_vrot
    end

    step_entities(abstract_basic_game, basic_game.entities)

    for i in length(basic_game.entities):-1:1
        ent = basic_game.entities[i]

        if has_agent_collision(abstract_basic_game, ent)
            handle_agent_collision(abstract_basic_game, ent)
        end

        if ent.collides_with_entities
            for j in length(basic_game.entities):-1:1
                if i == j
                    continue
                end
                ent2 = basic_game.entities[j]
                if has_collision(abstract_basic_game, ent, ent2, ent.collision_margin) && !ent.will_erase && !ent2.will_erase
                    handle_collision(abstract_basic_game, ent, ent2)
                end
            end
        end

        if ent.smart_step
            check_grid_collisions(abstract_basic_game, ent)
        end
    end

    erase_if_needed(abstract_basic_game)

    basic_game.game.step_data.done = basic_game.game.step_data.done || is_out_of_bounds(abstract_basic_game, basic_game.agent)
    return nothing
end

function erase_if_needed(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game = get_basic_game(abstract_basic_game)

    for i in length(basic_game.entities):-1:1
        e = basic_game.entities[i]

        if e.will_erase || (e.auto_erase && is_out_of_bounds(abstract_basic_game, e))
            deleteat!(basic_game.entities, i)
        end
    end
    return nothing
end

function game_reset(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game_reset(abstract_basic_game)
end

function basic_game_reset(abstract_basic_game::AbstractBasicAbstractGame)

    basic_game = get_basic_game(abstract_basic_game)

    choose_world_dim(abstract_basic_game)

    basic_game.bg_pct_x = rand01(basic_game.game.rand_gen)

    basic_game.grid_size = basic_game.main_width * basic_game.main_height
    resize(basic_game.grid, basic_game.main_width, basic_game.main_height)

    basic_game.background_index = randn(basic_game.game.rand_gen, length(basic_game.main_bg_images_ptr))

    bggen = AssetGen(basic_game.game.rand_gen)

    if basic_game.use_procgen_background
        generate_resource(bggen, basic_game.main_bg_images_ptr[basic_game.background_index])
    end

    empty!(basic_game.entities)

    a_r::Float32 = 0.4

    if basic_game.random_agent_start
        ax = rand01(basic_game.game.rand_gen) * (basic_game.main_width - 2 * a_r) + a_r
        ay = rand01(basic_game.game.rand_gen) * (basic_game.main_height - 2 * a_r) + a_r
    else
        ax = a_r
        ay = a_r
    end

    _agent = Entity(ax, ay, Float32(0), Float32(0), a_r, Int(PLAYER))
    basic_game.agent = _agent
    basic_game.agent.smart_step = true
    basic_game.agent.render_z = 1
    push!(basic_game.entities, basic_game.agent)

    erase_if_needed(abstract_basic_game)

    fill_elem(abstract_basic_game, 0, 0, basic_game.main_width, basic_game.main_height, Int(SPACE))
    return nothing
end

function get_screen_rect(abstract_basic_game::AbstractBasicAbstractGame, x::Float32, y::Float32, dx::Float32, dy::Float32, render_eps::Float32 = Float32(0))

    basic_game = get_basic_game(abstract_basic_game)
    return rect(Point((x - render_eps) * basic_game.unit - basic_game.x_off, (basic_game.view_dim - y - render_eps) * basic_game.unit + basic_game.y_off), (dx + 2 * render_eps) * basic_game.unit, (dy + 2 * render_eps) * basic_game.unit, vertices = true)
end

function get_abs_rect(abstract_basic_game::AbstractBasicAbstractGame, x::Float32, y::Float32, dx::Float32, dy::Float32)

    basic_game = get_basic_game(abstract_basic_game)
    return rect(Point(x * basic_game.unit, y * basic_game.unit), dx * basic_game.unit, dy * basic_game.unit, vertices = true)
end

function get_adjusted_image_rect(abstract_basic_game::AbstractBasicAbstractGame, type::Int, rectangle)

    return rectangle
end

function get_object_rect(abstract_basic_game::AbstractBasicAbstractGame, obj::Entity)

    basic_game = get_basic_game(abstract_basic_game)

    if obj.use_abs_coords
        return get_abs_rect(abstract_basic_game, basic_game.view_dim * (obj.x - obj.rx), basic_game.view_dim * (obj.y + obj.ry), 2 * basic_game.view_dim * obj.rx, 2 * basic_game.view_dim * obj.ry)
    end
    return get_screen_rect(abstract_basic_game, obj.x - obj.rx, obj.y + obj.ry, 2 * obj.rx, 2 * obj.ry)
end

function prepare_for_drawing(abstract_basic_game::AbstractBasicAbstractGame, rect_height::Float32)

    basic_game = get_basic_game(abstract_basic_game)

    basic_game.center_x = basic_game.main_width * 0.5
    basic_game.center_y = basic_game.main_height * 0.5

    if basic_game.game.options.center_agent
        # Missing Code
    else
        basic_game.visibility = basic_game.main_width > basic_game.main_height ? basic_game.main_width : basic_game.main_height
        if basic_game.visibility < basic_game.min_visibility
            basic_game.visibility = basic_game.min_visibility
        end
    end

    raw_unit = 64 / basic_game.visibility
    basic_game.unit = raw_unit * (rect_height / 64)

    basic_game.view_dim = 64 / raw_unit

    basic_game.x_off = basic_game.unit * (basic_game.center_x - basic_game.view_dim / 2)
    basic_game.y_off = basic_game.unit * (basic_game.center_y - basic_game.view_dim / 2)
    return nothing
end

function tile_image(abstract_basic_game::AbstractBasicAbstractGame, image::Image, rectangle, tile_ratio::Float32)

    if tile_ratio != 0
        if tile_ratio < 0
            tile_ratio = -1 * tile_ratio
            num_tiles = trunc(Int, h / (w * tile_ratio))
            if num_tiles < 1
                num_tiles = 1
            end
            tile_height = h / num_tiles
            tile_width = w

            for i in 0:(num_tiles - 1)
                tile_rect = rect(Point(rectangle[2].x, rectangle[2].y + tile_height * i), tile_width, tile_height, vertices = true)
                show(image, tile_rect)
            end
        else
            num_tiles = trunc(Int, w / (h * tile_ratio))
            if num_tiles < 1
                num_tiles = 1
            end
            tile_width = w / num_tiles
            tile_height = h

            for i in 0:(num_tiles - 1)
                tile_rect = rect(Point(rectangle[2].x + tile_width * i, rectangle[2].y), tile_width, tile_height, vertices = true)
                show(image, tile_rect)
            end
        end
    else
        show(image, rectangle)
    end
    return nothing
end

function lookup_asset(abstract_basic_game::AbstractBasicAbstractGame, img_idx::Int, is_reflected::Bool)

    basic_game = get_basic_game(abstract_basic_game)

    initialize_asset_if_necessary(abstract_basic_game, img_idx)
    assets = is_reflected ? basic_game.basic_reflections : basic_game.basic_assets
    return assets[img_idx + OFFSET]
end

function draw_image(abstract_basic_game::AbstractBasicAbstractGame, base_rect, rotation::Float32, is_reflected::Bool, base_type::Int, theme::Int, alpha::Float32, tile_ratio::Float32)

    basic_game = get_basic_game(abstract_basic_game)

    img_type = image_for_type(abstract_basic_game, base_type)

    if img_type < 0
        return nothing
    end

    if basic_game.game.options.use_monochrome_assets || img_type >= USE_ASSET_THRESHOLD
        draw_grid_obj(abstract_basic_game, base_rect, img_type, theme)
    else
        img_idx = img_type + theme * MAX_ASSETS

        adjusted_rect = get_adjusted_image_rect(abstract_basic_game, img_type, base_rect)

        asset_ptr = lookup_asset(abstract_basic_game, img_idx, is_reflected)

        if alpha != 1
            # Missing Code
        end

        if rotation == 0
            # For BigFish alpha is always 1 and rotation is always 0
            tile_image(abstract_basic_game, asset_ptr, adjusted_rect, tile_ratio)
        else
            # Missing Code
        end

        if alpha != 1
            # Missing Code
        end
    end
    return nothing
end

function draw_grid_obj(abstract_basic_game::AbstractBasicAbstractGame, rectangle, type::Int, theme::Int)

    if type == Int(SPACE)
        return nothing
    end

    color_for_type(abstract_basic_game, type, theme)
    rectangle = ENLARGEMENT * rectangle
    w = rectangle[3].x - rectangle[2].x
    h = rectangle[1].y - rectangle[2].y
    rect(rectangle[2], w, h, :fill)
    return nothing
end

function draw_foreground(abstract_basic_game::AbstractBasicAbstractGame, rectangle)

    basic_game = get_basic_game(abstract_basic_game)

    height::Float32 = rectangle[1].y - rectangle[2].y
    prepare_for_drawing(abstract_basic_game, height)

    draw_entities(abstract_basic_game, basic_game.entities, -1)

    if basic_game.game.options.center_agent
        # Missing Code
    else
        low_x = 0
        high_x = basic_game.main_width - 1
        low_y = 0
        high_y = basic_game.main_height - 1
    end

    for x in low_x:high_x
        for y in low_y:high_y
            type = get_obj(abstract_basic_game, x, y)

            if type == Int(INVALID_OBJ)
                continue
            end

            theme = theme_for_grid_obj(abstract_basic_game, type)

            r2 = get_screen_rect(abstract_basic_game, Float32(x), Float32(y + 1), Float32(1), Float32(1), RENDER_EPS)

            draw_image(abstract_basic_game, r2, Float32(0), false, type, theme, Float32(1.0), Float32(0.0))
        end
    end

    draw_entities(abstract_basic_game, basic_game.entities, 0)
    draw_entities(abstract_basic_game, basic_game.entities, 1)

    # Missing Options
    #if has_useful_vel_info && basic_game.game.options.paint_vel_info
    #end
    return nothing
end

function draw_background(abstract_basic_game::AbstractBasicAbstractGame, rectangle)

    basic_game = get_basic_game(abstract_basic_game)

    setcolor(0, 0, 0)
    rectangle_large = rectangle * ENLARGEMENT
    left_top_large = rectangle_large[2]
    width_large = rectangle_large[3].x - left_top_large.x
    height_large = rectangle_large[1].y - left_top_large.y
    rect(left_top_large, width_large, height_large, :fill)

    height::Float32 = rectangle[1].y - rectangle[2].y
    prepare_for_drawing(abstract_basic_game, height)

    if !basic_game.game.options.use_backgrounds
        return nothing
    end

    main_rect = get_screen_rect(abstract_basic_game, Float32(0), Float32(basic_game.main_height), Float32(basic_game.main_width), Float32(basic_game.main_height))

    background_image = basic_game.main_bg_images_ptr[basic_game.background_index + OFFSET]

    if basic_game.bg_tile_ratio < 0
        tile_image(abstract_basic_game, background_image, main_rect, basic_game.bg_tile_ratio)
    else
        bgw::Float32 = background_image.width
        bgh::Float32 = background_image.height
        bg_ar = bgw / bgh

        world_ar::Float32 = basic_game.main_width / basic_game.main_height

        extra_w = bg_ar - world_ar
        offset_x = basic_game.bg_pct_x * extra_w

        bg_rect = adjust_rect(main_rect, rect(Point(-offset_x, 0), bg_ar / world_ar, 1, vertices = true))
        show(background_image, bg_rect)
    end
    return nothing
end

function game_draw(abstract_basic_game::AbstractBasicAbstractGame, rectangle)

    draw_background(abstract_basic_game, rectangle)
    draw_foreground(abstract_basic_game, rectangle)
    return nothing
end

function match_aspect_ratio(abstract_basic_game::AbstractBasicAbstractGame, ent::Entity, match_width::Bool = true)

    basic_game = get_basic_game(abstract_basic_game)

    img_idx = ent.image_type + ent.image_theme * MAX_ASSETS
    initialize_asset_if_necessary(abstract_basic_game, img_idx)

    if match_width
        ent.ry = ent.rx / basic_game.asset_aspect_ratios[img_idx + OFFSET]
    else
        ent.rx = ent.ry * basic_game.asset_aspect_ratios[img_idx + OFFSET]
    end
    return nothing
end

function choose_random_theme(abstract_basic_game::AbstractBasicAbstractGame, ent::Entity)

    basic_game = get_basic_game(abstract_basic_game)

    initialize_asset_if_necessary(abstract_basic_game, ent.image_type)
    ent.image_theme = randn(basic_game.game.rand_gen, basic_game.asset_num_themes[ent.image_type + OFFSET])
    return nothing
end

function should_draw_entity(abstract_basic_game::AbstractBasicAbstractGame, entity::Entity)

    return true
end

function draw_entity(abstract_basic_game::AbstractBasicAbstractGame, ent::Entity)

    if should_draw_entity(abstract_basic_game, ent)
        r1 = get_object_rect(abstract_basic_game, ent)
        tile_ratio = get_tile_aspect_ratio(abstract_basic_game, ent)
        draw_image(abstract_basic_game, r1, ent.rotation, ent.is_reflected, ent.image_type, ent.image_theme, ent.alpha, tile_ratio)
    end
    return nothing
end

function draw_entities(abstract_basic_game::AbstractBasicAbstractGame, to_draw::Vector{Entity}, render_z::Int)

    for m in to_draw
        if m.render_z == render_z
            draw_entity(abstract_basic_game, m)
        end
    end
    return nothing
end

function is_out_of_bounds(abstract_basic_game::AbstractBasicAbstractGame, e1::Entity)

    basic_game = get_basic_game(abstract_basic_game)

    x = e1.x
    y = e1.y
    rx = e1.rx
    ry = e1.ry

    if x + rx < 0
        return true
    end
    if y + ry < 0
        return true
    end
    if x - rx > basic_game.main_width
        return true
    end
    if y - ry > basic_game.main_height
        return true
    end

    return false
end

function step_entities(abstract_basic_game::AbstractBasicAbstractGame, given::Vector{Entity})

    entities_count = length(given)

    for i in entities_count:-1:1
        ent = given[i]

        if ent.smart_step
            basic_step_object(abstract_basic_game, ent)
        end

        step(ent)
    end
    return nothing
end

function has_agent_collision(abstract_basic_game::AbstractBasicAbstractGame, e1::Entity)

    basic_game = get_basic_game(abstract_basic_game)

    if e1.type == Int(PLAYER)
        return false
    end

    return has_collision(abstract_basic_game, e1, basic_game.agent, e1.collision_margin)
end

function has_collision(abstract_basic_game::AbstractBasicAbstractGame, e1::Entity, e2::Entity, margin::Float32)

    threshold_x = (e1.rx + e2.rx) + margin
    threshold_y = (e1.ry + e2.ry) + margin

    return (abs(e1.x - e2.x) < threshold_x) && (abs(e1.y - e2.y) < threshold_y)
end


text:
module TestMacrosOuterJoin

import Test: @testset, @test
import Volcanito: @outer_join, materialize
import DataFrames: DataFrame

@testset "@outer_join" begin
    df1 = DataFrame(
        a = [1, 2],
        b = [10, 20],
    )

    df2 = DataFrame(
        c = [1, 1, 2, 2],
        d = [4, 3, 2, 1],
    )

    plan1 = @outer_join(a = df1, b = df2, a.a == b.c)
    res1 = materialize(plan1)
    @test res1 == DataFrame(
        a = [1, 1, 2, 2],
        b = [10, 10, 20, 20],
        d = [4, 3, 2, 1],
    )

    # plan2 = @outer_join(a = df2, b = df1, a.c == b.a)
    # res2 = materialize(plan2)
    # @test res1 == DataFrame(
    #     c = [10, 10, 20, 20],
    #     d = [4, 3, 2, 1],
    #     b = [1, 1, 2, 2],
    # )
end

end


text:
# Copyright (C) 2021 Ilya Baran.  This program is distributed under the terms of the MIT license.

using ModelBlocks
using Test
using Plots
using Interpolations

println("Land model force transient Test starting");

variables = Variables([
    Variable("TRPN", 0.0752, (0, 100), "kg", ""),
    Variable("XB", 0.00046, (0, 100), "kg", ""),
]);

parameters = Variables([
    Variable("Tref", 156.067, (0, 1000), "kPa", "Maximum force that can be generated"),
    Variable("beta1", -1.5, (-100, 100), "", ""),
    Variable("lambda_const", 1.0, (0, 100), "", ""),
    Variable("n_xb", 5.0, (0, 100), "", ""),
    Variable("n_TRPN", 2.0, (0, 100), "", ""),
    Variable("TRPN_50", 0.35, (0, 100), "", ""),
    Variable("k_on", 0.1, (0, 100), "", ""),
    Variable("k_off", 0.0515, (0, 100), "", ""),
    Variable("k_xb", .0172, (0, 100), "", ""),
    Variable("CaT50", 1.0, (0, 100), "", ""),
]);

Ca50 = 2.1708;
parameters.CaT50 = Ca50 *(1 + parameters.beta1 * (parameters.lambda_const - 1));

Cai_transient = [0.14615806, 0.16809044, 0.23975627, 0.24182846, 0.26624959, 0.32315128, 0.40846157, 0.50964583, 0.6141502, 0.71285975, 0.80032514, 0.87399915, 0.93330914, 0.9789494, 1.01229084, 1.03501069, 1.04879625, 1.05545079, 1.0565415, 1.05305274, 1.0466294, 1.03790691, 1.02759521, 1.01665548, 1.00477103, 0.99288059, 0.9808191, 0.96862228, 0.95646617, 0.94456834, 0.93265957, 0.92062926, 0.90892036, 0.89721261, 0.88539131, 0.87372928, 0.86214212, 0.85059505, 0.83911839, 0.82774447, 0.81647891, 0.8053252, 0.79424877, 0.78327627, 0.7724101, 0.76160429, 0.7508795, 0.74023296, 0.72966264, 0.71916982, 0.70875044, 0.69840241, 0.68812979, 0.67794566, 0.66784322, 0.65782355, 0.64789118, 0.63805268, 0.62830954, 0.61866595, 0.60912213, 0.59967838, 0.59033832, 0.58110354, 0.57197444, 0.56295247, 0.5540384, 0.54523488, 0.5365426, 0.52796248, 0.51949613, 0.51114537, 0.50291124, 0.49479501, 0.48679746, 0.47892009, 0.47116413, 0.46352924, 0.45601652, 0.44862701, 0.44136046, 0.43421679, 0.42719693, 0.42030099, 0.41352811, 0.40687818, 0.40035057, 0.39394477, 0.38766014, 0.38149583, 0.37545106, 0.3695245, 0.36371534, 0.35802232, 0.352444, 0.34697929, 0.34162643, 0.33638408, 0.33125046, 0.32622404, 0.32130302, 0.31648575, 0.31177038, 0.30715516, 0.30263818, 0.29821769, 0.29389171, 0.28965844, 0.28551596, 0.28146237, 0.2774958, 0.27361434, 0.26981612, 0.26609934, 0.26246209, 0.2589025, 0.25541869, 0.25200905, 0.24867187, 0.2454052, 0.24220723, 0.23907637, 0.23601143, 0.23301034, 0.23007141, 0.22719294, 0.22437432, 0.22161345, 0.21890874, 0.21625863, 0.21366243, 0.21111866, 0.20862562, 0.20618187, 0.20378597, 0.20143794, 0.19913583, 0.19687828, 0.19466402, 0.19249196, 0.19036213, 0.18827266, 0.18622241, 0.18421026, 0.18223555, 0.18029796, 0.17839595, 0.17652854, 0.17469475, 0.17289523, 0.17112907, 0.16939451, 0.16769064, 0.16601657, 0.16437141, 0.16275424, 0.16116418, 0.15960033, 0.1580647, 0.15655702, 0.15507466, 0.15361691, 0.15218304, 0.15077233, 0.14938403, 0.14801744, 0.14667182];
Cai_interp = interpolate(Cai_transient, BSpline(Constant()));
Cai = extrapolate(Cai_interp, Flat());

reactions = [
    GeneralRateReaction("TRPN IN", [], ["TRPN"], (t, v, p) -> p.k_on * (1 - v.TRPN) * (Cai(t + 0.5) / p.CaT50) ^ p.n_TRPN),
    SimpleReaction("TRPN OUT", ["TRPN"], [], "k_off"),
    GeneralRateReaction("XB", [], ["XB"], (t, v, p) -> begin
        permtot = max(0, v.TRPN / p.TRPN_50) ^ (p.n_xb / 2);
        p.k_xb * (permtot * (1 - v.XB) - v.XB / permtot);
    end),
];

block = Block(variables, parameters, reactions);
setTimeRange!(block, 0:5:334);

outputs = Variables([
    Variable("f_act_vel", 0.0, (0, 100), "?", ""),
    Variable("f_relax_vel", 0.0, (0, 100), "?", ""),
    Variable("time_to_70perForce", 0.0, (0, 100), "?", ""),
    Variable("f_act_vel_norm", 0.0, (0, 100), "?", ""),
    Variable("f_relax_vel_norm", 0.0, (0, 100), "?", ""),
]);

setOutputDefinition!(block, outputs, (variables, parameters, timeRange, solution, outputs) -> begin
    force = block.parameters.Tref * solution.XB;
    idx_maxF = min(length(force) - 1, max(2, argmax(force)));
    maxF = force[idx_maxF];
    t_at_maxF = timeRange[idx_maxF];

    dy = diff(force[1:idx_maxF]) / step(timeRange);
    outputs.f_act_vel = maximum(dy);
    outputs.f_act_vel_norm = maximum(dy) / maxF;

    dy = diff(force[idx_maxF:end]) / step(timeRange);
    outputs.f_relax_vel = maximum(-dy);
    outputs.f_relax_vel_norm = maximum(-dy) / maxF;

    outputs.time_to_70perForce = 3; #TODO: interpolate

    return outputs;
end);

@time solution = runBlock(block);
@time outputs = computeOutputs(block)
@time sensitivity = localSensitivity(block);

setTimeRange!(block, 0:334);

@time fit = fitParameters([block], [
    ("lambda_const", 0.5, 2.0),
    ("n_xb",         2.5, 10.0),
], Dict(
    "f_act_vel" => (outputs.f_act_vel, 1),
    "f_relax_vel" => (outputs.f_relax_vel + 0.01, 1),
    "time_to_70perForce" => (outputs.time_to_70perForce, 1),
    "f_act_vel_norm" => (outputs.f_act_vel_norm, 1),
    "f_relax_vel_norm" => (outputs.f_relax_vel_norm, 1)
));

max_stars_repo_name: ilya-baran-personal/modelBlocks

max_issues_repo_name: ilya-baran-personal/modelBlocks

max_forks_repo_name: ilya-baran-personal/modelBlocks