text:
# Julia wrapper for header: /usr/local/include/libavfilter/avfilter.h
# Automatically generated using Clang.jl wrap_c, version 0.0.0

export
    avfilter_version,
    avfilter_configuration,
    avfilter_license,
    avfilter_pad_count,
    avfilter_pad_get_name,
    avfilter_pad_get_type,
    avfilter_link,
    avfilter_link_free,
    avfilter_link_get_channels,
    avfilter_link_set_closed,
    avfilter_config_links,
    avfilter_process_command,
    avfilter_register_all,
    avfilter_uninit,
    avfilter_register,
    avfilter_get_by_name,
    avfilter_next,
    av_filter_next,
    avfilter_open,
    avfilter_init_filter,
    avfilter_init_str,
    avfilter_init_dict,
    avfilter_free,
    avfilter_insert_filter,
    avfilter_get_class,
    avfilter_graph_alloc,
    avfilter_graph_alloc_filter,
    avfilter_graph_get_filter,
    avfilter_graph_add_filter,
    avfilter_graph_create_filter,
    avfilter_graph_set_auto_convert,
    avfilter_graph_config,
    avfilter_graph_free,
    avfilter_inout_alloc,
    avfilter_inout_free,
    avfilter_graph_parse,
    avfilter_graph_parse_ptr,
    avfilter_graph_parse2,
    avfilter_graph_send_command,
    avfilter_graph_queue_command,
    avfilter_graph_dump,
    avfilter_graph_request_oldest

function avfilter_version()
    ccall((:avfilter_version,libavfilter),UInt32,())
end

function avfilter_configuration()
    ccall((:avfilter_configuration,libavfilter),Cstring,())
end

function avfilter_license()
    ccall((:avfilter_license,libavfilter),Cstring,())
end

function avfilter_pad_count(pads)
    ccall((:avfilter_pad_count,libavfilter),Cint,(Ptr{AVFilterPad},),pads)
end

function avfilter_pad_get_name(pads,pad_idx::Integer)
    ccall((:avfilter_pad_get_name,libavfilter),Cstring,(Ptr{AVFilterPad},Cint),pads,pad_idx)
end

function avfilter_pad_get_type(pads,pad_idx::Integer)
    ccall((:avfilter_pad_get_type,libavfilter),Cint,(Ptr{AVFilterPad},Cint),pads,pad_idx)
end

function avfilter_link(src,srcpad::Integer,dst,dstpad::Integer)
    ccall((:avfilter_link,libavfilter),Cint,(Ptr{AVFilterContext},UInt32,Ptr{AVFilterContext},UInt32),src,srcpad,dst,dstpad)
end

function avfilter_link_free(link)
    ccall((:avfilter_link_free,libavfilter),Void,(Ptr{Ptr{AVFilterLink}},),link)
end

function avfilter_link_get_channels(link)
    ccall((:avfilter_link_get_channels,libavfilter),Cint,(Ptr{AVFilterLink},),link)
end

function avfilter_link_set_closed(link,closed::Integer)
    ccall((:avfilter_link_set_closed,libavfilter),Void,(Ptr{AVFilterLink},Cint),link,closed)
end

function avfilter_config_links(filter)
    ccall((:avfilter_config_links,libavfilter),Cint,(Ptr{AVFilterContext},),filter)
end

function avfilter_process_command(filter,cmd,arg,res,res_len::Integer,flags::Integer)
    ccall((:avfilter_process_command,libavfilter),Cint,(Ptr{AVFilterContext},Cstring,Cstring,Cstring,Cint,Cint),filter,cmd,arg,res,res_len,flags)
end

function avfilter_register_all()
    ccall((:avfilter_register_all,libavfilter),Void,())
end

function avfilter_uninit()
    ccall((:avfilter_uninit,libavfilter),Void,())
end

function avfilter_register(filter)
    ccall((:avfilter_register,libavfilter),Cint,(Ptr{AVFilter},),filter)
end

function avfilter_get_by_name(name)
    ccall((:avfilter_get_by_name,libavfilter),Ptr{AVFilter},(Cstring,),name)
end

function avfilter_next(prev)
    ccall((:avfilter_next,libavfilter),Ptr{AVFilter},(Ptr{AVFilter},),prev)
end

function av_filter_next(filter)
    ccall((:av_filter_next,libavfilter),Ptr{Ptr{AVFilter}},(Ptr{Ptr{AVFilter}},),filter)
end

function avfilter_open(filter_ctx,filter,inst_name)
    ccall((:avfilter_open,libavfilter),Cint,(Ptr{Ptr{AVFilterContext}},Ptr{AVFilter},Cstring),filter_ctx,filter,inst_name)
end

function avfilter_init_filter(filter,args,opaque)
    ccall((:avfilter_init_filter,libavfilter),Cint,(Ptr{AVFilterContext},Cstring,Ptr{Void}),filter,args,opaque)
end

function avfilter_init_str(ctx,args)
    ccall((:avfilter_init_str,libavfilter),Cint,(Ptr{AVFilterContext},Cstring),ctx,args)
end

function avfilter_init_dict(ctx,options)
    ccall((:avfilter_init_dict,libavfilter),Cint,(Ptr{AVFilterContext},Ptr{Ptr{AVDictionary}}),ctx,options)
end

function avfilter_free(filter)
    ccall((:avfilter_free,libavfilter),Void,(Ptr{AVFilterContext},),filter)
end

function avfilter_insert_filter(link,filt,filt_srcpad_idx::Integer,filt_dstpad_idx::Integer)
    ccall((:avfilter_insert_filter,libavfilter),Cint,(Ptr{AVFilterLink},Ptr{AVFilterContext},UInt32,UInt32),link,filt,filt_srcpad_idx,filt_dstpad_idx)
end

function avfilter_get_class()
    ccall((:avfilter_get_class,libavfilter),Ptr{AVClass},())
end

function avfilter_graph_alloc()
    ccall((:avfilter_graph_alloc,libavfilter),Ptr{AVFilterGraph},())
end

function avfilter_graph_alloc_filter(graph,filter,name)
    ccall((:avfilter_graph_alloc_filter,libavfilter),Ptr{AVFilterContext},(Ptr{AVFilterGraph},Ptr{AVFilter},Cstring),graph,filter,name)
end

function avfilter_graph_get_filter(graph,name)
    ccall((:avfilter_graph_get_filter,libavfilter),Ptr{AVFilterContext},(Ptr{AVFilterGraph},Cstring),graph,name)
end

function avfilter_graph_add_filter(graphctx,filter)
    ccall((:avfilter_graph_add_filter,libavfilter),Cint,(Ptr{AVFilterGraph},Ptr{AVFilterContext}),graphctx,filter)
end

function avfilter_graph_create_filter(filt_ctx,filt,name,args,opaque,graph_ctx)
    ccall((:avfilter_graph_create_filter,libavfilter),Cint,(Ptr{Ptr{AVFilterContext}},Ptr{AVFilter},Cstring,Cstring,Ptr{Void},Ptr{AVFilterGraph}),filt_ctx,filt,name,args,opaque,graph_ctx)
end

function avfilter_graph_set_auto_convert(graph,flags::Integer)
    ccall((:avfilter_graph_set_auto_convert,libavfilter),Void,(Ptr{AVFilterGraph},UInt32),graph,flags)
end

function avfilter_graph_config(graphctx,log_ctx)
    ccall((:avfilter_graph_config,libavfilter),Cint,(Ptr{AVFilterGraph},Ptr{Void}),graphctx,log_ctx)
end

function avfilter_graph_free(graph)
    ccall((:avfilter_graph_free,libavfilter),Void,(Ptr{Ptr{AVFilterGraph}},),graph)
end

function avfilter_inout_alloc()
    ccall((:avfilter_inout_alloc,libavfilter),Ptr{AVFilterInOut},())
end

function avfilter_inout_free(inout)
    ccall((:avfilter_inout_free,libavfilter),Void,(Ptr{Ptr{AVFilterInOut}},),inout)
end

function avfilter_graph_parse(graph,filters,inputs,outputs,log_ctx)
    ccall((:avfilter_graph_parse,libavfilter),Cint,(Ptr{AVFilterGraph},Cstring,Ptr{AVFilterInOut},Ptr{AVFilterInOut},Ptr{Void}),graph,filters,inputs,outputs,log_ctx)
end

function avfilter_graph_parse_ptr(graph,filters,inputs,outputs,log_ctx)
    ccall((:avfilter_graph_parse_ptr,libavfilter),Cint,(Ptr{AVFilterGraph},Cstring,Ptr{Ptr{AVFilterInOut}},Ptr{Ptr{AVFilterInOut}},Ptr{Void}),graph,filters,inputs,outputs,log_ctx)
end

function avfilter_graph_parse2(graph,filters,inputs,outputs)
    ccall((:avfilter_graph_parse2,libavfilter),Cint,(Ptr{AVFilterGraph},Cstring,Ptr{Ptr{AVFilterInOut}},Ptr{Ptr{AVFilterInOut}}),graph,filters,inputs,outputs)
end

function avfilter_graph_send_command(graph,target,cmd,arg,res,res_len::Integer,flags::Integer)
    ccall((:avfilter_graph_send_command,libavfilter),Cint,(Ptr{AVFilterGraph},Cstring,Cstring,Cstring,Cstring,Cint,Cint),graph,target,cmd,arg,res,res_len,flags)
end

function avfilter_graph_queue_command(graph,target,cmd,arg,flags::Integer,ts::Cdouble)
    ccall((:avfilter_graph_queue_command,libavfilter),Cint,(Ptr{AVFilterGraph},Cstring,Cstring,Cstring,Cint,Cdouble),graph,target,cmd,arg,flags,ts)
end

function avfilter_graph_dump(graph,options)
    ccall((:avfilter_graph_dump,libavfilter),Cstring,(Ptr{AVFilterGraph},Cstring),graph,options)
end

function avfilter_graph_request_oldest(graph)
    ccall((:avfilter_graph_request_oldest,libavfilter),Cint,(Ptr{AVFilterGraph},),graph)
end

max_stars_repo_name: asafasban/AAVideoIO

max_issues_repo_name: asafasban/AAVideoIO

max_forks_repo_name: asafasban/AAVideoIO


text:
"""
    CosineKernel()

Cosine kernel.

# Definition

For inputs ``x, x' \\in \\mathbb{R}^d``, the cosine kernel is defined as
```math
k(x, x') = \\cos(\\pi \\|x-x'\\|_2).
```
"""
struct CosineKernel <: SimpleKernel end

kappa(::CosineKernel, d::Real) = cospi(d)

metric(::CosineKernel) = Euclidean()

Base.show(io::IO, ::CosineKernel) = print(io, "Cosine Kernel")

ext: jl


text:
using BandedMatrices, FillArrays, Test, LinearAlgebra, SparseArrays
import BandedMatrices: _BandedMatrix

# used to test general matrix backends
struct MyMatrix{T} <: AbstractMatrix{T}
    A::Matrix{T}
end

MyMatrix{T}(::UndefInitializer, n::Int, m::Int) where T = MyMatrix{T}(Array{T}(undef, n, m))
MyMatrix(A::AbstractMatrix{T}) where T = MyMatrix{T}(Matrix{T}(A))
Base.convert(::Type{MyMatrix{T}}, A::MyMatrix{T}) where T = A
Base.convert(::Type{MyMatrix{T}}, A::MyMatrix) where T = MyMatrix(convert(AbstractArray{T}, A.A))
Base.convert(::Type{MyMatrix}, A::MyMatrix)= A
Base.convert(::Type{AbstractArray{T}}, A::MyMatrix) where T = MyMatrix(convert(AbstractArray{T}, A.A))
Base.convert(::Type{AbstractMatrix{T}}, A::MyMatrix) where T = MyMatrix(convert(AbstractArray{T}, A.A))
Base.convert(::Type{MyMatrix{T}}, A::AbstractArray{T}) where T = MyMatrix{T}(A)
Base.convert(::Type{MyMatrix{T}}, A::AbstractArray) where T = MyMatrix{T}(convert(AbstractArray{T}, A))
Base.convert(::Type{MyMatrix}, A::AbstractArray{T}) where T = MyMatrix{T}(A)
Base.getindex(A::MyMatrix, kj...) = A.A[kj...]
Base.getindex(A::MyMatrix, ::Colon, j::AbstractVector) = MyMatrix(A.A[:,j])
Base.setindex!(A::MyMatrix, v, kj...) = setindex!(A.A, v, kj...)
Base.size(A::MyMatrix) = size(A.A)
Base.similar(::Type{MyMatrix{T}}, m::Int, n::Int) where T = MyMatrix{T}(undef, m, n)
Base.similar(::MyMatrix{T}, m::Int, n::Int) where T = MyMatrix{T}(undef, m, n)
Base.similar(::MyMatrix, ::Type{T}, m::Int, n::Int) where T = MyMatrix{T}(undef, m, n)

@testset "BandedMatrix" begin
    @testset "Undef BandedMatrix" begin
        @test typeof(BandedMatrix{Float64}(undef,(10,10),(1,1))) ==
            typeof(BandedMatrix{Float64,Matrix{Float64}}(undef,(10,10),(1,1))) ==
            typeof(BandedMatrix{Float64,Matrix{Float64},Base.OneTo{Int}}(undef,(10,10),(1,1)))
        @test typeof(BandedMatrix{Int}(undef,(10,10),(1,1))) ==
            typeof(BandedMatrix{Int,Matrix{Int}}(undef,(10,10),(1,1))) ==
            typeof(BandedMatrix{Int,Matrix{Int},Base.OneTo{Int}}(undef,(10,10),(1,1)))
@test typeof(BandedMatrix{Vector{Int}}(undef,(10,10),(1,1))) ==
            typeof(BandedMatrix{Vector{Int},Matrix{Vector{Int}}}(undef,(10,10),(1,1))) ==
            typeof(BandedMatrix{Vector{Int},Matrix{Vector{Int}},Base.OneTo{Int}}(undef,(10,10),(1,1)))
    end

    @testset "Creating BandedMatrix" begin
        @test BandedMatrix(Zeros(5,5), (1,1)) == _BandedMatrix(zeros(3,5), 5, 1, 1)
        @test BandedMatrix(Zeros{Int32}(5,5), (1,1)) isa BandedMatrix{Int32, Matrix{Int32}}
        @test BandedMatrix(Zeros{Int}(5,5), (1,1)) == _BandedMatrix(zeros(Int,3,5), 5, 1, 1)
        @test BandedMatrix{Int}(Zeros(5,5), (1,1)) == _BandedMatrix(zeros(Int,3,5), 5, 1, 1)
        @test Matrix(BandedMatrix(Ones(5,5), (1,1))) == Matrix(BandedMatrix(Fill(1.0,(5,5)), (1,1))) ==
                                                        Matrix(SymTridiagonal(ones(5), ones(4)))

        @test all(BandedMatrix(0 => 1:5, 2=> 2:3, -3=> 1:7) .=== diagm(0 => 1:5, 2=> 2:3, -3=> 1:7))
        @test all(BandedMatrix{Float64}(0 => 1:5, 2=> 2:3, -3=> 1:7) .=== Matrix{Float64}(diagm(0 => 1:5, 2=> 2:3, -3=> 1:7)))
        @test all(BandedMatrix((0 => 1:5, 2=> 2:3, -3=> 1:7),(10,10)) .=== Matrix{Int}(diagm(0 => 1:5, 2=> 2:3, -3=> 1:7)))
        @test all(BandedMatrix{Float64}((0 => 1:5, 2=> 2:3, -3=> 1:7),(10,10)) .=== Matrix{Float64}(diagm(0 => 1:5, 2=> 2:3, -3=> 1:7)))
        @test all(BandedMatrix((0 => 1:5, 2=> 2:3, -3=> 1:7),(10,10),(4,3)) .=== Matrix{Int}(diagm(0 => 1:5, 2=> 2:3, -3=> 1:7)))
        @test all(BandedMatrix{Float64}((0 => 1:5, 2=> 2:3, -3=> 1:7),(10,10),(4,3)) .=== Matrix{Float64}(diagm(0 => 1:5, 2=> 2:3, -3=> 1:7)))

        matrix = MyMatrix(ones(Int64,5,5))
        @test !(matrix isa Matrix)
        @test BandedMatrix(matrix) isa BandedMatrix{Int64, Matrix{Int64}}
        @test BandedMatrix(matrix).data isa Matrix{Int64}
        @test BandedMatrix{Int64, typeof(matrix)}(matrix, (1, 1)) isa BandedMatrix{Int64, typeof(matrix)}
        @test BandedMatrix{Int64, typeof(matrix)}(matrix, (1, 1)).data isa typeof(matrix)
        @test_throws UndefRefError BandedMatrix{Vector{Float64}}(undef, (5,5), (1,1))[1,1]
    end

    @testset "BandedMatrix arithmetic" begin
        let A = brand(10,12,2,3),B = brand(10,12,3,4)
            @test Matrix(sparse(A)) ≈ Matrix(A)

            @test Matrix(A') ≈ Matrix(A)'
            @test Matrix(transpose(A)) ≈ transpose(Matrix(A))
            @test Matrix((A+im*A)') ≈ (Matrix(A)+im*Matrix(A))'
            @test Matrix(transpose(A+im*A)) ≈ transpose(Matrix(A)+im*Matrix(A))

            @test Matrix(A+B) ≈ (Matrix(A)+Matrix(B))
            @test Matrix(A-B) ≈ (Matrix(A)-Matrix(B))

            @test Matrix(A.*B) ≈ (Matrix(A).*Matrix(B))
        end

        @testset "UniformScaling" begin
            A = brand(10,10,1,2)
            @test A+I isa BandedMatrix
            @test bandwidths(A+I) == (1,2)
            @test Matrix(A+I) ≈ Matrix(A)+I
            @test Matrix(I-A) ≈ I-Matrix(A)

            # number
            @test Matrix(A/2) ≈ Matrix(A)/2
        end
    end

    @testset "BandedMatrix * Vector" begin
        let A=brand(10,12,2,3), v=rand(12), w=rand(10)
            @test A*v ≈ Matrix(A)*v
            @test A'*w ≈ Matrix(A)'*w
        end

        let A=brand(Float64,5,3,2,2), v=rand(ComplexF64,3), w=rand(ComplexF64,5)
            @test A*v ≈ Matrix(A)*v
            @test A'*w ≈ Matrix(A)'*w
        end

        let A=brand(ComplexF64,5,3,2,2), v=rand(ComplexF64,3), w=rand(ComplexF64,5)
            @test A*v ≈ Matrix(A)*v
            @test A'*w ≈ Matrix(A)'*w
        end

        let A=brand(ComplexF64,5,3,2,2), v=rand(Float64,3), w=rand(Float64,5)
            @test A*v ≈ Matrix(A)*v
            @test A'*w ≈ Matrix(A)'*w
        end
    end

    @testset "Banded * Dense" begin
        @testset "big banded * dense" begin
            A=brand(1000,1000,200,300)
            B=rand(1000,1000)
            @test A*B ≈ Matrix(A)*B
            @test B*A ≈ B*Matrix(A)
        end
        # gbmm! not yet implemented
        # @test A'*B ≈ Matrix(A)'*B
        # @test A*B' ≈ Matrix(A)*B'
        # @test A'*B' ≈ Matrix(A)'*B'

        let
            A=brand(1200,1000,200,300); B=rand(1000,1000); C=rand(1200,1200)
            @test A*B ≈ Matrix(A)*B
            @test C*A ≈ C*Matrix(A)
        end
        # gbmm! not yet implemented
        # @test A'*C ≈ Matrix(A)'*C
        # @test A*B' ≈ Matrix(A)*B'
        # @test A'*C' ≈ Matrix(A)'*C'
    end

    @testset "Banded * Banded" begin
        for n in (1,5), ν in (1,5), m in (1,5), Al in (0,1,3), Au in (0,1,3),
                Bl in (0,1,3), Bu in (0,1,3)
            A = brand(n, ν, Al, Au); B = brand(ν, m, Bl, Bu)
            C = brand(ν, n, Al, Bu); D = brand(m, ν, Al, Bu)
            @test Matrix(A*B) ≈ Matrix(A)*Matrix(B)
            @test Matrix(C'*B) ≈ Matrix(C)'*Matrix(B)
            @test Matrix(A*D') ≈ Matrix(A)*Matrix(D)'
            @test Matrix(C'*D') ≈ Matrix(C)'*Matrix(D)'
        end

        let A = brand(ComplexF64, 5, 4, 2, 3), B = brand(ComplexF64, 4, 6, 3, 1),
            C = brand(ComplexF64, 4, 5, 1, 1), D = brand(ComplexF64, 6, 4, 0, 3)
            @test Matrix(A*B) ≈ Matrix(A)*Matrix(B)
            @test Matrix(C'*B) ≈ Matrix(C)'*Matrix(B)
            @test Matrix(A*D') ≈ Matrix(A)*Matrix(D)'
            @test Matrix(C'*D') ≈ Matrix(C)'*Matrix(D)'
        end

        let A = brand(ComplexF64, 5, 4, 2, 3), B = brand(4, 6, 3, 1), C = brand(4, 5, 1, 1),
                D = brand(ComplexF64, 6, 4, 0, 3)
            @test Matrix(A*B) ≈ Matrix(A)*Matrix(B)
            @test Matrix(C'*B) ≈ Matrix(C)'*Matrix(B)
            @test Matrix(A*D') ≈ Matrix(A)*Matrix(D)'
            @test Matrix(C'*D') ≈ Matrix(C)'*Matrix(D)'
        end

        @testset "BigFloat" begin
            let A = brand(5, 5, 1, 2), B = BandedMatrix(Zeros{BigFloat}(5,5),(2,3)), D = rand(5, 5)
                for j = 1:size(B,2), k = colrange(B,j)
                    B[k,j]=randn()
                end

                x = BigFloat[1:size(B,1)...]

                @test Matrix(A)*Matrix(B) ≈ A*B
                @test Matrix(B)*Matrix(A) ≈ B*A
                @test Matrix(B)*x ≈ B*x
                @test Matrix(B*B) ≈ Matrix(B)*Matrix(B)
                @test Matrix(A)*Matrix(D) ≈ A*D
                @test Matrix(D)*Matrix(A) ≈ D*A
            end
        end

        @testset "Negative bands" begin
            for A in (brand(3,4,-1,2),brand(5,4,-1,2),
                        brand(3,4,2,-1),brand(5,4,2,-1))
                b = rand(size(A,2))
                c = rand(size(A,1))
                @test A*b ≈ Matrix(A)*b
                @test A'*c ≈ Matrix(A)'*c
            end

            let C = brand(4, 5, -1, 3), D = rand(4, 4)
                for A in (brand(3,4,1,2),brand(3,4,-1,2),brand(3,4,2,-1)),
                    B in (brand(4,5,1,2),brand(4,5,-1,2),brand(4,5,2,-1))
                    @test A*B ≈ Matrix(A)*Matrix(B)
                    @test B*C' ≈ Matrix(B)*Matrix(C)'
                    @test B'*C ≈ Matrix(B)'*Matrix(C)
                    @test B'*A' ≈ Matrix(B)'*Matrix(A)'
                end

                for A in (brand(5,4,-1,2),brand(5,4,2,-1),brand(3,4,-1,2),brand(3,4,2,-1))
                    @test A*D ≈ Matrix(A)*Matrix(D)
                end

                for B in (brand(4,3,-1,2),brand(4,3,2,-1),brand(4,5,-1,2),brand(4,5,2,-1))
                    @test D*B ≈ Matrix(D)*Matrix(B)
                end
            end
        end

        @testset "zero" begin
            let b = rand(4)
                for A in (brand(3,4,-1,0),brand(3,4,0,-1),brand(3,4,-1,-1)),
                    B in (brand(4,3,1,2),brand(4,3,-1,0),brand(4,3,-1,-1))
                    @test Matrix(A) == zeros(3,4)
                    @test A*B == zeros(3,3)
                    @test A*b == zeros(3)
                end
            end
        end

        @testset "errors in collect" begin
           let B = brand(10,10,0,4)
               @test B*[collect(1.0:10) collect(1.0:10)] ≈ Matrix(B)*[collect(1.0:10) collect(1.0:10)]
           end
        end
    end

    @testset "BandedMatrix interface" begin
        # check that col/rowstop is ≥ 0
        let A = brand(3,4,-2,2)
            @test BandedMatrices.colstop(A, 1) == BandedMatrices.colstop(A, 2) == 0
            @test BandedMatrices.colstop(A, 3) == 1
        end

        let A = brand(3,4,2,-2)
            @test BandedMatrices.rowstop(A, 1) == BandedMatrices.rowstop(A, 2) == 0
            @test BandedMatrices.rowstop(A, 3) == 1
        end

        # Test fill!
        let B = brand(10,10,1,4)
            @test_throws BandError fill!(B, 1.0)
            @test_throws BandError fill!(B, 1)
            fill!(B, 0)
            @test Matrix(B) == zeros(10,10)
        end
    end

    @testset "Conversions" begin
        @testset "banded -> some matrix" begin
            banded = brand(Int32, 10,12,2,3)

            matrix = Matrix(banded)
            @test matrix isa Matrix{Int32}
            for i in 1:length(matrix)
                @test matrix[i] == banded[i]
            end

            matrix = convert(Matrix, banded)
            @test matrix isa Matrix{Int32}
            for i in 1:length(matrix)
                @test matrix[i] == banded[i]
            end

            matrix = convert(Matrix{Int64}, banded)
            @test matrix isa Matrix{Int64}
            for i in 1:length(matrix)
                @test matrix[i] == banded[i]
            end

            matrix = convert(BandedMatrix, banded)
            @test matrix === banded

            matrix = convert(BandedMatrix{Int64}, banded)
            @test matrix isa BandedMatrix{Int64, Matrix{Int64}}
            for i in 1:length(matrix)
                @test matrix[i] == banded[i]
            end
        end

        @testset "some matrix -> banded" begin
            matrix = rand(Int32, 10, 12)

            types = Dict(BandedMatrix => Matrix{Int32},
                         BandedMatrix{Int64} => Matrix{Int64},
                         BandedMatrix{Int64, MyMatrix{Int64}} => MyMatrix{Int64})
            @testset "Matrix to $Final via $Step" for (Step, Final) in types
                banded = @inferred convert(Step, matrix)
                @test banded isa BandedMatrix{eltype(Final), Final}
                @test banded == matrix
                @test @inferred(convert(BandedMatrix, banded)) === banded
            end

            # Note: @inferred convert(MyMatrix, matrix) throws
            banded = convert(BandedMatrix{Int64, MyMatrix}, matrix)
            @test banded isa BandedMatrix{Int64, MyMatrix{Int64}}
            @test banded == matrix

            banded = convert(BandedMatrix{<:, MyMatrix}, matrix)
            @test banded isa BandedMatrix{Int32, MyMatrix{Int32}}
            @test banded == matrix

            jlarray = convert(MyMatrix, rand(Int32, 10, 12))
            types = Dict(BandedMatrix => MyMatrix{Int32},
                         BandedMatrix{Int64} => MyMatrix{Int64},
                         BandedMatrix{Int64, Matrix} => Matrix{Int64},
                         BandedMatrix{<:, Matrix} => Matrix{Int32},
                         BandedMatrix{<:, Matrix{Int32}} => Matrix{Int32},
                         BandedMatrix{<:, Matrix{Int64}} => Matrix{Int64},
                         BandedMatrix{Int64, Matrix{Int64}} => Matrix{Int64})
            @testset "MyMatrix to $Final via $Step" for (Step, Final) in types
                banded = @inferred convert(Step, jlarray)
                @test banded isa BandedMatrix{eltype(Final), Final}
                @test banded == jlarray
            end

            banded[5, 5] = typemax(Int32)
            @test_throws InexactError convert(BandedMatrix{Int16}, matrix)
            @test_throws InexactError convert(BandedMatrix{Int16, MyMatrix}, matrix)
            @test_throws InexactError convert(BandedMatrix{Int16, MyMatrix{Int16}}, matrix)

            banded = @inferred BandedMatrix{eltype(jlarray), typeof(jlarray)}(jlarray, (1, 1))
            types = [BandedMatrix, BandedMatrix{Int32},
                     BandedMatrix{<:, MyMatrix},
                     BandedMatrix{<:, MyMatrix{Int32}}]
            @testset "no-op banded to banded via $Step" for Step in types
                @test @inferred(convert(Step, banded)) === banded
            end

            types = Dict(BandedMatrix{Int64} => MyMatrix{Int64},
                         BandedMatrix{Int64, Matrix} => Matrix{Int64},
                         BandedMatrix{<:, MyMatrix{Int64}} => MyMatrix{Int64},
                         BandedMatrix{<:, Matrix} => Matrix{Int32},
                         BandedMatrix{<:, Matrix{Int32}} => Matrix{Int32},
                         BandedMatrix{<:, Matrix{Int64}} => Matrix{Int64},
                         BandedMatrix{Int64, Matrix{Int64}} => Matrix{Int64})
            @testset "banded to banded $Final via $Step" for (Step, Final) in types
                newbanded = @inferred convert(Step, banded)
                @test newbanded isa BandedMatrix{eltype(Final), Final}
                @test newbanded == banded
            end

            @testset "banded to banded" begin
                A = BandedMatrix{Int}(undef, (5,5), (1,1)); A.data[:] .= 1:length(A.data)
                @test convert(BandedMatrix, A) === convert(BandedMatrix{Int}, A) ===
                    convert(BandedMatrix{Int,Matrix{Int}}, A) === convert(BandedMatrix{Int,Matrix{Int},Base.OneTo{Int}}, A) === A
                @test convert(BandedMatrix{Float64}, A) ==
                    convert(BandedMatrix{Float64,Matrix{Float64}}, A) == convert(BandedMatrix{Float64,Matrix{Float64},Base.OneTo{Int}}, A) == A
            end
        end

        @testset "similar" begin
            banded = brand(Int32, 10, 12, 1, 2)
            @test @inferred(similar(banded)) isa BandedMatrix{Int32, Matrix{Int32}}
            @test size(similar(banded)) == size(banded)
            @test similar(banded).l == banded.l
            @test similar(banded).u == banded.u
            @test @inferred(similar(banded,(5,5))) isa BandedMatrix{Int32, Matrix{Int32}}
            @test @inferred(similar(banded,5,5)) isa BandedMatrix{Int32, Matrix{Int32}}
            @test @inferred(similar(banded,5,5,1,1)) isa BandedMatrix{Int32, Matrix{Int32}}

            banded = convert(BandedMatrix{<:, MyMatrix}, brand(Int32, 10, 12, 1, 2))
            @test banded isa BandedMatrix{Int32, MyMatrix{Int32}}
            @test @inferred(similar(banded)) isa BandedMatrix{Int32, MyMatrix{Int32}}
            @test size(similar(banded)) == size(banded)
            @test similar(banded).l == banded.l
            @test similar(banded).u == banded.u

            banded = convert(BandedMatrix{<:, MyMatrix}, brand(Int32, 10, 12, 1, 2))
            @test @inferred(similar(banded, Int64)) isa BandedMatrix{Int64, MyMatrix{Int64}}
            @test size(similar(banded, Int64)) == size(banded)
            @test similar(banded, Int64).l == banded.l
            @test similar(banded, Int64).u == banded.u

            bview = @view banded[1:5, 1:5]
            expected = convert(BandedMatrix, bview)
            @test @inferred(similar(bview)) isa BandedMatrix{Int32, MyMatrix{Int32}}
            @test @inferred(similar(bview, Int64)) isa BandedMatrix{Int64, MyMatrix{Int64}}
            @test size(similar(bview, Int64)) == size(expected)
            @test bandwidth(similar(bview, Int64), 1) == bandwidth(expected, 1)
            @test bandwidth(similar(bview, Int64), 2) == bandwidth(expected, 2)

            bview = @view banded[1:5, 6:8]
            expected = convert(BandedMatrix, bview)
            @test size(similar(bview, Int64)) == size(expected)
            @test bandwidth(similar(bview, Int64), 1) == bandwidth(expected, 1)
            @test bandwidth(similar(bview, Int64), 2) == bandwidth(expected, 2)

            bview = @view banded[6:8, 1:5]
            expected = convert(BandedMatrix, bview)
            @test size(similar(bview, Int64)) == size(expected)
            @test bandwidth(similar(bview, Int64), 1) == bandwidth(expected, 1)
            @test bandwidth(similar(bview, Int64), 2) == bandwidth(expected, 2)
        end

        @testset "banded -> sparse" begin
            B = brand(5,5,1,1)
            @test Matrix(sparse(B)) == Matrix(B)
        end
    end

    @testset "real-imag" begin
        B = brand(ComplexF64,5,5,1,1)
        @test real(B) isa BandedMatrix
        @test imag(B) isa BandedMatrix
        @test bandwidths(real(B)) == bandwidths(imag(B)) == bandwidths(B)
        @test real(B) + im*imag(B) == B
    end

    @testset "induced norm" begin
        A = brand(Float64, 12, 10, 2, 3)
        B = Matrix(A)
	    @test opnorm(A) ≈ opnorm(B)
	    @test cond(A) ≈ cond(B)
    end

    @testset "triu/tril" begin
        n = 20; A = brand(n,n,2,3); B = Matrix(A);
        @test triu(A) == triu(B)
        @test tril(A) == tril(B)
        for j = -5:5
            @test triu(A,j) == triu(B,j)
            @test tril(A,j) == tril(B,j)
        end
    end

    @testset "BandedMatrix{Any}" begin
        A = BandedMatrix{Any}(undef, (10,10), (1,1))
        @test A[1,3] === nothing
    end
end


text:
"""
    tridiagonalize(A, d::Integer=0)

The symmetric real or Hermitian input matrix `A` is transformed to a real
`SymTridiagonal` matrix.
For general matrices only `d` superdiagonals are processed.
Works efficiently for `BandedMatrices`.
"""
function tridiagonalize(A::Union{Symmetric{<:Real},Hermitian}, d::Integer=0)
    m, n = size(A)
    m == n || throw(DimensionMismatch("matrix is not square: dimensions are $((m, n))"))
    dmax = bandwidth(A) + 1
    if d == 0
        d = min(n, dmax)
    end

    0 < d <= dmax || throw(ArgumentError("number of diagonals $d not in 1:$dmax"))

    B = copybands(A, d)
    td = _tridiag_algorithm!(B)
    dv = [real(B[1,i]) for i in 1:n]
    ev = d > 1 ? [-abs(B[2,i]) for i = 2:n] : zeros(real(eltype(A)), n-1)
    SymTridiagonal(dv, ev)
end

# B[1:q,1:n] has the bands of a (2q-1)-banded hermitian nxn-matrix.
# superdiagonal i valid in B[i,i+1:n]. B is completely overwritten.
# The result is in B[1:2,1:nvens2]
function _tridiag_algorithm!(B)
    d, n = size(B)
    0 < d <= n || throw(ArgumentError("number of diagonals $d not in 1:$n"))

    @inbounds for bm = d-1:-1:2
        for k = 1:n-bm
            kp = k
            apiv = B[bm+1,bm+kp]
            iszero(apiv) && continue
            for i = bm+k-1:bm:n-1
                b = B[ i-kp+1,i]
                c, s, r = LinearAlgebra.givensAlgorithm(b, apiv)
                u, v = B[1,i], B[1,i+1]
                upx = (u + v) / 2
                B[1,i] = (u - v) / 2
                B[i-kp+1,i] = r
                for j = kp+1:i
                    u = B[i-j+1,i]
                    v = B[i-j+2,i+1]
                    B[i-j+1,i], B[i-j+2,i+1] = u * c + v * s, -u * s' + v * c
                end
                B[1,i+1] = -(B[1,i])'
                ip = i + bm
                for j = i+1:min(ip, n)
                    u = B[j-i+1,j]
                    v = B[j-i,j]
                    B[j-i+1,j], B[j-i,j] = u * c + v * s', -u * s + v * c
                end
                w = real(B[1,i+1])
                B[1,i] = upx - w
                B[1,i+1] = upx + w
                if ip < n
                    v = B[ip-i+1,ip+1]
                    apiv, B[ip-i+1,ip+1] = v * s', v * c
                end
                kp = i
            end
        end
    end
    B
end

# generalization of method for symmetric BandedMatrices
bandwidth(A::AbstractMatrix) = min(size(A)...) - 1

function copybands(A::AbstractMatrix{T}, d::Integer) where T
    n = min(size(A)...)
    d = min(d, n)
    B = Matrix{T}(undef, d, n)
    for i = 1:d
        B[i,1:i-1] .= zero(T)
        for j = i:n
            B[i,j] = A[j-i+1,j]
        end
    end
    B
end


text:
instr = "invalid value"
inhandle = 999%Clong
@test_throws ErrorException set_reference_stateS("Water", instr);
@test_throws ErrorException HAPropsSI(instr, "T", 298.15, "P", 101325, "R", 0.5)
@test_throws ErrorException HAPropsSI("H", instr, 298.15, "P", 101325, "R", 0.5)
@test_throws ErrorException PropsSI(instr, "rhomolar_critical")
@test_throws ErrorException PropsSI("n-Butane", instr)
@test_throws ErrorException PropsSI(instr, "T", 300, "P", 101325, "n-Butane")
@test_throws ErrorException PropsSI("D", "T", 300, "P", 101325, instr)
@test_throws ErrorException PhaseSI(instr, 300, "Q", 1, "Water")
@test_throws ErrorException PhaseSI("T", 300, "Q", 1, instr)
@test_throws ErrorException set_reference_stateS(instr, "DEF")
@test_throws ErrorException set_reference_stateS("Water", instr)
@test_throws ErrorException get_global_param_string(instr)
@test_throws ErrorException get_parameter_information_string(instr)
@test_throws ErrorException get_parameter_information_string("HMOLAR", instr)
@test_throws ErrorException get_fluid_param_string(instr, "aliases")
@test_throws ErrorException get_fluid_param_string("Water", instr)
@test_throws ErrorException get_param_index(instr)
@test_throws ErrorException get_input_pair_index(instr)
@test_throws ErrorException AbstractState_factory(instr, "R245fa")
@test_throws ErrorException AbstractState_factory("HEOS", instr)
@test_throws ErrorException AbstractState_free(inhandle)
@test_throws ErrorException AbstractState_set_fractions(inhandle, [0.0])
@test_throws ErrorException AbstractState_update(inhandle, "PQ_INPUTS", 101325, 0)
handle = AbstractState_factory("HEOS", "Water&Ethanol")
@test_throws ErrorException AbstractState_update(handle, instr, 101325, 0)
@test_throws ErrorException AbstractState_update(handle, inhandle, 101325, 0)
@test_throws ErrorException AbstractState_output(handle, instr)
@test_throws ErrorException AbstractState_output(inhandle, "T")
@test_throws ErrorException AbstractState_keyed_output(inhandle, 1%Clong)
@test_throws ErrorException AbstractState_keyed_output(handle, inhandle)
@test_throws ErrorException AbstractState_specify_phase(inhandle, "phase_gas")
@test_throws ErrorException AbstractState_specify_phase(handle, instr)
@test_throws ErrorException AbstractState_unspecify_phase(inhandle)
pq_inputs = get_input_pair_index("PQ_INPUTS")
T = [0.0]; p = [0.0]; rhomolar = [0.0]; hmolar = [0.0]; smolar = [0.0]
@test_throws ErrorException AbstractState_update_and_common_out(inhandle, pq_inputs, [101325.0], [0.0], 1, T, p, rhomolar, hmolar, smolar)
@test_throws ErrorException AbstractState_update_and_common_out(handle, instr, [101325.0], [0.0], 1, T, p, rhomolar, hmolar, smolar)
out = [0.0]
@test_throws ErrorException AbstractState_update_and_1_out(inhandle, pq_inputs, [101325.0], [0.0], 1, 1%Clong, out)
@test_throws ErrorException AbstractState_update_and_1_out(handle, instr, [101325.0], [0.0], 1, "T", out)
@test_throws ErrorException AbstractState_update_and_5_out(inhandle, pq_inputs, [101325.0], [0.0], 1, [1%Clong, 1%Clong, 1%Clong, 1%Clong, 1%Clong], out, out, out, out, out)
@test_throws ErrorException AbstractState_update_and_5_out(handle, instr, [101325.0], [0.0], 1, ["T", "T", "T", "T", "T"], out, out, out, out, out)
@test_throws ErrorException AbstractState_set_binary_interaction_double(inhandle, 0, 1, "betaT", 0.987)
@test_throws ErrorException AbstractState_set_binary_interaction_double(handle, 0, 1, instr, 0.987)
AbstractState_free(handle)
handle = AbstractState_factory("SRK", "Ethanol");
@test_throws ErrorException AbstractState_set_fluid_parameter_double(inhandle, 1, "c", 0.0)
@test_throws ErrorException AbstractState_set_fluid_parameter_double(handle, 0, instr, 0.0)
if (branchname == "nightly")
  @test_throws ErrorException AbstractState_set_cubic_alpha_C(inhandle, 0, "TWU", 0.0, 0.0, 0.0)
  @test_throws ErrorException AbstractState_set_cubic_alpha_C(handle, 0, instr, 0.0, 0.0, 0.0)
end
if (haskey(ENV, "includelocalwrapper") && ENV["includelocalwrapper"]=="on")
  @test_throws ErrorException saturation_ancillary(instr, "I", 1, "T", 300.0)
  @test_throws ErrorException saturation_ancillary("R410A", instr, 1, "T", 300.0)
  AbstractState_free(handle)
  handle = AbstractState_factory("HEOS", "Water")
  AbstractState_update(handle, "PQ_INPUTS", 15e5, 0)
  @test_throws ErrorException AbstractState_first_saturation_deriv(inhandle, 1%Clong, 2%Clong)
  @test_throws ErrorException AbstractState_first_saturation_deriv(handle, inhandle, 2%Clong)
  @test_throws ErrorException AbstractState_first_partial_deriv(inhandle, 1%Clong, 2%Clong, 3%Clong)
  @test_throws ErrorException AbstractState_first_partial_deriv(handle, inhandle, 2%Clong, 3%Clong)
  @test_throws ErrorException AbstractState_build_phase_envelope(inhandle, "none")
  len=100;t=zeros(len);p=zeros(len);x=zeros(2*len);y=zeros(2*len);rhomolar_vap=zeros(len);rhomolar_liq=zeros(len);
  @test_throws ErrorException AbstractState_get_phase_envelope_data(inhandle, len, t, p, rhomolar_vap, rhomolar_liq, x, y)
  @test_throws ErrorException AbstractState_build_spinodal(inhandle)
  rhomolar=zeros(len); stable=zeros(Clong, len)
  @test_throws ErrorException AbstractState_all_critical_points(inhandle, 2, t, p, rhomolar, stable)
  tau=zeros(len);delta=zeros(len);m1=zeros(len)
  @test_throws ErrorException AbstractState_get_spinodal_data(inhandle, len, tau, delta, m1)
  AbstractState_free(handle)
end


text:
module PkgBake

using PackageCompiler
using MethodAnalysis

#stdlibs
using Artifacts, Base64, CRC32c, Dates, DelimitedFiles,
      Distributed, FileWatching,
      InteractiveUtils, LazyArtifacts,
      Libdl, LibGit2, LinearAlgebra, Logging,
      Markdown, Mmap, Printf, Profile, Random, REPL, Serialization, SHA,
      SharedArrays, Sockets, SparseArrays, TOML, Test, Unicode, UUIDs,
      ArgTools, Downloads, NetworkOptions, Pkg, Statistics, Tar

const base_stdlibs = [Base, Artifacts, Base64, CRC32c, Dates, DelimitedFiles,
Distributed, FileWatching,
InteractiveUtils, LazyArtifacts,
Libdl, LibGit2, LinearAlgebra, Logging,
Markdown, Mmap, Printf, Profile, Random, REPL, Serialization, SHA,
SharedArrays, Sockets, SparseArrays, TOML, Test, Unicode, UUIDs,
ArgTools, Downloads, NetworkOptions, Pkg, Statistics, Tar]

include("cmd_utils.jl")
include("sanitizer.jl")

function get_all_modules()
    mods = Module[]
    for lib in base_stdlibs
        visit(lib) do obj
            if isa(obj, Module)
                push!(mods, obj)
                return true     # descend into submodules
            end
            false   # but don't descend into anything else (MethodTables, etc.)
        end
    end
    return vcat(mods, base_stdlibs)
end

const bakeable_libs = get_all_modules()

const __BAKEFILE = "bakefile.jl"

global __TRACE_PATH = "" # Avoid getting GC'ed when we force `trace_compile`

function __init__()
    init_dir()
    if !have_trace_compile()
        path, io = mktemp(;cleanup=false)
        close(io) # we don't need it open
        global __TRACE_PATH = path
        force_trace_compile(__TRACE_PATH)
    end
end

function init_dir()
    isempty(DEPOT_PATH) && @error "DEPOT_PATH is empty!"
    dir = joinpath(DEPOT_PATH[1],"pkgbake")
    !isdir(dir) && mkdir(dir)
    return abspath(dir)
end

"""
    bake

Add additional precompiled methods to Base and StdLibs that are self contained.
"""
function bake(;project=dirname(Base.active_project()), yes=false, useproject=false, replace_default=true)
    pkgbakedir = init_dir()

    precompile_lines = readlines(abspath(joinpath(init_dir(), "bakefile.jl")))

    unique!(sort!(precompile_lines))

    pc_unsanitized = joinpath(pkgbakedir, "pkgbake_unsanitized.jl")
    @info "PkgBake: Writing unsanitized precompiles to $pc_unsanitized"
    open(pc_unsanitized, "a") do io
        for line in precompile_lines
            println(io, line)
        end
    end

    original_len = length(precompile_lines)
    sanitized_lines = sanitize_precompile(precompile_lines)
    sanitized_len = length(sanitized_lines)

    pc_sanitized = joinpath(pkgbakedir, "pkgbake_sanitized.jl")
    @info "PkgBake: Writing sanitized precompiles to $pc_sanitized"
    open(pc_sanitized, "a") do io
        for line in sanitized_lines
            println(io, line)
        end
    end

    @info "PkgBake: Found $sanitized_len new precompilable methods for Base out of $original_len generated statements"
    !yes && println("Make new sysimg? [y/N]:")
    if yes || readline() == "y"
        @info "PkgBake: Generating sysimage"
        PackageCompiler.create_sysimage(; precompile_statements_file=pc_sanitized, replace_default=replace_default)
    end

    nothing
end

function bakefile_io(f)
    bake = abspath(joinpath(init_dir(), "bakefile.jl"))
    !isfile(bake) && touch(bake)
    open(f, bake, "a")
end

"""
atexit hook for caching precompile files

add to .julia/config/startup.jl

```
using PkgBake

atexit(PkgBake.atexit_hook)
```
"""
function atexit_hook()

    !have_trace_compile() && return

    trace_path = trace_compile_path()
    isempty(trace_path) && return

    trace_file = open(trace_path, "r")

    bakefile_io() do io
        for l in eachline(trace_file, keep=true)
            write(io, l)
        end
    end
    close(trace_file)
end

end


text:
module TestTrapezoid

using Test
using ToyStencils: example_prob, stencil!

function check(; kwargs...)
    prob = example_prob(; kwargs...)

    us_ker = copy(stencil!(prob; timewindow = Inf, basesize = Inf, smallsize = Inf))
    us_seq = copy(stencil!(prob; timewindow = Inf, basesize = Inf, smallsize = 0))
    us_par = copy(stencil!(prob; timewindow = 0, basesize = 0, smallsize = 0))

    @test us_seq == us_ker
    @test us_par == us_ker
end

function test()
    @testset for nx in [5, 10], nt in [1, 3, 4, 5, 11]
        check(; nx = nx, nt = nt)
    end
end

end  # module


text:
using UnicodePlots: barplot
using Base: show, print

"""
Limit Order Book Object

fields:
    `bid_orders::OneSideBook` - book of bid orders
    `ask_orders::OneSideBook` - book of ask orders
    `acct_map::Dict - Dict{Int64,AVLTree{Int64,Order}}` mapping account ids to orders

Initialize empty book: `OrderBook()`

"""
@kwdef mutable struct OrderBook
    bid_orders::OneSidedBook = OneSidedBook(side=:BID) # bid orders
    ask_orders::OneSidedBook = OneSidedBook(side=:ASK) # ask orders
    acct_map::Dict{Int64,AVLTree{Int64,Order}} = Dict{Int64,AVLTree{Int64,Order}}() # Map from acct_id::Int64 to AVLTree{tick_id::Int64,nothing}
    flags = Dict{Symbol,Bool}(:LOAutoCross => true) # container for additional order book logic flags
end

## Limit Order Submission and Cancellation functions

@inline function _add_order_acct_map!(acct_map,acct::Union{Int64,Nothing},order::Order)
    if !isnothing(acct)
        if !haskey(acct_map,acct)
            acct_map[acct]=AVLTree{Int64,Order}() # If account isn't registered, register
        end
        insert!(acct_map[acct],order.orderid,order) # Add order to account map
    end
end

@inline function _delete_order_acct_map!(acct_map,acct::Union{Int64,Nothing},orderid::Int64)
    if !isnothing(acct)
        !haskey(acct_map,acct) ? nothing : delete!(acct_map[acct],orderid)
    end
end

"""
    `submit_limit_order!(ob::OrderBook, orderid::Int64, price::Float32, size::Int64, side::Symbol, acct_id::Union{Nothing,Int64}=nothing)`

Enter limit order with matching properties to the LOB.
Optionally provide `acct_id`, defaults to `nothing`. If `acct_id` provided, account holdings are tracked in
"""
function submit_limit_order!(ob::OrderBook, orderid::Int64, price::Float32, size::Int64, side::Symbol;
                             acct_id::Union{Nothing,Int64}=nothing)
    side in (:BID, :ASK) || error("invalid trade side provided") # check valid side
    ord = Order(orderid,side,size,price,acct_id) # create order object
    # Add actual order to the correct OneSideBook
    ord.side == :ASK ? insert_order!(ob.ask_orders,ord) : insert_order!(ob.bid_orders,ord)
    # Update account map
    _add_order_acct_map!(ob.acct_map,acct_id,ord)
    return nothing
end

"""
    `cancel_limit_order!(ob::OrderBook, orderid::Int64, price::Float32,
                            side::Symbol, acct_id::Union{Nothing,Int64}=nothing)`

Cancels order with matching tick_id from OrderBook.
Optionally provide acct_id if known.

"""
function cancel_limit_order!(ob::OrderBook, orderid::Int64, price::Float32, side::Symbol; acct_id::Union{Nothing,Int64}=nothing)
    side in (:BID, :ASK) || error("invalid trade side provided") # check valid side
    # Delete order from bid or ask book
    (side == :ASK) ? delete_order!(ob.ask_orders,price,orderid) : delete_order!(ob.bid_orders,price,orderid)
    # Delete order from account maps
    isnothing(acct_id) || _delete_order_acct_map!(ob.acct_map,acct_id,orderid)
end

## Market Order insertion functions

"""
    Fill market order by walking the book using Price/Arrival priority
"""
@inline function _submit_market_order!(sb::OneSidedBook,size::Int64)
    size>0 || error("market order size must be positive")
    order_match_lst = Vector{Order}()
    left_to_trade = size # remaining quantity to trade
    while !isempty(sb.book) && (left_to_trade>0)
        price_queue :: OrderQueue = popfirst!(sb.book)
        if false && price_queue.total_volume[] <= left_to_trade # If entire queue is to be wiped out
            append!(order_match_lst,price_queue.queue) # Add all of the orders to the match list
            left_to_trade -= price_queue.total_volume[] # decrement what's left to trade
            # Update stats
            sb.num_orders -= price_queue.num_orders[]
            sb.total_volume -= price_queue.total_volume[]
        else
            while !isempty(price_queue) && (left_to_trade>0)
                best_ord :: Order = popfirst!(price_queue) # pop out best order
                if left_to_trade >= best_ord.size # Case 1: Limit order gets wiped out
                    # Add best_order to match list & decrement outstanding MO
                    push!(order_match_lst,best_ord)
                    left_to_trade -= best_ord.size
                    # Update book stats - n_orders & volume
                    sb.total_volume -= best_ord.size
                    sb.num_orders -= 1
                else left_to_trade < best_ord.size # Case 2: Market Order gets wiped out
                    # Return the difference: LO.size-MO.size back to the order book
                    return_ord = copy_modify_size(best_ord,best_ord.size - left_to_trade)
                    pushfirst!(price_queue,return_ord)
                    # Add remainder to match list & decrement outstanding MO
                    best_ord = copy_modify_size(best_ord,left_to_trade)
                    push!(order_match_lst,best_ord)
                    left_to_trade -= best_ord.size
                    # Update book stats - only volume, since we placed an order back
                    sb.total_volume -= best_ord.size
                end
            end
            if !isempty(price_queue) # If price queue wasn't killed, put it back into the OneSideBook
                price_key = (sb.side == :ASK) ? price_queue.price : -price_queue.price
                insert!(sb.book,price_key,price_queue)
            end
        end
    end
    # Update Sidebook statistics
    _update_next_best_price!(sb)
    # Return results
    complete_status = left_to_trade > 0 ? :INCOMPLETE : :COMPLETE # Note whether order is complete
    return order_match_lst, complete_status
end

"""
    `submit_market_order!(ob,side,size)`

Submit market to OrderBook on given side (:ASK or :BID) and size::Int64,
Returns the assigned internal tick_id.

"""
function submit_market_order!(ob::OrderBook,side,size)
    side ∈ (:BID, :ASK) || error("invalid trade side provided") # check valid side
    if side == :ASK
        order_match_lst, complete_status = _submit_market_order!(ob.ask_orders,size::Int64)
    else
        order_match_lst, complete_status = _submit_market_order!(ob.bid_orders,size::Int64)
    end
    return order_match_lst, complete_status
end

## Order book statistics functions

@inline function _sidebook_stats(sb::OneSidedBook, max_depth)
    # get book statistics until fixed price depth
    raw_info_list = [ ( pq.price[], pq.total_volume[], pq.num_orders[] ) for (pk,pq) in Base.Iterators.take(sb.book,max_depth) ]
    # Compile and return as dict of vectors
    return Dict(:side => sb.side,
                :price =>  [ x[1] for x in raw_info_list],
                :volume => [ x[2] for x in raw_info_list],
                :orders => [ x[3] for x in raw_info_list] )
end

"""
    `book_depth_info(ob::OrderBook; max_depth=5)`

    Retrieve prices, volumes and order counts at bid and ask until fixed depth
"""
function book_depth_info(ob::OrderBook; max_depth=5)
    return Dict( :BID => _sidebook_stats(ob.bid_orders,max_depth),
                 :ASK => _sidebook_stats(ob.ask_orders,max_depth) )
end

"Return best ask price from order book"
best_bid_ask(ob::OrderBook) = (ob.bid_orders.best_price, ob.ask_orders.best_price)

"""
    `volume_bid_ask(ob::OrderBook)`

Return total bid and ask volume from order book.
"""
volume_bid_ask(ob::OrderBook) = (ob.bid_orders.best_price, ob.ask_orders.best_price)

"""
    n_orders_bid_ask(ob::OrderBook)

Return total number of orders on each side of order book
"""
n_orders_bid_ask(ob::OrderBook) = (ob.bid_orders.best_price, ob.ask_orders.best_price)

"""
    get_acct(ob::OrderBook,acct_id::Int64)

Return all open orders assigned to
"""
get_acct(ob::OrderBook,acct_id::Int64) = ob.acct_map[acct_id]

"""
    `write_csv(io::IO,ob::OrderBook;row_formatter = _order_to_csv, header="TRD,ID,SIDE,SIZE,PX,ACCT")`

Write OrderBook `ob` to an IO stream into csv format where each row corresponds to an order
The formatting for each row is given by the function `string_map(::Order)::String`.
The csv header can be provided as an argument where setting it to `nothing` writes no header.
"""
function write_csv(io::IO,ob::OrderBook;row_formatter = _order_to_csv, header="TRD,ID,SIDE,SIZE,PX,ACCT")
    cnt = 0
    !is_nothing(write_header) && ( cnt += write(io,header,'\n') )
    # write all of the bids
    for (pk,pq) in ob.bid_orders.book
        for ord in pq.queue
            cnt += write(io,_order_to_csv(ord),'\n')
        end
    end
    # write all of the asks
    for (pk,pq) in ob.ask_orders.book
        for ord in pq.queue
            cnt += write(io,_order_to_csv(ord)*'\n')
        end
    end
    return cnt
end

@inline function _print_book_barplot(io::IO,ob::OrderBook;max_depth=5)
    sb_info = LimitOrderBook.book_depth_info(ob,max_depth=max_depth)

    # Get max price str length
    all_prices = [ sb_info[:BID][:price] ; sb_info[:ASK][:price] ]
    max_len = string.(all_prices) .|> length |> maximum

    bid_plt = barplot( lpad.(string.(reverse(sb_info[:BID][:price])),max_len," ") ,reverse(sb_info[:BID][:volume]),color=:red,ylabel=":BID",border=:none,padding=0)
    ask_plt = barplot( lpad.(string.(sb_info[:ASK][:price]),max_len," "),sb_info[:BID][:volume],ylabel=":ASK",border=:none,padding=0)

    println("OrderBook object with flags $(ob.flags)\n Display depth cutoff=$max_depth.")
    println(io,bid_plt)
    println(io,ask_plt)
    return
end

function Base.print(io::IO,ob::OrderBook;max_depth=5)
    return _print_book_barplot(io,ob;max_depth=max_depth)
end

function Base.show(io::IO,ob::OrderBook,::MIME"text/plain";max_depth=5)
    return _print_book_barplot(io,ob;max_depth=max_depth)
end


text:
"""
    AbstractUncertainIndexValueDataset

A dataset of uncertain value (`UncertainValue` instances), with indices (time,
depth, etc..) that are also uncertain values (`UncertainValue` instances).

Concrete types must as a minimum implement the following fields:

- **`indices::UncertainDataset`**: The (uncertain) indices of the dataset.
- **`values::UncertainDataset`**: The (uncertain) values of the dataset.
"""
abstract type AbstractUncertainIndexValueDataset <: AbstractUncertainDataset end

function summarise(uid::AbstractUncertainIndexValueDataset)
    _type = typeof(uid)
    n_values = length(uid.values)
    n_indices = length(uid.indices)
    summary = "$_type containing $n_values uncertain values coupled with $n_indices uncertain indices"
    return summary
end

Base.show(io::IO, uid::AbstractUncertainIndexValueDataset) =
    println(io, summarise(uid))

Base.getindex(uvd::AbstractUncertainIndexValueDataset, i) = uvd.indices[i], uvd.values[i]
Base.length(uvd::AbstractUncertainIndexValueDataset) = length(uvd.values)
Base.size(uvd::AbstractUncertainIndexValueDataset) = length(uvd.values)
Base.firstindex(uvd::AbstractUncertainIndexValueDataset) = 1
Base.lastindex(uvd::AbstractUncertainIndexValueDataset) = length(uvd.values)

import ..UncertainValues: minimum, maximum

function Base.minimum(udata::AbstractUncertainIndexValueDataset)
    [minimum(uval) for uval in udata.indices],
    [minimum(uval) for uval in udata.values]
end

function Base.maximum(udata::AbstractUncertainIndexValueDataset)
    [maximum(uval) for uval in udata.indices],
    [maximum(uval) for uval in udata.values]
end

export AbstractUncertainIndexValueDataset


text:
@testset "Long read datastores" begin
    function get_fastq_seqs(file)
        seqs = map(open(FASTQ.Reader, file) do rdr
            collect(rdr)
        end) do rec
            FASTQ.sequence(LongDNASeq, rec)
        end
        return seqs
    end

    function check_round_trip(::Type{A}, FQ) where {A<:DNAAlphabet}
        seqs = get_fastq_seqs(FQ)
        fq = open(FASTQ.Reader, FQ)
        ds = LongReads{A}(fq, "human-nanopore", "human-nanopore", 0)
        ds2 = open(LongReads{A}, "human-nanopore.loseq")
        ds_seqs = collect(ds)
        ds2_seqs = collect(ds2)
        return ds_seqs == seqs == ds2_seqs
    end

    function check_show(ds, msg)
        buf = IOBuffer()
        show(buf, ds)
        return String(take!(buf)) == msg
    end

    function run_tests(::Type{A}) where {A<:DNAAlphabet}
        @test check_round_trip(A, "human_nanopore_tester_2D.fastq")
        ds = open(LongReads{A}, "human-nanopore.loseq")
        ds2 = open(LongReads{A}, "human-nanopore.loseq")
        @test ReadDatastores.deduce_datastore_type("human-nanopore.loseq") == LongReads{A}
        @test ReadDatastores.index(ds) == ReadDatastores.index(ds2)
        @test ReadDatastores.index(ds)[1] == ReadDatastores.index(ds2)[1]
        @test firstindex(ds) == firstindex(ds2) == 1
        @test lastindex(ds) == lastindex(ds2) == 10

        @test check_show(ds, "Long Read Datastore 'human-nanopore': 10 reads")

        @test Base.IteratorSize(ds) == Base.IteratorSize(ds2) == Base.HasLength()
        @test Base.IteratorEltype(ds) == Base.IteratorEltype(ds2) == Base.HasEltype()
        @test Base.eltype(ds) == Base.eltype(ds2) == LongSequence{A}

        @test collect(ds) == collect(buffer(ds)) == collect(buffer(ds, 1))
        @test ds[5] == buffer(ds)[5] == load_sequence!(ds, 5, LongSequence{A}()) == load_sequence!(buffer(ds), 5, LongSequence{A}())

        bds = buffer(ds)
        @test eltype(bds) == eltype(ds)
        @test firstindex(bds) == firstindex(ds)
        @test eachindex(bds) == eachindex(ds)
        @test Base.IteratorSize(bds) == Base.IteratorSize(ds)
        @test Base.IteratorEltype(bds) == Base.IteratorEltype(ds)
    end

    @testset "Human oxford nanopore 2D consensus reads tester" begin
        run_tests(DNAAlphabet{4})
        run_tests(DNAAlphabet{2})

        @test_throws ReadDatastores.DatastoreVersionError{LongReads{DNAAlphabet{2}}} open(LongReads{DNAAlphabet{2}}, "human-nanopore-old.loseq")
        @test_throws ReadDatastores.DatastoreEncodingError{LongReads{DNAAlphabet{4}}} open(LongReads{DNAAlphabet{4}}, "human-nanopore.loseq")
    end
end


text:
module SumTrees

export SumTree

import Base: show, setindex!, getindex, sum
import Base: start, next, done, length, eltype

immutable SumTree{T, NCells}
    tree::Vector{T}
end

function SumTree{T}(::Type{T}, ncells::Integer)
    SumTree{T, ncells}(zeros(T, 2 * ncells - 1))
end

# default type is Float64
SumTree(ncells::Integer) = SumTree(Float64, ncells)

function setindex!{T, NCells}(tree::SumTree{T, NCells}, value::T, idx::Integer)
    if idx < 1 || idx > NCells
        throw(BoundsError(tree, idx))
    end

    idx += NCells - 1
    tree.tree[idx] = value

    # bubble up sums
    while idx > 1
        tree.tree[idx >>> 1] = tree.tree[idx] + tree.tree[idx $ 1]
        idx >>>= 1
    end

    return nothing
end

function setindex!{T, NCells}(tree::SumTree{T, NCells}, value, idx::Integer)
    setindex!(tree, convert(T, value), idx)
end

function getindex{T, NCells}(tree::SumTree{T, NCells}, idx)
    if idx < 1 || idx > NCells
        throw(BoundsError(tree, idx))
    end

    tree.tree[idx + NCells - 1]
end

sum(tree::SumTree) = tree.tree[1]

length{T, NCells}(tree::SumTree{T, NCells}) = NCells

start{T, NCells}(tree::SumTree{T, NCells}) = NCells
next{T, NCells}(tree::SumTree{T, NCells}, state) = (tree.tree[state]::T, state + 1)
done{T, NCells}(tree::SumTree{T, NCells}, state) = state >= 2 * NCells

eltype{T, NCells}(tree::SumTree{T, NCells}) = T

function show{T, NCells}(io::IO, tree::SumTree{T, NCells})
    println(io, "SumTree{$T, $(NCells)} ::")

    level = 1
    while level <= NCells
        print(io, "    (")
        next_level = level * 2
        for i = level:(next_level - 1)
            print(io, "$(tree.tree[i]),")
        end
        println(io, ")")

        level = next_level
    end
end

end # module


text:
abstract type populations end

abstract type DT_population <: populations end

"""
    mutable struct simple_sites <: populations

Population of simple sites with fixed length.

...
# Arguments
- `N::Float64=1000`: population size.
- `l::Int64=10`: Length of site.
- `n::Int64=4`: Alphabet size.
- `seqs::Array{Array{Int64, 1}, 1}=[]`: Array of sequences for each species.
- `freqs::Array{Int64, 1}=[]` Species sizes.
...

# Examples
```jldoctest
julia> Jevo.simple_sites()
Jevo.simple_sites(1000, 10, 4, Array{Int64,1}[], Int64[])
```
"""
mutable struct simple_sites <: populations
    "Population size"
    N::Int64
    "Length of site"
    l::Int64
    "Alphabet size"
    n::Int64
    "Sequences"
    seqs::Array{Array{Int64, 1}, 1}
    "Subpopulation sizes"
    freqs::Array{Int64, 1}

end

simple_sites(;N=1000, l=10, n=4, seqs=Array{Int64, 1}[], freqs=Int64[]) = simple_sites(N, l, n, seqs, freqs)

"""
    mutable struct driver_trailer <: DT_population

Population of simple binding sites of fixed length with a given driver sequence.
"""
mutable struct driver_trailer <: DT_population
    "Population size"
    N::Int64
    "Maximal length (in case of length mutations)"
    L::Int64
    "Inital length of specific site"
    l::Int64
    "Driver alphabet size"
    m::Int64
    "Trailer alphabet size"
    n::Int64
    "Driver sequence"
    driver::Array{Int64, 1}
    "Trailer sequences"
    seqs::Array{Array{Int64, 1}, 1}
    "Subpopulation sizes"
    freqs::Array{Int64, 1}

    function driver_trailer(N, L, l, m, n, driver, seqs, freqs)
        if l > L
            @warn "l>L. Choosing L=l."
            L = l
        end
        new(N, L, l, m, n, driver, seqs, freqs)
    end
end

driver_trailer(;N=10000, L=10, l=L, m=4, n=4, driver=zeros(Int64, L), seqs=Array{Int64, 1}[], freqs=Int64[]) = driver_trailer(N, L, l, m, n, driver, seqs, freqs)

"""
    mutable struct driver_trailer_l <: DT_population

Creates population of binding sites with a common driver sequence, variable length.
"""
mutable struct driver_trailer_l <: DT_population
    "Population size"
    N::Int64
    "Maximal length (in case of length mutations)"
    L::Int64
    "Inital length of specific site"
    l_0::Int64
    "Driver alphabet size"
    m::Int64
    "Trailer alphabet size"
    n::Int64
    "Driver sequence"
    driver::Array{Int64, 1}
    "Trailer sequences"
    seqs::Array{Array{Int64, 1}, 1}
    "Subpopulation sizes"
    freqs::Array{Int64, 1}
    "length for each subpopulation"
    l::Array{Int64, 1}

    function driver_trailer_l(N, L, l_0, m, n, driver, seqs, freqs, l)
        if l_0 > L
            @warn "l_0>L. Choosing L=l_0."
            L = l_0
        end
        new(N, L, l_0, m, n, driver, seqs, freqs, l)
    end
end

driver_trailer_l(;N=10000, L=10, l_0=L, m=4, n=4, driver=zeros(Int64, L), seqs=Array{Int64, 1}[], freqs=Int64[], l=Int64[]) = driver_trailer_l(N, L, l_0, m, n, driver, seqs, freqs, l)

"""
    mutable struct mono_pop <: populations

Creates population of a single binding site and driver.
"""
mutable struct mono_pop <: DT_population
    "Population size"
    N::Int64
    "Inital length of specific site"
    l::Int64
    "Alphabet size"
    n::Int64
    "Sequences"
    seqs::Array{Int64, 1}
    "Driver alphabet size"
    m::Int64
    "Driver sequence"
    driver::Array{Int64, 1}
end

mono_pop(;N=1000, l=15, n=4, seqs=Int64[], m=4, driver=Int64[]) = mono_pop(N, l, n, seqs, m, driver)


text:
platforms_and_sources = Dict(
    Platform("x86_64", "linux") => [
        ArchiveSource("https://developer.nvidia.com/compute/machine-learning/cudnn/secure/8.2.0.53/10.2_04222021/cudnn-10.2-linux-x64-v8.2.0.53.tgz",
                      "6ecbc98b3795e940ce0831ffb7cd2c0781830fdd6b1911f950bcaf6d569f807c")],
    Platform("x86_64", "windows") => [
        ArchiveSource("https://developer.nvidia.com/compute/machine-learning/cudnn/secure/8.2.0.53/10.2_04222021/cudnn-10.2-windows10-x64-v8.2.0.53.zip",
                      "1dc2182e2c8ff995e81cf812dd81d628fc5e4b9bcd5eb838ac71acc2928409d8")],
)

max_stars_repo_name: waralex/Yggdrasil

max_issues_repo_name: waralex/Yggdrasil

max_forks_repo_name: waralex/Yggdrasil


text:
module TriangulationPortionsTests

using Gridap.ReferenceFEs
using Gridap.Geometry
using Test

domain = (0,1,0,1)
partition = (10,10)
oldmodel = CartesianDiscreteModel(domain,partition)
oldtrian = get_triangulation(oldmodel)

cell_to_oldcell = collect(1:34)
trian = TriangulationPortion(oldtrian,cell_to_oldcell)
test_triangulation(trian)

oldbtrian = BoundaryTriangulation(oldmodel)

bface_to_oldbface = collect(1:32)
btrian = TriangulationPortion(oldbtrian,bface_to_oldbface)
test_triangulation(btrian)

nb = get_normal_vector(btrian)
@test length(nb) == num_cells(btrian)

sface_to_oldsface = collect(10:45)
oldstrian = SkeletonTriangulation(oldmodel)
strian = TriangulationPortion(oldstrian,sface_to_oldsface)
test_triangulation(strian)

ns = get_normal_vector(strian)
@test length(ns) == num_cells(strian)

v = [i for i=1:num_cells(trian)]
cell_to_oldcell = [2,9,7]
trian_portion = TriangulationPortion(oldtrian,cell_to_oldcell)
trian_portion_portion = TriangulationPortion(trian_portion,[3,1])
@test get_cell_id(trian_portion_portion)==[7,2]
@test restrict(v, trian_portion_portion)==[7,2]

#using Gridap.Visualization
#writevtk(oldtrian,"oldtrian")
#writevtk(btrian,"btrian",cellfields=["normal"=>nb],celldata=["oldcell"=>get_cell_id(btrian)])
#writevtk(strian,"strian",cellfields=["normal"=>ns],
#  celldata=["oldcell_left"=>get_cell_id(strian).left,"oldcell_right"=>get_cell_id(strian).right])

end # module


text:
@testset "Tables" begin
    X = (
        x = rand(5),
        y = rand(Int, 5),
        z = categorical(collect("asdfa")),
        w = rand(5)
    )
    s = schema(X)
    @test info(X) == schema(X)
    @test s.scitypes == (Continuous, Count, Multiclass{4}, Continuous)
    @test s.types == (Float64, Int64, CategoricalValue{Char,UInt32}, Float64)
    @test s.nrows == 5

    @test_throws ArgumentError schema([:x, :y])

    t = scitype(X)
    @test t <: Table(Continuous, Finite, Count)
    @test t <: Table(Infinite, Multiclass)
    @test !(t <: Table(Continuous, Union{Missing, Count}))

    @test MLJScientificTypes._nrows(X) == 5
    @test MLJScientificTypes._nrows(()) == 0
    @test MLJScientificTypes._nrows((i for i in 1:7)) == 7

    # PR #61 "scitype checks for `Tables.DictColumn`"
    X1 = Dict(:a=>rand(5), :b=>rand(Int, 5))
    s1 = schema(X1)
    @test info(X1) == schema(X1)
    @test s1.scitypes == (Continuous, Count)
    @test s1.types == (Float64, Int64)
    @test s.nrows == 5

    #issue 47
    X2 = ((x=rand(3), y=rand(3)),)
    s2 = schema(X2)
    @test s2 === nothing
end

@testset "csvfile" begin
    X = (x = rand(4), )
    CSV.write("test.csv", X)
    file = CSV.File("test.csv")
    @test scitype(file) == scitype(X)
    rm("test.csv")
end


text:
######################IO################

#functions for converting Floats to Strings
function num2Str(x::T, decimals::Int=DEFAULT_DECIMALS;
    scaleFactor::Float64 = 1.0,
    scaleFactorOnHurdle::Float64 = 1.0,
    Ints::Bool = false, scaleHurdle::U=nothing, texEquation::Bool = false)::String where {T<:Real, U<:Union{Nothing,Real}}

  local outStr::String

  x*=scaleFactor

  if scaleHurdle ≠ nothing && x≥scaleHurdle
    outStr = "$(Int(round(x*scaleFactorOnHurdle)))"
  elseif decimals == 0 || (Ints && round(x) == x)
    outStr = "$(Int(round(x)))"
  else
    outStr = format("{:.$(decimals)f}",x)
  end

  outStr = texEquation ? "\$$outStr\$" : outStr

  return outStr
end

#converts a vector of anything to a string given an abstract delimitor
function vec2String(v::Vector{T}, delim::W = "")::String where
  {T<:Any, W<:Union{AbstractString,Char}}

  return join(String.(v), delim)
end

#texTable
#=    Creates a well-formed Latex table from string components
  The content matrix consists of an array of matrices, which are overlayed
  via a one line offset. For example, the first matrix might be the values,
  the second matrix the errors. Observational rows can be put in
  descContent, which is a vector of rows. Similarly, colNames is a vector
  of rows. So that column headers can span multiple columns, the widthColNames
  and widthDescContent provides the option to specify dimentions
  IN: caption (title), colNames, rowNames, content, descContent (rows of
      descriptive data), notes, optionally widthColNames and widthDescConent
      (which are the width of each entry in columns)
  OUT: The tex table as a string=#

function texTable(; titleCaption::String = "",
  caption::String = "",
  colNames::Vector{Vector{String}} = Vector{Vector{String}}(),
  contentRowNames::Vector{String} = Vector{String}(),
  content::Vector{Matrix{String}} = Vector{Matrix{String}}(),
  descRowNames::Vector{String} = Vector{String}(),
  descContent::Vector{Vector{String}}=Vector{Vector{String}}(),
  notes::Vector{String} = Vector{String}(),
  arrayStretch::Float64 = 1.5,
  lineSpacer::String = "\\\\",
  summaryMathMode::Bool = true,
  widthColNames::Vector{Vector{Int}} = #contains the number of columns for each entry
    broadcast((i::Int)->ones(Int,length(colNames[i])),1:length(colNames)),
  alignmentColNames::Vector{Vector{String}} = #contains the number of columns for each entry
    broadcast((i::Int)->["r" for i ∈ 1:length(colNames[i])],1:length(colNames)),
  widthDescContent::Vector{Vector{Int}} = #contains the number of columns for each entry
    broadcast((i::Int)->ones(Int,length(descContent[i])),1:length(descContent)),
  colHeaderName::Vector{String} = ["" for i ∈ 1:length(colNames)],
  nakedTable::Bool = false)

  #size parameters for content
  numContentRows::Int = length(contentRowNames)

  numContentCols::Int = sum(widthColNames[1])

  numContentSubRows::Int = length(content)

      #intiate the stream
  b::IOBuffer = IOBuffer()
  (!nakedTable) && write(b, """
      %This table was programatically generated

      \\begin{table} \\caption{$titleCaption} \\label{} \\centering
      $(length(caption)>0 ? "\\textit{$caption\\\\}" : "")
      \\renewcommand{\\arraystretch}{$arrayStretch}""")
  write(b, """
      \\begin{tabular}{l | """)

    for i ∈ 1:(numContentCols) #set the dimensions in tex
      write(b,"r")
    end
    #write(b, "{\\textwidth}{Xccccccc}")
    write(b,"}\n \\toprule")#filler tex

  for r::Int ∈ 1:length(colNames) #for each row of column headings
      #write(b,"\n\\\\[-1.8ex]") #use this if the below doesn't work
    write(b, colHeaderName[r])
    if length(colNames[r]) ≠ numContentCols
      for c::Int ∈ 1:length(colNames[r]) #for each column heading
          write(b,"\t&\t\\multicolumn{$(widthColNames[r][c])}{$(alignmentColNames[r][c])}{$(colNames[r][c])}")
      end
    else
      for c::Int ∈ 1:length(colNames[r]) #for each column heading
        write(b,"\t&\t$(colNames[r][c])")
      end
    end
      write(b,"\n\\\\")
  end
  write(b, " \\midrule\n ")

  #now write out the table content
    if numContentRows > 0
        write(b," \t \t ")
        for r::Int ∈ 1:numContentRows
          write(b, "$(contentRowNames[r])") #the row label
          for s::Int ∈ 1:numContentSubRows #print the sub-rows for each row
            for c::Int ∈ 1:numContentCols #print the columns for each sub-row
              #write(b,"\t&\t\\multicolumn{1}{r}{\$$((content[s])[r, c])\$}")
              write(b,"\t&\t$((content[s])[r, c])")
            end
            write(b,"\n $(lineSpacer) \t\t") #line-break stylistic formatting
          end
        end
        write(b, "\n \\midrule\n")
    end
    sumMathFlag::String = summaryMathMode ? "\$" : ""

    if length(descContent)>0
        for r::Int ∈ 1:length(descContent) #for each description row
          write(b, "$(descRowNames[r])") #the row label
          if length(widthDescContent[r]) ≠ numContentCols
            for c::Int ∈ 1:length(descContent[r]) #for each descriptive row
              if length(descContent[r][c]) ≥ 1
                write(b,"\t&\t\\multicolumn{$(widthDescContent[r][c])}{r}{$(sumMathFlag)$(descContent[r][c])$(sumMathFlag)}")
              else
                write(b,"\t&\t\\multicolumn{$(widthDescContent[r][c])}{r}{}")
              end
            end
          else
            for c::Int ∈ 1:length(descContent[r]) #for each descriptive row
              if length(descContent[r][c]) ≥ 1
                write(b,"\t&\t$(sumMathFlag)$(descContent[r][c])$(sumMathFlag)")
              else
                write(b,"\t&\t")
              end
            end
          end
            write(b,"\n $(lineSpacer) \t\t") #line-break stylistic formatting
        end
    end

      write(b,""" \\bottomrule""")
  #if we have footnotes
  if length(notes) > 0
      write(b,"""\n \\\\[-1.0ex] \\textit{Notes:} \t """)
      for r ∈ length(notes)
          write(b, "\t&\t \\multicolumn{$numContentCols}{l}{$(notes[r])}\n \\\\")
      end
  end
  write(b, "\t \\end{tabular}\n")
  (!nakedTable) && write(b, " \\\\ \\end{table}")
  return String(take!(copy(b)))
end

#=this version formats a table from a set of linear models
While customizable, the idea here is to have sensible default arguments
where possible.
IN: A collection of models, the rows to capture (returns an empty string in
the table if a row is not present, optionally column names, their width,
names of the rows, names of the descriptive rows, descriptive content,
a boolean switch for including signficance stars, signficance levels for the stars,
a scaling factor, the number of digits in values, and customizable
notes
OUT: A latex table string
=#
function texTable(models::Vector{FMLM}, getΣ::Function, rows::Vector{Symbol};
    titleCaption::String = "",
    caption::String = "",
    colNames::Vector{Vector{String}} = [["C$i" for i ∈ 1:length(models)]],
    contentRowNames::Vector{String} = String.(rows),
    descRowNames::Vector{String}=Vector{String}(),
    descContent::Vector{Vector{String}}=Vector{Vector{String}}(),
    notes::Vector{String} = Vector{String}(),
    arrayStretch::Float64 = 1.5,
    lineSpacer::String = "\\\\",
    summaryMathMode::Bool = true,
    widthColNames::Vector{Vector{Int}} =
        broadcast((i::Int)->ones(Int,length(colNames[i])),1:length(colNames)),
    alignmentColNames::Vector{Vector{String}} = #contains the number of columns for each entry
      broadcast((i::Int)->["r" for i ∈ 1:length(colNames[i])],1:length(colNames)),
    widthDescContent::Vector{Vector{Int}} =
        broadcast((i::Int)->ones(Int,length(descContent[i])),1:length(descContent)),
    stars::Bool=true,
    starLvls::Vector{Float64} = [.9, .95, .99],
    starLegend::String = stars ? DEFAULT_STAR_LEGEND : "",
    starStrings::Vector{String} =
      ["\\ensuremath{^\\text{*}}","\\ensuremath{^\\text{**}}","\\ensuremath{^\\text{***}}"],
    scaling::Vector{Float64}=ones(length(rows)),
    decimalDigits::Int = 2,
    colHeaderName::Vector{String} = ["" for i::Int ∈ 1:length(colNames)],
    nakedTable::Bool = false)

  numCols = length(models)
  numContentRows = length(rows)

  #Pre-allocate the vector of SE errors
  modelsσ::Vector{Vector{Float64}} =
      [Vector{Float64}(undef, models[i].K) for i∈1:numCols]

  #pull out the β coefficients and N
  modelsβ::Vector{Vector{Float64}} = [models[i].β for i::Int ∈ 1:numCols]
  modelsN::Vector{Int} = [models[i].N for i ∈ 1:numCols]
  modelsXNames::Vector{Vector{Symbol}} = [models[i].XNames for i::Int ∈ 1:numCols]

  #get the standard errors
  for c ∈ 1:numCols
      modelsσ[c] .= sqrt.(diag(getΣ(models[c])))
  end

  if length(starLegend) > 0
      notes = [starLegend; notes]
  end

  #get the content matrices
  content::Vector{Matrix{String}} = getContentMatrices!( modelsβ, modelsσ, modelsXNames, modelsN, rows,
      stars=stars, starLvls=starLvls, scaling=scaling,
      decimalDigits=decimalDigits, starStrings=starStrings)

  return texTable(titleCaption = titleCaption,caption=caption, colNames=colNames,
    contentRowNames=contentRowNames, content=content, descRowNames=descRowNames,
    descContent=descContent, notes=notes, arrayStretch=arrayStretch, lineSpacer=lineSpacer,
    summaryMathMode=summaryMathMode, widthColNames=widthColNames,
    widthDescContent=widthDescContent, colHeaderName=colHeaderName,
    alignmentColNames=alignmentColNames, nakedTable=nakedTable)
end

function texTable(models::Vector{FM2SLS}, getΣ::Function, rows::Vector{Symbol};
    titleCaption::String = "",
    caption::String = "",
    colNames::Vector{Vector{String}} = [["C$i" for i ∈ 1:length(models)]],
    contentRowNames::Vector{String} = String.(rows),
    descRowNames::Vector{String}=Vector{String}(),
    descContent::Vector{Vector{String}}=Vector{Vector{String}}(),
    notes::Vector{String} = Vector{String}(),
    arrayStretch::Float64 = 1.5,
    lineSpacer::String = "\\\\",
    summaryMathMode::Bool = true,
    widthColNames::Vector{Vector{Int}} =
        broadcast((i::Int)->ones(Int,length(colNames[i])),1:length(colNames)),
    alignmentColNames::Vector{Vector{String}} = #contains the number of columns for each entry
      broadcast((i::Int)->["r" for i ∈ 1:length(colNames[i])],1:length(colNames)),
    widthDescContent::Vector{Vector{Int}} =
        broadcast((i::Int)->ones(Int,length(descContent[i])),1:length(descContent)),
    stars::Bool=true,
    starLvls::Vector{Float64} = [.9, .95, .99],
    starLegend::String = stars ? DEFAULT_STAR_LEGEND : "",
    starStrings::Vector{String} =
      ["\\ensuremath{^\\text{*}}","\\ensuremath{^\\text{**}}","\\ensuremath{^\\text{***}}"],
    scaling::Vector{Float64}=ones(length(rows)),
    decimalDigits::Int = 2,
    colHeaderName::Vector{String} = ["" for i ∈ 1:length(colNames)],
    nakedTable=nakedTable)

    numCols = length(models)
    numContentRows = length(rows)

    #Pre-allocate the vector of SE errors
    modelsσ::Vector{Vector{Float64}} =
    [Vector{Float64}(models[i].K+models[i].KW) for i∈1:numCols]

    #pull out the β coefficients and N
    modelsβ::Vector{Vector{Float64}} = [models[i].δ2 for i ∈ 1:numCols]
    modelsN::Vector{Int} = [models[i].N for i ∈ 1:numCols]
    modelsXWNames::Vector{Vector{Symbol}} = [[models[i].XNames; models[i].WNames]  for i ∈ 1:numCols]

    #get the standard errors
    for c ∈ 1:numCols
        modelsσ[c] .= sqrt.(diag(getΣ(models[c])))
    end

    if length(starLegend) > 0
        notes = [starLegend; notes]
    end

    #get the content matrices
    content::Vector{Matrix{String}} = getContentMatrices!( modelsβ, modelsσ, modelsXWNames, modelsN, rows,
      stars=stars, starLvls=starLvls, scaling=scaling,
      decimalDigits=decimalDigits, starStrings=starStrings)

    texTable(titleCaption = titleCaption,caption=caption, colNames=colNames,
      contentRowNames=contentRowNames, content=content, descRowNames=descRowNames,
      descContent=descContent, notes=notes, arrayStretch=arrayStretch, lineSpacer=lineSpacer,
      summaryMathMode=summaryMathMode, widthColNames=widthColNames,
      widthDescContent=widthDescContent, colHeaderName=colHeaderName,
      alignmentColNames=alignmentColNames, nakedTable=nakedTable)
end

#=getContentMatrices!
this is a bit of utiltiy code for making tex tables. It is not model specific
hence why it was extracted into its own function
#IN: a vector of βs, σs, names, rows (coefficeints selected). Optional parameters
include a pre-allcoation of the string matrix, a switch for the inclusion of
stars, a scaling factor, and the number of digits (rounding level)=#
#OUT: Writes to and returns the content matrix
function getContentMatrices!(modelsβ::Vector{Vector{Float64}},
    modelsσ::Vector{Vector{Float64}},
    modelsXNames::Vector{Vector{Symbol}},
    modelsN::Vector{Int},
    rows::Vector{Symbol};
    stars::Bool=true, #whether to display signficance stars
    starLvls::Vector{Float64} = [.9, .95, .99],  #cutoffs for signficance (must be sorted)
    starStrings::Vector{String} =
      ["\\ensuremath{^\\text{*}}","\\ensuremath{^\\text{**}}","\\ensuremath{^\\text{***}}"],
    scaling::Vector{Float64}=ones(length(rows)), # an optional scaling factor
    decimalDigits::Int = 2) #number of decimal digits

    content::Vector{Matrix{String}} = #will hold the coefficients and errors
        [fill("",length(rows),length(modelsβ)) for i ∈ 1:2];

    #iterate through the models
    for c ∈ 1:length(modelsβ)

        #build a dictionary of the names
        XNameTbl::Dict = Dict(modelsXNames[c][i] => i for i::Int ∈ 1:length(modelsβ[c]))

        for r ∈ 1:length(rows)
            if haskey(XNameTbl, rows[r]) #need to check if it exists
                ind::Int = XNameTbl[rows[r]]
                p::Float64 =
                    cdf(TDist(modelsN[c]), modelsβ[c][ind]/modelsσ[c][ind]) #get CDF from T distribution
                p = p > .5 ? 1-(1.0 - p)*2.0 : 1.0 - p*2.0 #calc 2-tailed p value
                sigLevel::Int = sum(p.>starLvls)
                if sigLevel > 0 && stars
                    starString::String = starStrings[sum(p.>starLvls)]
                else
                    starString = ""
                end

                #scale, round and write the β coefficeint and σ into the string matrices
                content[1][r,c] =
                  #"$(round(scaling[r]*modelsβ[c][ind],decimalDigits))^{$starString}"
                  "\$$(num2Str(modelsβ[c][ind], decimalDigits, scaleFactor=scaling[r]))\$$starString"
                content[2][r,c] =
                  "(\$$(num2Str(modelsσ[c][ind], decimalDigits, scaleFactor=scaling[r]))\$)"
                  #"($(round(scaling[r]*modelsσ[c][ind],decimalDigits)))"
            end
        end
    end

  return content
end

#=Writes the fully formed string to the file
  IN: A string with the output, optionally a path to the file, and the
  output name
  OUT: Writes the string to a file=#
function writeTables2File(tablesString::String;
    path::String=pwd(),
    outName::String = "$path\\results$(Dates.format(now(),"yymmdd_THMS")).tex")

    oStream::IOStream = open("$path\\$outName","w+")
    write(oStream, tablesString)
    close(oStream)
end

#=Writes the table strings to a file
  Convenience method which takes an array of table strings and optionally
  header and footer strings and writes the table to a tex file
  IN: A string array of table strings, optionally a header string, a footer
  string, a path to the file, the output name
  OUT: Writes the tables to a file=#
function writeTables2File(tables::Vector{String};
    header::String = "\n", footer::String = "",
    path::String=pwd(),
    outName::String = "$path\\results$(Dates.format(now(),"yymmdd_THMS")).tex")

    b::IOBuffer = IOBuffer() #make an IO buffer
    write(b,"\n$header\n\n")
    for t ∈ tables
        write(b,"$t\n\n\n") #write each table
    end
    write(b,footer) #write the footer

    #write the output via another instance
    writeTables2File(String(take!(b)), path=path, outName = outName)
end

#=Writes the table strings to a file
Convenience method which takes an array of table strings and the locations of
header and footer files.
IN: A string array of table strings, the name of a header file, the name of a
a footer file, optionally a path to the file, the output name
OUT: Writes the tables to a file=#
function writeTables2File(tables::Vector{String}, headerName::String,
  footerName::String;
  path::String=pwd(),
  outName::String = "$path\\results$(Dates.format(now(),"yymmdd_THMS")).tex")

  iStream::IOStream = open("$path\\$headerName")
  header::String = String(take!(IOBuffer(read(iStream))))
  close(iStream)

  iStream = open("$path\\$footerName")
  footer::String = String(take!(IOBuffer(read(iStream))))
  close(iStream)

  #write the output via another instance
  writeTables2File(tables, path=path, outName = outName, header=header,
      footer=footer)
end

function writeNakedTable(tableString::String;
  path::String=pwd(),
  outName::String = "$path\\results$(Dates.format(now(),"yymmdd_THMS")).tex")

  oStream::IOStream = open("$path\\$outName","w+")
  write(oStream, tableString)
  close(oStream)
end

#array2String
  #=takes content of an array, formats each cell into a string, appends a
  prefix and suffix, and sends back an array of strings with the same
  dimension as the input
  IN: A general array, optionally a prefix, suffix, scaling factor, and
   set number of decimal digits
  OUT: An array of strings with the same dimensions as the input=#
function array2String(m::Array{Float64}; prefix::String = "",
  suffix::String = "", scaling::Float64 = 1.0,
  decimalDigits::Int = 2)::Array{String}

  #broadcast the prefix, value in m, and suffix and concatenate respectively
  stringMat::Array{String} =
      ((x::Float64)->(prefix*"$(round(x,decimalDigits))"*suffix)).(m)

  return stringMat
end

#vcats like with rbind in r
function vbind(df1::AbstractDataFrame, df2::AbstractDataFrame)::AbstractDataFrame
  #get info on what we are joining
  fields1::Vector{Symbol} = names(df1)
  fields2::Vector{Symbol} = names(df2)
  allFields::Vector{Symbol} = union(fields1, fields2)

  rows1::Int = size(df1,1)
  rows2::Int = size(df2,1)

  for f::Symbol ∈ setdiff(allFields, fields1)
    origType::Type = eltype(df2[f])

    if !(Missing <: origType) #in this case we need to promote the eltype
      origType = Union{origType, Missing}
      df2[f] = Vector{origType}(df2[f])
    end

    df1[ f] = Vector{origType}(missing, rows1)
  end

  for f::Symbol ∈ setdiff(allFields, fields2)
    origType = eltype(df1[f])

    if !(Missing <: origType) #in this case we need to promote the eltype
      origType = Union{origType, Missing}
      df1[f] = Vector{origType}(df1[f])
    end

    #println("origType: $origType")
    df2[ f] = Vector{origType}(missing, rows2)
  end

  return [df1; df2]
end

#recursive version that works similar to vcat with the above stipulations
function vbind(dfs::T...)::T where T<:AbstractDataFrame
  if length(dfs) == 1
    return dfs[1]
  else
    pivot::Int = length(dfs) ÷ 2
    return vbind(vbind(dfs[1:pivot]...), vbind(dfs[(pivot+1):end]...))
  end
end

function IOTest()
  #test parameters
  nrowsContent = 8
  ncols = 4
  nsecs = ncols ÷ 2
  nrowsDesc = 2
  nsubRows = 2

  path = pwd() * "\\results"
  outName = "test.tex"
  footerName = "footer.tex"
  headerName = "header.tex"

  #test table
  caption = "test table"
  colNames::Vector{Vector{String}} =
      [broadcast(i->"secs$i",1:nsecs),broadcast(i->"ncols$i",1:ncols)]
  contentRowNames::Vector{String} = ["nrows$i" for i ∈ 1:nrowsContent]
  content::Vector{Matrix{String}} =
      array2String.(broadcast((i::Int)->i .* ones(nrowsContent,ncols), 1:nsubRows))
  descRowNames::Vector{String} = ["Desc. row $i" for i ∈ 1:nrowsDesc]
  descContent::Vector{Vector{String}} =
      [broadcast(i->"desc-r1c$i",1:ncols),broadcast(i->"desc-r1c$i",1:ncols)]
  notes::Vector{String} = ["a note"]
  widthColNames::Vector{Vector{Int}} =
      [broadcast(i->(ncols ÷ nsecs), 1:nsecs),broadcast(i->1, 1:ncols)]

  s=texTable(caption, colNames,  contentRowNames, content, descRowNames,
      descContent, notes, widthColNames=widthColNames)

  writeTables2File([s,s], headerName, footerName, path=path, outName = outName)

  #println(array2String(fill(.45/π,3,3,4),decimalDigits=4))

  print(s)
end

max_stars_repo_name: clintonTE/CCA

max_issues_repo_name: clintonTE/CCA

max_forks_repo_name: clintonTE/CCA


text:
module StaticCompiler

using GPUCompiler: GPUCompiler
using LLVM
using LLVM.Interop
using LLVM: API
using Libdl: Libdl, dlsym, dlopen
using Base: RefValue
using Serialization: serialize, deserialize
using Clang_jll: clang

export compile, load_function, compile_shlib, compile_executable
export native_code_llvm, native_code_typed, native_llvm_module, native_code_native

include("target.jl")
include("pointer_patching.jl")
include("code_loading.jl")
include("optimize.jl")

"""
    compile(f, types, path::String = tempname()) --> (compiled_f, path)

   !!! Warning: this will fail on programs that have dynamic dispatch !!!

Statically compile the method of a function `f` specialized to arguments of the type given by `types`.

This will create a directory at the specified path (or in a temporary directory if you exclude that argument)
that contains the files needed for your static compiled function. `compile` will return a
`StaticCompiledFunction` object and `obj_path` which is the absolute path of the directory containing the
compilation artifacts. The `StaticCompiledFunction` can be treated as if it is a function with a single
method corresponding to the types you specified when it was compiled.

To deserialize and instantiate a previously compiled function, simply execute `load_function(path)`, which
returns a callable `StaticCompiledFunction`.

### Example:

Define and compile a `fib` function:
```julia
julia> using StaticCompiler

julia> fib(n) = n <= 1 ? n : fib(n - 1) + fib(n - 2)
fib (generic function with 1 method)

julia> fib_compiled, path = compile(fib, Tuple{Int}, "fib")
(f = fib(::Int64) :: Int64, path = "fib")

julia> fib_compiled(10)
55
```
Now we can quit this session and load a new one where `fib` is not defined:
```julia
julia> fib
ERROR: UndefVarError: fib not defined

julia> using StaticCompiler

julia> fib_compiled = load_function("fib.cjl")
fib(::Int64) :: Int64

julia> fib_compiled(10)
55
```
Tada!

### Details:

Here is the structure of the directory created by `compile` in the above example:
```julia
shell> tree fib
path
├── obj.cjl
└── obj.o

0 directories, 3 files
````
* `obj.o` contains statically compiled code in the form of an LLVM generated object file.
* `obj.cjl` is a serialized `LazyStaticCompiledFunction` object which will be deserialized and instantiated
with `load_function(path)`. `LazyStaticcompiledfunction`s contain the requisite information needed to link to the
`obj.o` inside a julia session. Once it is instantiated in a julia session (i.e. by
`instantiate(::LazyStaticCompiledFunction)`, this happens automatically in `load_function`), it will be of type
`StaticCompiledFunction` and may be called with arguments of type `types` as if it were a function with a
single method (the method determined by `types`).
"""
function compile(f, _tt, path::String = tempname();  name = GPUCompiler.safe_name(repr(f)), filename="obj",
                 strip_llvm = false,
                 strip_asm  = true,
                 opt_level=3,
                 kwargs...)
    tt = Base.to_tuple_type(_tt)
    isconcretetype(tt) || error("input type signature $_tt is not concrete")

    rt = only(native_code_typed(f, tt))[2]
    isconcretetype(rt) || error("$f on $_tt did not infer to a concrete type. Got $rt")

    f_wrap!(out::Ref, args::Ref{<:Tuple}) = (out[] = f(args[]...); nothing)
    _, _, table = generate_obj(f_wrap!, Tuple{RefValue{rt}, RefValue{tt}}, path, name; opt_level, strip_llvm, strip_asm, filename, kwargs...)

    lf = LazyStaticCompiledFunction{rt, tt}(Symbol(f), path, name, filename, table)
    cjl_path = joinpath(path, "$filename.cjl")
    serialize(cjl_path, lf)

    (; f = instantiate(lf), path=abspath(path))
end

"""
```julia
generate_obj(f, tt, path::String = tempname(), name = GPUCompiler.safe_name(repr(f)), filenamebase::String="obj";
            \tstrip_llvm = false,
            \tstrip_asm  = true,
            \topt_level=3,
            \tkwargs...)
```
Low level interface for compiling object code (`.o`) for for function `f` given
a tuple type `tt` characterizing the types of the arguments for which the
function will be compiled.

### Examples
```julia
julia> fib(n) = n <= 1 ? n : fib(n - 1) + fib(n - 2)
fib (generic function with 1 method)

julia> path, name, table = StaticCompiler.generate_obj(fib, Tuple{Int64}, "./test")
("./test", "fib", IdDict{Any, String}())

shell> tree \$path
./test
└── obj.o

0 directories, 1 file
```
"""
function generate_obj(f, tt, path::String = tempname(), name = GPUCompiler.safe_name(repr(f)), filenamebase::String="obj";
                        strip_llvm = false,
                        strip_asm  = true,
                        opt_level=3,
                        kwargs...)
    mkpath(path)
    obj_path = joinpath(path, "$filenamebase.o")
    tm = GPUCompiler.llvm_machine(NativeCompilerTarget())
    job, kwargs = native_job(f, tt; name, kwargs...)
    #Get LLVM to generated a module of code for us. We don't want GPUCompiler's optimization passes.
    mod, meta = GPUCompiler.JuliaContext() do context
        GPUCompiler.codegen(:llvm, job; strip=strip_llvm, only_entry=false, validate=false, optimize=false, ctx=context)
    end
    # Use Enzyme's annotation and optimization pipeline
    annotate!(mod)
    optimize!(mod, tm)

    # Scoop up all the pointers in the optimized module, and replace them with unitialized global variables.
    # `table` is a dictionary where the keys are julia objects that are needed by the function, and the values
    # of the dictionary are the names of their associated LLVM GlobalVariable names.
    table = relocation_table!(mod)

    # Now that we've removed all the pointers from the code, we can (hopefully) safely lower all the instrinsics
    # (again, using Enzyme's pipeline)
    post_optimize!(mod, tm)

    # Make sure we didn't make any glaring errors
    LLVM.verify(mod)

    # Compile the LLVM module to native code and save it to disk
    obj, _ = GPUCompiler.emit_asm(job, mod; strip=strip_asm, validate=false, format=LLVM.API.LLVMObjectFile)
    open(obj_path, "w") do io
        write(io, obj)
    end
    path, name, table
end

"""
```julia
compile_executable(f, types::Tuple, path::String, name::String=repr(f); filename::String=name, kwargs...)
```
Attempt to compile a standalone executable that runs function `f` with a type signature given by the tuple of `types`.

### Examples
```julia
julia> using StaticCompiler

julia> function puts(s::Ptr{UInt8}) # Can't use Base.println because it allocates.
           # Note, this `llvmcall` requires Julia 1.8+
           Base.llvmcall((\"""
           ; External declaration of the puts function
           declare i32 @puts(i8* nocapture) nounwind

           define i32 @main(i8*) {
           entry:
               %call = call i32 (i8*) @puts(i8* %0)
               ret i32 0
           }
           \""", "main"), Int32, Tuple{Ptr{UInt8}}, s)
       end
puts (generic function with 1 method)

julia> function print_args(argc::Int, argv::Ptr{Ptr{UInt8}})
           for i=1:argc
               # Get pointer
               p = unsafe_load(argv, i)
               # Print string at pointer location (which fortunately already exists isn't tracked by the GC)
               puts(p)
           end
           return 0
       end

julia> compile_executable(print_args, (Int, Ptr{Ptr{UInt8}}))
"/Users/foo/code/StaticCompiler.jl/print_args"

shell> ./print_args 1 2 3 4 Five
./print_args
1
2
3
4
Five
```
```julia
julia> using StaticTools # So you don't have to define `puts` and friends every time

julia> hello() = println(c"Hello, world!") # c"..." makes a stack-allocated StaticString

julia> compile_executable(hello)
"/Users/foo/code/StaticCompiler.jl/hello"

shell> ./hello
Hello, world!
```
"""
function compile_executable(f, types=(), path::String="./", name=GPUCompiler.safe_name(repr(f));
                            filename=name,
                            kwargs...)

    tt = Base.to_tuple_type(types)
    # tt == Tuple{} || tt == Tuple{Int, Ptr{Ptr{UInt8}}} || error("input type signature $types must be either () or (Int, Ptr{Ptr{UInt8}})")

    rt = only(native_code_typed(f, tt))[2]
    isconcretetype(rt) || error("$f$types did not infer to a concrete type. Got $rt")

    # Would be nice to use a compiler pass or something to check if there are any heap allocations or references to globals
    # Keep an eye on https://github.com/JuliaLang/julia/pull/43747 for this

    generate_executable(f, tt, path, name, filename; kwargs...)

    joinpath(abspath(path), filename)
end

"""
```julia
compile_shlib(f, types::Tuple, path::String, name::String=repr(f); filename::String=name, kwargs...)
```
As `compile_executable`, but compiling to a standalone `.dylib`/`.so` shared library.
"""
function compile_shlib(f, types=(), path::String="./", name=GPUCompiler.safe_name(repr(f));
                        filename=name,
                        kwargs...)

    tt = Base.to_tuple_type(types)
    isconcretetype(tt) || error("input type signature $types is not concrete")

    rt = only(native_code_typed(f, tt))[2]
    isconcretetype(rt) || error("$f$types did not infer to a concrete type. Got $rt")

    # Would be nice to use a compiler pass or something to check if there are any heap allocations or references to globals
    # Keep an eye on https://github.com/JuliaLang/julia/pull/43747 for this

    generate_shlib(f, tt, path, name, filename; kwargs...)

    joinpath(abspath(path), filename * "." * Libdl.dlext)
end

function generate_shlib_fptr(f, tt, path::String=tempname(), name = GPUCompiler.safe_name(repr(f)), filename::String=name;
                            temp::Bool=true,
                            kwargs...)

    generate_shlib(f, tt, path, name; kwargs...)
    lib_path = joinpath(abspath(path), "$filename.$(Libdl.dlext)")
    ptr = Libdl.dlopen(lib_path, Libdl.RTLD_LOCAL)
    fptr = Libdl.dlsym(ptr, "julia_$name")
    @assert fptr != C_NULL
    if temp
        atexit(()->rm(path; recursive=true))
    end
    fptr
end

"""
```julia
generate_shlib_fptr(path::String, name)
```
Low level interface for obtaining a function pointer by `dlopen`ing a shared
library given the `path` and `name` of a `.so`/`.dylib` already compiled by
`generate_shlib`.

See also `StaticCompiler.generate_shlib`.

### Examples
```julia
julia> function test(n)
           r = 0.0
           for i=1:n
               r += log(sqrt(i))
           end
           return r/n
       end
test (generic function with 1 method)

julia> path, name = StaticCompiler.generate_shlib(test, Tuple{Int64}, "./test");

julia> test_ptr = StaticCompiler.generate_shlib_fptr(path, name)
Ptr{Nothing} @0x000000015209f600

julia> ccall(test_ptr, Float64, (Int64,), 100_000)
5.256496109495593

julia> @ccall \$test_ptr(100_000::Int64)::Float64 # Equivalently
5.256496109495593

julia> test(100_000)
5.256496109495593
```
"""
function generate_shlib_fptr(path::String, name, filename::String=name)
    lib_path = joinpath(abspath(path), "$filename.$(Libdl.dlext)")
    ptr = Libdl.dlopen(lib_path, Libdl.RTLD_LOCAL)
    fptr = Libdl.dlsym(ptr, "julia_$name")
    @assert fptr != C_NULL
    fptr
end

"""
```julia
generate_executable(f, tt, path::String, name, filename=string(name); kwargs...)
```
Attempt to compile a standalone executable that runs `f`.

### Examples
```julia
julia> function test(n)
           r = 0.0
           for i=1:n
               r += log(sqrt(i))
           end
           return r/n
       end
test (generic function with 1 method)

julia> path, name = StaticCompiler.generate_executable(test, Tuple{Int64}, "./scratch")
```
"""
function generate_executable(f, tt, path::String = tempname(), name = GPUCompiler.safe_name(repr(f)), filename::String=string(name); kwargs...)
    mkpath(path)
    obj_path = joinpath(path, "$filename.o")
    exec_path = joinpath(path, filename)
    job, kwargs = native_job(f, tt; name, kwargs...)
    obj, _ = GPUCompiler.codegen(:obj, job; strip=true, only_entry=false, validate=false)

    # Write to file
    open(obj_path, "w") do io
        write(io, obj)
    end

    # Pick a compiler
    cc = Sys.isapple() ? `cc` : clang()
    # Compile!
    if Sys.isapple()
        # Apple no longer uses _start, so we can just specify a custom entry
        entry = "_julia_$name"
        run(`$cc -e $entry $obj_path -o $exec_path`)
    else
        # Write a minimal wrapper to avoid having to specify a custom entry
        wrapper_path = joinpath(path, "wrapper.c")
        f = open(wrapper_path, "w")
        print(f, """int main(int argc, char** argv)
        {
            julia_$name(argc, argv);
            return 0;
        }""")
        close(f)
        run(`$cc $wrapper_path $obj_path -o $exec_path`)
        # Clean up
        run(`rm $wrapper_path`)
    end

    path, name
end

"""
```julia
generate_shlib(f, tt, path::String, name::String, filenamebase::String="obj"; kwargs...)
```
Low level interface for compiling a shared object / dynamically loaded library
 (`.so` / `.dylib`) for function `f` given a tuple type `tt` characterizing
the types of the arguments for which the function will be compiled.
See also `StaticCompiler.generate_shlib_fptr`.
### Examples
```julia
julia> function test(n)
           r = 0.0
           for i=1:n
               r += log(sqrt(i))
           end
           return r/n
       end
test (generic function with 1 method)
julia> path, name = StaticCompiler.generate_shlib(test, Tuple{Int64}, "./test")
("./test", "test")
shell> tree \$path
./test
|-- obj.o
`-- obj.so
0 directories, 2 files
julia> test(100_000)
5.256496109495593
julia> ccall(StaticCompiler.generate_shlib_fptr(path, name), Float64, (Int64,), 100_000)
5.256496109495593
```
"""
function generate_shlib(f, tt, path::String = tempname(), name = GPUCompiler.safe_name(repr(f)), filename::String=name; kwargs...)
    mkpath(path)
    obj_path = joinpath(path, "$filename.o")
    lib_path = joinpath(path, "$filename.$(Libdl.dlext)")
    job, kwargs = native_job(f, tt; name, kwargs...)
    obj, _ = GPUCompiler.codegen(:obj, job; strip=true, only_entry=false, validate=false)

    open(obj_path, "w") do io
        write(io, obj)
    end

    # Pick a Clang
    cc = Sys.isapple() ? `cc` : clang()
    # Compile!
    run(`$cc -shared -o $lib_path $obj_path`)

    path, name
end

function native_code_llvm(@nospecialize(func), @nospecialize(types); kwargs...)
    job, kwargs = native_job(func, types; kwargs...)
    GPUCompiler.code_llvm(stdout, job; kwargs...)
end

function native_code_typed(@nospecialize(func), @nospecialize(types); kwargs...)
    job, kwargs = native_job(func, types; kwargs...)
    GPUCompiler.code_typed(job; kwargs...)
end

# Return an LLVM module
function native_llvm_module(f, tt, name = GPUCompiler.safe_name(repr(f)); kwargs...)
    job, kwargs = native_job(f, tt; name, kwargs...)
    m, _ = GPUCompiler.JuliaContext() do context
        GPUCompiler.codegen(:llvm, job; strip=true, only_entry=false, validate=false, ctx=context)
    end
    return m
end

function native_code_native(@nospecialize(f), @nospecialize(tt), name = GPUCompiler.safe_name(repr(f)); kwargs...)
    job, kwargs = native_job(f, tt; name, kwargs...)
    GPUCompiler.code_native(stdout, job; kwargs...)
end

end # module