text:
"""
    Tangent{P, T} <: AbstractTangent

This type represents the differential for a `struct`/`NamedTuple`, or `Tuple`.
`P` is the the corresponding primal type that this is a differential for.

`Tangent{P}` should have fields (technically properties), that match to a subset of the
fields of the primal type; and each should be a differential type matching to the primal
type of that field.
Fields of the P that are not present in the Tangent are treated as `Zero`.

`T` is an implementation detail representing the backing data structure.
For Tuple it will be a Tuple, and for everything else it will be a `NamedTuple`.
It should not be passed in by user.

For `Tangent`s of `Tuple`s, `iterate` and `getindex` are overloaded to behave similarly
to for a tuple.
For `Tangent`s of `struct`s, `getproperty` is overloaded to allow for accessing values
via `comp.fieldname`.
Any fields not explictly present in the `Tangent` are treated as being set to `ZeroTangent()`.
To make a `Tangent` have all the fields of the primal the [`canonicalize`](@ref)
function is provided.
"""
struct Tangent{P, T} <: AbstractTangent
    # Note: If T is a Tuple/Dict, then P is also a Tuple/Dict
    # (but potentially a different one, as it doesn't contain differentials)
    backing::T
end

function Tangent{P}(; kwargs...) where P
    backing = (; kwargs...)  # construct as NamedTuple
    return Tangent{P, typeof(backing)}(backing)
end

function Tangent{P}(args...) where P
    return Tangent{P, typeof(args)}(args)
end

function Tangent{P}() where P<:Tuple
    backing = ()
    return Tangent{P, typeof(backing)}(backing)
end

function Tangent{P}(d::Dict) where {P<:Dict}
    return Tangent{P, typeof(d)}(d)
end

function Base.:(==)(a::Tangent{P, T}, b::Tangent{P, T}) where {P, T}
    return backing(a) == backing(b)
end
function Base.:(==)(a::Tangent{P}, b::Tangent{P}) where {P, T}
    all_fields = union(keys(backing(a)), keys(backing(b)))
    return all(getproperty(a, f) == getproperty(b, f) for f in all_fields)
end
Base.:(==)(a::Tangent{P}, b::Tangent{Q}) where {P, Q} = false

Base.hash(a::Tangent, h::UInt) = Base.hash(backing(canonicalize(a)), h)

function Base.show(io::IO, comp::Tangent{P}) where P
    print(io, "Tangent{")
    show(io, P)
    print(io, "}")
    if isempty(backing(comp))
        print(io, "()")  # so it doesn't show `NamedTuple()`
    else
        # allow Tuple or NamedTuple `show` to do the rendering of brackets etc
        show(io, backing(comp))
    end
end

Base.convert(::Type{<:NamedTuple}, comp::Tangent{<:Any, <:NamedTuple}) = backing(comp)
Base.convert(::Type{<:Tuple}, comp::Tangent{<:Any, <:Tuple}) = backing(comp)
Base.convert(::Type{<:Dict}, comp::Tangent{<:Dict, <:Dict}) = backing(comp)

Base.getindex(comp::Tangent, idx) = getindex(backing(comp), idx)

# for Tuple
Base.getproperty(comp::Tangent, idx::Int) = unthunk(getproperty(backing(comp), idx))
function Base.getproperty(
    comp::Tangent{P, T}, idx::Symbol
) where {P, T<:NamedTuple}
    hasfield(T, idx) || return ZeroTangent()
    return unthunk(getproperty(backing(comp), idx))
end

Base.keys(comp::Tangent) = keys(backing(comp))
Base.propertynames(comp::Tangent) = propertynames(backing(comp))

Base.haskey(comp::Tangent, key) = haskey(backing(comp), key)
if isdefined(Base, :hasproperty)
    Base.hasproperty(comp::Tangent, key::Symbol) = hasproperty(backing(comp), key)
end

Base.iterate(comp::Tangent, args...) = iterate(backing(comp), args...)
Base.length(comp::Tangent) = length(backing(comp))
Base.eltype(::Type{<:Tangent{<:Any, T}}) where T = eltype(T)
function Base.reverse(comp::Tangent)
    rev_backing = reverse(backing(comp))
    Tangent{typeof(rev_backing), typeof(rev_backing)}(rev_backing)
end

function Base.indexed_iterate(comp::Tangent{P,<:Tuple}, i::Int, state=1) where {P}
    return Base.indexed_iterate(backing(comp), i, state)
end

function Base.map(f, comp::Tangent{P, <:Tuple}) where P
    vals::Tuple = map(f, backing(comp))
    return Tangent{P, typeof(vals)}(vals)
end
function Base.map(f, comp::Tangent{P, <:NamedTuple{L}}) where{P, L}
    vals = map(f, Tuple(backing(comp)))
    named_vals = NamedTuple{L, typeof(vals)}(vals)
    return Tangent{P, typeof(named_vals)}(named_vals)
end
function Base.map(f, comp::Tangent{P, <:Dict}) where {P<:Dict}
    return Tangent{P}(Dict(k => f(v) for (k, v) in backing(comp)))
end

Base.conj(comp::Tangent) = map(conj, comp)

"""
    backing(x)

Accesses the backing field of a `Tangent`,
or destructures any other composite type into a `NamedTuple`.
Identity function on `Tuple`. and `NamedTuple`s.

This is an internal function used to simplify operations between `Tangent`s and the
primal types.
"""
backing(x::Tuple) = x
backing(x::NamedTuple) = x
backing(x::Dict) = x
backing(x::Tangent) = getfield(x, :backing)

# For generic structs
function backing(x::T)::NamedTuple where T
    # note: all computation outside the if @generated happens at runtime.
    # so the first 4 lines of the branchs look the same, but can not be moved out.
    # see https://github.com/JuliaLang/julia/issues/34283
    if @generated
        !isstructtype(T) && throw(DomainError(T, "backing can only be use on composite types"))
        nfields = fieldcount(T)
        names = fieldnames(T)
        types = fieldtypes(T)

        vals = Expr(:tuple, ntuple(ii->:(getfield(x, $ii)), nfields)...)
        return :(NamedTuple{$names, Tuple{$(types...)}}($vals))
    else
        !isstructtype(T) && throw(DomainError(T, "backing can only be use on composite types"))
        nfields = fieldcount(T)
        names = fieldnames(T)
        types = fieldtypes(T)

        vals = ntuple(ii->getfield(x, ii), nfields)
        return NamedTuple{names, Tuple{types...}}(vals)
    end
end

"""
    canonicalize(comp::Tangent{P}) -> Tangent{P}

Return the canonical `Tangent` for the primal type `P`.
The property names of the returned `Tangent` match the field names of the primal,
and all fields of `P` not present in the input `comp` are explictly set to `ZeroTangent()`.
"""
function canonicalize(comp::Tangent{P, <:NamedTuple{L}}) where {P,L}
    nil = _zeroed_backing(P)
    combined = merge(nil, backing(comp))
    if length(combined) !== fieldcount(P)
        throw(ArgumentError(
            "Tangent fields do not match primal fields.\n" *
            "Tangent fields: $L. Primal ($P) fields: $(fieldnames(P))"
        ))
    end
    return Tangent{P, typeof(combined)}(combined)
end

# Tuple composites are always in their canonical form
canonicalize(comp::Tangent{<:Tuple, <:Tuple}) = comp

# Dict composite are always in their canonical form.
canonicalize(comp::Tangent{<:Any, <:AbstractDict}) = comp

# Tangents of unspecified primal types (indicated by specifying exactly `Any`)
# all combinations of type-params are specified here to avoid ambiguities
canonicalize(comp::Tangent{Any, <:NamedTuple{L}}) where {L} = comp
canonicalize(comp::Tangent{Any, <:Tuple}) where {L} = comp
canonicalize(comp::Tangent{Any, <:AbstractDict}) where {L} = comp

"""
    _zeroed_backing(P)

Returns a NamedTuple with same fields as `P`, and all values `ZeroTangent()`.
"""
@generated function _zeroed_backing(::Type{P}) where P
    nil_base = ntuple(fieldcount(P)) do i
        (fieldname(P, i), ZeroTangent())
    end
    return (; nil_base...)
end

"""
    construct(::Type{T}, fields::[NamedTuple|Tuple])

Constructs an object of type `T`, with the given fields.
Fields must be correct in name and type, and `T` must have a default constructor.

This internally is called to construct structs of the primal type `T`,
after an operation such as the addition of a primal to a composite.

It should be overloaded, if `T` does not have a default constructor,
or if `T` needs to maintain some invarients between its fields.
"""
function construct(::Type{T}, fields::NamedTuple{L}) where {T, L}
    # Tested and verified that that this avoids a ton of allocations
    if length(L) !== fieldcount(T)
        # if length is equal but names differ then we will catch that below anyway.
        throw(ArgumentError("Unmatched fields. Type: $(fieldnames(T)),  NamedTuple: $L"))
    end

    if @generated
        vals = (:(getproperty(fields, $(QuoteNode(fname)))) for fname in fieldnames(T))
        return :(T($(vals...)))
    else
        return T((getproperty(fields, fname) for fname in fieldnames(T))...)
    end
end

construct(::Type{T}, fields::T) where T<:NamedTuple = fields
construct(::Type{T}, fields::T) where T<:Tuple = fields

elementwise_add(a::Tuple, b::Tuple) = map(+, a, b)

function elementwise_add(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn}
    # Rule of Tangent addition: any fields not present are implict hard Zeros

    # Base on the `merge(:;NamedTuple, ::NamedTuple)` code from Base.
    # https://github.com/JuliaLang/julia/blob/592748adb25301a45bd6edef3ac0a93eed069852/base/namedtuple.jl#L220-L231
    if @generated
        names = Base.merge_names(an, bn)

        vals = map(names) do field
            a_field = :(getproperty(a, $(QuoteNode(field))))
            b_field = :(getproperty(b, $(QuoteNode(field))))
            value_expr = if Base.sym_in(field, an)
                if Base.sym_in(field, bn)
                    # in both
                    :($a_field + $b_field)
                else
                    # only in `an`
                    a_field
                end
            else # must be in `b` only
                b_field
            end
            Expr(:kw, field, value_expr)
        end
        return Expr(:tuple, Expr(:parameters, vals...))
    else
        names = Base.merge_names(an, bn)
        vals = map(names) do field
            value = if Base.sym_in(field, an)
                a_field = getproperty(a, field)
                if Base.sym_in(field, bn)
                    # in both
                    b_field = getproperty(b, field)
                    a_field + b_field
                else
                    # only in `an`
                    a_field
                end
            else # must be in `b` only
                getproperty(b, field)
            end
            field => value
        end
        return (;vals...)
    end
end

elementwise_add(a::Dict, b::Dict) = merge(+, a, b)

struct PrimalAdditionFailedException{P} <: Exception
    primal::P
    differential::Tangent{P}
    original::Exception
end

function Base.showerror(io::IO, err::PrimalAdditionFailedException{P}) where {P}
    println(io, "Could not construct $P after addition.")
    println(io, "This probably means no default constructor is defined.")
    println(io, "Either define a default constructor")
    printstyled(io, "$P(", join(propertynames(err.differential), ", "), ")", color=:blue)
    println(io, "\nor overload")
    printstyled(io,
        "ChainRulesCore.construct(::Type{$P}, ::$(typeof(err.differential)))";
        color=:blue
    )
    println(io, "\nor overload")
    printstyled(io, "Base.:+(::$P, ::$(typeof(err.differential)))"; color=:blue)
    println(io, "\nOriginal Exception:")
    printstyled(io, err.original; color=:yellow)
    println(io)
end


text:
include("./params.jl")
include("./fitgamma.jl")

function ssgamma(data)
    # Example usage:
    # x = 0.5-0.5*log(rand(1,1e3));
    # rs = ssgamma(x);
    #
    # Input argument
    # data: Spike-rate.
    #
    # Output argument
    # rs:   Estimated intensity function

    ################# WRITE SOMETHING #################

    return rs
end

ext: jl

max_stars_repo_name: ganow/julia-point-process

max_issues_repo_name: ganow/julia-point-process

max_forks_repo_name: ganow/julia-point-process


text:
# Recursion will be more performant than iteration for small tuples. My own recursive map makes sure that there is no iteration for performance
function map_unrolled(call, variables::Tuple{})
    ()
end
function map_unrolled(call, variables)
    (call(first(variables)), map_unrolled(call, tail(variables))...)
end

function map_unrolled(call, variables1::Tuple{}, variables2::Tuple{})
    ()
end
function map_unrolled(call, variables1, variables2)
    (
        call(first(variables1), first(variables2)),
        map_unrolled(call, tail(variables1), tail(variables2))...
    )
end

# In partial map, fixed is passed each time. This avoids captures, which have performace issues
function partial_map(call, fixed, variables::Tuple{})
    ()
end
function partial_map(call, fixed, variables)
    (
        call(fixed, first(variables)),
        partial_map(call, fixed, tail(variables))...
    )
end
function partial_map(call, fixed, variables1::Tuple{}, variables2::Tuple{})
    ()
end
function partial_map(call, fixed, variables1, variables2)
    (
        call(fixed, first(variables1), first(variables2)),
        partial_map(call, fixed, tail(variables1), tail(variables2))...
    )
end

function as_symbols(them)
    map_unrolled(Symbol, (them...,))
end

split_keyword(keyword::Expr) =
    if keyword.head === :kw
        Pair(keyword.args[1], keyword.args[2])
    else
        error("Cannot split keyword $keyword")
    end

# Split a function call into its pieces
# Normalize non-function-like patterns into function calls
function split_call(call_expression::Expr)
    if @capture call_expression call_(arguments__; keywords__)
        (call, arguments...), (; map(split_keyword, keywords)...)
    elseif @capture call_expression call_(arguments__)
        (call, arguments...), ()
    else
        error("$call_expression is not a function call")
    end
end

"""
    if_else(switch, yes, no)

`ifelse` that you can add methods to.

```jldoctest
julia> using QuerySQLite

julia> if_else(true, 1, 0)
1

julia> if_else(false, 1, 0)
0
```
"""
function if_else(switch, yes, no)
    ifelse(switch, yes, no)
end
export if_else

"""
    type_of(it)

`typeof` that you can add methods to.

```jldoctest
julia> using QuerySQLite

julia> type_of('a')
Char
```
"""
function type_of(it)
    typeof(it)
end
export type_of


text:
@testset "Primitives" begin

using Base.Test
using DataValueArrays

n = rand(1:5)
siz = [ rand(2:5) for i in n ]
A = rand(siz...)
M = rand(Bool, siz...)
X = DataValueArray(A, M)
i = rand(1:length(X))
@test values(X, i) == X.values[i]
@test isnull(X, i) == X.isnull[i]
I = [ rand(1:size(X,i)) for i in 1:n ]
@test values(X, I...) == X.values[I...]
@test isnull(X, I...) == X.isnull[I...]

# ----- test Base.similar, Base.size  ----------------------------------------#

x = DataValueArray(Int, (5, 2))
@test isa(x, DataValueMatrix{Int})
@test size(x) === (5, 2)

y = similar(x, DataValue{Int}, (3, 3))
@test isa(y, DataValueMatrix{Int})
@test size(y) === (3, 3)

z = similar(x, DataValue{Int}, (2,))
@test isa(z, DataValueVector{Int})
@test size(z) === (2, )

# test common use-patterns for 'similar'
dv = DataValueArray(Int, 2)
dm = DataValueArray(Int, 2, 2)
dt = DataValueArray(Int, 2, 2, 2)

similar(dv)
similar(dm)
similar(dt)

similar(dv, 2)
similar(dm, 2, 2)
similar(dt, 2, 2, 2)

# ----- test Base.copy/Base.copy! --------------------------------------------#

#copy
x = DataValueArray([1, 2, nothing], Int, Void)
y = DataValueArray([3, nothing, 5], Int, Void)
@test isequal(copy(x), x)
@test isequal(copy!(y, x), x)

# copy!
function nonbits(dv)
    ret = similar(dv, Integer)
    for i = 1:length(dv)
        if !dv.isnull[i]
            ret[i] = dv[i]
        end
    end
    ret
end
set1 = Any[DataValueArray([1, nothing, 3], Int, Void),
            DataValueArray([nothing, 5], Int, Void),
            DataValueArray([1, 2, 3, 4, 5], Int, Void),
            DataValueArray(Int[]),
            DataValueArray([nothing, 5, 3], Int, Void),
            DataValueArray([1, 5, 3], Int, Void)]
set2 = map(nonbits, set1)

for (dest, src, bigsrc, emptysrc, res1, res2) in Any[set1, set2]
    @test isequal(copy!(copy(dest), src), res1)
    @test isequal(copy!(copy(dest), 1, src), res1)

    @test isequal(copy!(copy(dest), 2, src, 2), res2)
    @test isequal(copy!(copy(dest), 2, src, 2, 1), res2)

    @test isequal(copy!(copy(dest), 99, src, 99, 0), dest)

    @test isequal(copy!(copy(dest), 1, emptysrc), dest)
    @test_throws BoundsError copy!(dest, 1, emptysrc, 1)

    for idx in [0, 4]
        @test_throws BoundsError copy!(dest, idx, src)
        @test_throws BoundsError copy!(dest, idx, src, 1)
        @test_throws BoundsError copy!(dest, idx, src, 1, 1)
        @test_throws BoundsError copy!(dest, 1, src, idx)
        @test_throws BoundsError copy!(dest, 1, src, idx, 1)
    end

    @test_throws ArgumentError copy!(dest, 1, src, 1, -1)

    @test_throws BoundsError copy!(dest, bigsrc)

    @test_throws BoundsError copy!(dest, 3, src)
    @test_throws BoundsError copy!(dest, 3, src, 1)
    @test_throws BoundsError copy!(dest, 3, src, 1, 2)
    @test_throws BoundsError copy!(dest, 1, src, 2, 2)
end

# ----- test Base.fill! ------------------------------------------------------#

X = DataValueArray(Int, 10, 2)
fill!(X, DataValue(10))
Y = DataValueArray(Float64, 10)
fill!(Y, rand(Float64))

@test X.values == fill(10, 10, 2)
@test isequal(X.isnull, fill(false, 10, 2))
@test isequal(Y.isnull, fill(false, 10))

fill!(X, DataValue())
@test isequal(X.isnull, fill(true, 10, 2))

# ----- test Base.deepcopy ---------------------------------------------------#

Y1 = deepcopy(Y)
@test isequal(Y1, Y)
@assert !(Y === Y1)

# ----- test Base.resize! ----------------------------------------------------#

resize!(Y1, 20)
@test Y1.values[1:10] == Y.values[1:10]
@test Y1.isnull[1:10] == Y.isnull[1:10]
@test Y1.isnull[11:20] == fill(true, 10)

resize!(Y1, 5)
@test Y1.values[1:5] == Y.values[1:5]
@test Y1.isnull[1:5] == Y.isnull[1:5]

# ----- test Base.reshape ----------------------------------------------------#

Y1 = reshape(copy(Y), length(Y), 1)
@test size(Y1) == (length(Y), 1)
@test all(i->isequal(Y1[i], Y[i]), 1:length(Y))
Y2 = reshape(Y1, 1, length(Y1))
@test size(Y2) == (1, length(Y1))
@test all(i->isequal(Y1[i], Y2[i]), 1:length(Y2))
# Test that arrays share the same data
Y2.values[1] += 1
Y2.isnull[2] = true
@test all(i->isequal(Y1[i], Y2[i]), 1:length(Y2))

# ----- test Base.ndims ------------------------------------------------------#

for n in 1:4
    @test ndims(DataValueArray(Int, collect(1:n)...)) == n
end

# ----- test Base.length -----------------------------------------------------#

@test length(DataValueArray(Int, 10)) == 10
@test length(DataValueArray(Int, 5, 5)) == 25
@test length(DataValueArray(Int, (3, 3, 3))) == 27

# ----- test Base.endof ------------------------------------------------------#

@test endof(DataValueArray(collect(1:10))) == 10
@test endof(DataValueArray([1, 2, nothing, 4, nothing])) == 5

# ----- test Base.find -------------------------------------------------------#

z = DataValueArray(rand(Bool, 10))
@test find(z) == find(z.values)

z = DataValueArray([false, true, false, true, false, true])
@test isequal(find(z), [2, 4, 6])

# ----- test dropnull --------------------------------------------------------#

# dropnull(X::DataValueVector)
z = DataValueArray([1, 2, 3, 'a', 5, 'b', 7, 'c'], Int, Char)
@test dropnull(z) == [1, 2, 3, 5, 7]

# dropnull(X::AbstractVector)
A = Any[1, 2, 3, DataValue(), 5, DataValue(), 7, DataValue()]
@test dropnull(A) == [1, 2, 3, 5, 7]

# dropnull(X::AbstractVector{<:DataValue})
B = [1, 2, 3, DataValue(), 5, DataValue(), 7, DataValue()]
@test dropnull(B) == [1, 2, 3, 5, 7]
# assert dropnull returns copy for !(DataValue <: eltype(X))
nullfree = [1, 2, 3, 4]
returned_copy = dropnull(nullfree)
@test nullfree == returned_copy && !(nullfree === returned_copy)

# ----- test dropnull! -------------------------------------------------------#

# for each, assert returned values are unwrapped and inplace change
# dropnull!(X::DataValueVector)
@test dropnull!(z) == [1, 2, 3, 5, 7]
@test isequal(z, DataValueArray([1, 2, 3, 5, 7]))

# dropnull!(X::AbstractVector)
@test dropnull!(A) == [1, 2, 3, 5, 7]
@test isequal(A, Any[1, 2, 3, 5, 7])

# dropnull!(X::AbstractVector{<:DataValue})
@test dropnull!(B) == [1, 2, 3, 5, 7]
@test isequal(B, DataValue[1, 2, 3, 5, 7])

# when no nulls present, dropnull! returns input vector
returned_view = dropnull!(nullfree)
@test nullfree == returned_view && nullfree === returned_view

# test that dropnull! returns unwrapped values when DataValues are present
X = [false, 1, :c, "string", DataValue("I am not null"), DataValue()]
@test !any(x -> isa(x, DataValue), dropnull!(X))
@test any(x -> isa(x, DataValue), X)
Y = Any[false, 1, :c, "string", DataValue("I am not null"), DataValue()]
@test !any(x -> isa(x, DataValue), dropnull!(Y))
@test any(x -> isa(x, DataValue), Y)

# ----- test any(isnull, X) --------------------------------------------------#

# any(isnull, X::DataValueArray)
z = DataValueArray([1, 2, 3, 'a', 5, 'b', 7, 'c'], Int, Char)
@test any(isnull, z) == true
@test any(isnull, dropnull(z)) == false
z = DataValueArray(Int, 10)
@test any(isnull, z) == true

# any(isnull, A::AbstractArray)
A2 = [DataValue(1), DataValue(2), DataValue(3)]
@test any(isnull, A2) == false
push!(A2, DataValue{Int}())
@test any(isnull, A2) == true

# any(isnull, xs::NTuple)
@test any(isnull, (DataValue(1), DataValue(2))) == false
@test any(isnull, (DataValue{Int}(), DataValue(1), 3, 6)) == true

# any(isnull, S::SubArray{T, N, U<:DataValueArray})
A = rand(10, 3, 3)
M = rand(Bool, 10, 3, 3)
X = DataValueArray(A, M)
i, j = rand(1:3), rand(1:3)
S = view(X, :, i, j)

@test any(isnull, S) == any(isnull, X[:, i, j])
X = DataValueArray(A)
S = view(X, :, i, j)
@test any(isnull, S) == false

# ----- test all(isnull, X) --------------------------------------------------#

# all(isnull, X::DataValueArray)
z = DataValueArray(Int, 10)
@test all(isnull, z) == true
z[1] = 10
@test all(isnull, z) == false

# all(isnull, X::AbstractArray{<:DataValue})
@test all(isnull, DataValue{Int}[DataValue(), DataValue()]) == true
@test all(isnull, DataValue{Int}[DataValue(1), DataValue()]) == false

# all(isnull, X::Any)
@test all(isnull, Any[DataValue(), DataValue()]) == true
@test all(isnull, [1, 2]) == false
@test all(isnull, 1:3) == false

# ----- test Base.isnan ------------------------------------------------------#

x = DataValueArray([1, 2, NaN, 4, 5, NaN, Inf, nothing], Float64, Void)
_x = isnan(x)
@test isequal(_x, DataValueArray([false, false, true, false,
                                false, true, false, nothing], Bool, Void))
@test _x.isnull[8] == true

# ----- test Base.isfinite ---------------------------------------------------#

_x = isfinite(x)
@test isequal(_x, DataValueArray([true, true, false, true,
                                true, false, false, nothing], Bool, Void))
@test _x.isnull[8] == true

# ----- test conversion methods ----------------------------------------------#

u = DataValueArray(collect(1:10))
v = DataValueArray(Int, 4, 4)
fill!(v, 4)
w = DataValueArray(['a', 'b', 'c', 'd', 'e', 'f', nothing], Char, Void)
x = DataValueArray([(i, j, k) for i in 1:10, j in 1:10, k in 1:10])
y = DataValueArray([2, 4, 6, 8, 10])
z = DataValueArray([i*j for i in 1:10, j in 1:10])
_z = DataValueArray(reshape(collect(1:100), 10, 10),
                    convert(Array{Bool},
                            reshape([mod(j, 2) for i in 1:10, j in 1:10],
                                    (10, 10))))
_x = DataValueArray([false, true, false, nothing, false, true, nothing],
                    Bool, Void)
a = [i*j*k for i in 1:2, j in 1:2, k in 1:2]
b = collect(1:10)
c = [i for i in 1:10, j in 1:10]

e = convert(DataValueArray, a)
f = convert(DataValueArray{Float64}, b)
g = convert(DataValueArray, c)
h = convert(DataValueArray{Float64}, g)

@test_throws NullException convert(Array{Char, 1}, w)
@test convert(Array{Char, 1},
                DataValueArray(dropnull(w))) == ['a', 'b', 'c', 'd', 'e', 'f']
@test_throws NullException convert(Array{Char}, w)
@test convert(Array{Float64}, u) == Float64[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@test convert(Vector{Float64}, y) == Float64[2, 4, 6, 8, 10]
@test convert(Matrix{Float64}, z) == Float64[i*j for i in 1:10, j in 1:10]
@test_throws NullException convert(Array, w)
@test convert(Array, v) == [4 4 4 4; 4 4 4 4; 4 4 4 4; 4 4 4 4]
@test convert(Array, z) == [i*j for i in 1:10, j in 1:10]
@test convert(Array, u) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@test convert(Array, _x, false) == [false, true, false, false,
                                    false, true, false]
@test convert(Array{Int, 1}, _x, 0) == [0, 1, 0, 0, 0, 1, 0]
@test convert(Vector, _x, false) == [false, true, false, false,
                                    false, true, false]
@test sum(convert(Matrix, _z, 0)) == 2775
@test isequal(e[:, :, 1], DataValueArray([1 2; 2 4]))
@test isequal(f, DataValueArray(Float64[i for i in 1:10]))
@test isa(g, DataValueArray{Int, 2})
@test isa(h, DataValueArray{Float64, 2})

# Base.convert{T}(::Type{Vector}, X::DataValueVector{T})
X = DataValueArray([1, 2, 3, 4, 5])
@test convert(Vector, X) == [1, 2, 3, 4, 5]
push!(X, DataValue())
@test_throws NullException convert(Vector, X)

# Base.convert{T}(::Type{Matrix}, X::DataValueMatrix{T})
Y = DataValueArray([1 2; 3 4; 5 6; 7 8; 9 10])
@test convert(Matrix, Y) == [1 2; 3 4; 5 6; 7 8; 9 10]
Z = DataValueArray([1 2; 3 4; 5 6; 7 8; 9 nothing], Int, Void)
@test_throws NullException convert(Matrix, Z)

# float(X::DataValueArray)
A = rand(Int, 20)
M = rand(Bool, 20)
X = DataValueArray(A, M)
@test isequal(float(X), DataValueArray(float(A), M))

# ----- test Base.hash (julia/base/hashing.jl:5) -----------------------------#

# Omitted for now, pending investigation into DataValueArray-specific
# method.
# TODO: reinstate testing once decision whether or not to implement
# DataValueArray-specific hash method is reached.

# ----- test unique (julia/base/set.jl:107) ----------------------------------#

x = DataValueArray([1, nothing, -2, 1, nothing, 4], Int, Void)
@assert isequal(unique(x), DataValueArray([1, nothing, -2, 4], Int, Void))
@assert isequal(unique(reverse(x)),
                DataValueArray([4, nothing, 1, -2], Int, Void))

end


text:
using Painleve, SingularIntegralEquations, RiemannHilbert, ApproxFun, Test
import RiemannHilbert: RiemannDual, fpstieltjesmatrix!, orientedrightendpoint, Directed
import ApproxFun: mobius

@test painleve2_6ray((-im,0,im),0.0) ≈ 0.36706155154807807 # RHPackage
@test painleve2_6ray((1,2,1/3),0.0) ≈ -0.5006840381943177 + 0.11748452477363962im # RHPackage

@test pl2def_no_s2_pos_x((-im,0,im),1.0;n=400) ≈ painleve2_6ray((-im,0,im),1.0) ≈ 0.13564354350447155
@test pl2def_no_s2_neg_x1((-im,0,im),-5.0) ≈ painleve2_6ray((-im,0,im),-5.0) ≈ 1.5794870908867027

@test pl2def_no_s2_neg_x2((-0.5im,0,0.5im),-7.0) ≈ painleve2_6ray((-0.5im,0,0.5im),-7.0)

@test pl2def_no_s2_neg_x2((-2im,0,2im),-7.0) ≈ painleve2_6ray((-2im,0,2im),-7.0) ≈ 4.819318173454774 # RHPackage
@test pl2def_no_s2_neg_x3((-2im,0,2im),-7.0) ≈ painleve2_6ray((-2im,0,2im),-7.0) ≈ 4.819318173454774

@testset "small x error" begin
    s1,s2,s3 = -2im,0,2im
    x = -7.0
    z_0 = sqrt(-x)/2

    ΓD  = Segment(-z_0, z_0)
    Γ1  = Segment(z_0, z_0 + 2.5exp(im*π/4))
    ΓUi = Segment(z_0, z_0 + 2.5exp(im*3π/4))
    ΓU  = Segment(-z_0, -z_0 + 2.5exp(im*π/4))
    Γ3  = Segment(-z_0, -z_0 + 2.5exp(im*3π/4))
    Γ4  = Segment(-z_0, -z_0 + 2.5exp(-im*3π/4))
    ΓL  = Segment(-z_0, -z_0 + 2.5exp(-im*π/4))
    ΓLi = Segment(z_0, z_0 + 2.5exp(-im*3π/4))
    Γ6  = Segment(z_0, z_0 + 2.5exp(-im*π/4))
    Γ = ΓD ∪ Γ1 ∪ ΓUi ∪ ΓU ∪ Γ3 ∪ Γ4 ∪ ΓL ∪ ΓLi ∪ Γ6

    Θ(z) = 8/3*z^3+2*x*z

    D(z)     = [1-s1*s3 0; 0 1/(1-s1*s3)]
    S1(z)    = [1 0; s1*exp(im*Θ(z)) 1]
    Ui(z)    = [1 s3*exp(-im*Θ(z))/(1-s1*s3); 0 1]
    U(z)     = [1 -s3*exp(-im*Θ(z))/(1-s1*s3); 0 1]
    S3(z)    = [1 0; s3*exp(im*Θ(z)) 1]
    S4(z)    = [1 -s1*exp(-im*Θ(z)); 0 1]
    L(z)     = [1 0; s1*exp(im*Θ(z))/(1-s1*s3) 1]
    Li(z)    = [1 0; -s1*exp(im*Θ(z))/(1-s1*s3) 1]
    S6(z)    = [1 -s3*exp(-im*Θ(z)); 0 1]

    G = Fun( z -> if z in component(Γ, 1)    D(z)
              elseif z in component(Γ, 2)    S1(z)
              elseif z in component(Γ, 3)    Ui(z)
              elseif z in component(Γ, 4)    U(z)
              elseif z in component(Γ, 5)    S3(z)
              elseif z in component(Γ, 6)    S4(z)
              elseif z in component(Γ, 7)    L(z)
              elseif z in component(Γ, 8)    Li(z)
              elseif z in component(Γ, 9)    S6(z)
              end, Γ);

    γ = components(Γ)
    fpstieltjesmatrix!(Array{ComplexF64}(undef,n,n),Legendre(γ[2]),γ[1])
    sp = Legendre(γ[2])
    z = Directed{false}(orientedrightendpoint(domain(sp)))
    z = mobius(sp,z)
    z̃ = Directed{false}(RiemannDual(1,-0.8))
    @test stieltjesmoment(sp,0,z̃) == stieltjesmoment(sp,0,z)
    n = 50
    for k=1:length(γ), j=1:length(γ)
        fpstieltjesmatrix!(Array{ComplexF64}(undef,n,n),Legendre(γ[k]),γ[j])
    end

    Φ = transpose(rhsolve(transpose(G), n));
end

@test_broken pl2def_no_s2_neg_x1((-im,0,im),-5.0) ≈ painleve2_6ray((-im,0,im),-5.0) ≈ 1.5794870908867027

a = 0.122
x = Fun()
f = Fun(x -> (1-x)^a * exp(x), JacobiWeight(0,a,Jacobi(0,a)))
@test cauchy(f, 1+im) ≈ sum(f/(x-(1+im)))/(2π*im)

z = RiemannDual(1,1)
x = Fun()
f = exp(x)
cauchy(f, z)
x1 = Fun(-1..0)
x2 = Fun(0..1)
f1 = exp(x1)
f2 = exp(x2)

cauchy(f,0.0⁻)
cauchy(f,0.0-eps()im)

cauchy(f,1+im)
cauchy(f1,1+im)+cauchy(f2,1+im)

cauchy(f1,0.0⁻)+cauchy(f2,0.0⁻)
z = RiemannDual(0,-im)
cauchy(f1,z)+cauchy(f2,z)

a = 0.122
f = Fun(x -> (1-x)^a * exp(x), JacobiWeight(0,a,Jacobi(0,a)))
cauchy(f,RiemannDual(-1,-1))

cauchy(f,RiemannDual(1,1))

@testset "x3 in terms of x2" begin
    s1,s2,s3 = 2im,0,-2im
    n = 450
    x = -7.0
    l = 0.7
    @assert mod(n,9) == 0
    @assert abs(s1 - s2 + s3 + s1*s2*s3) ≤ 100eps()
    @assert s2 == 0
    @assert s1*s3 != 1
    @assert x < 0
    @assert l*cos(π/4) < 1/2

    ΓD  = Segment(-0.5, 0.5)
    Γ1  = Segment(0.5, 0.5 + l*exp(im*π/4))
    ΓUi = Segment(0.5, 0.5 + l*exp(im*3π/4))
    ΓU  = Segment(-0.5, -0.5 + l*exp(im*π/4))
    Γ3  = Segment(-0.5, -0.5 + l*exp(im*3π/4))
    Γ4  = Segment(-0.5, -0.5 + l*exp(-im*3π/4))
    ΓL  = Segment(-0.5, -0.5 + l*exp(-im*π/4))
    ΓLi = Segment(0.5, 0.5 + l*exp(-im*3π/4))
    Γ6  = Segment(0.5, 0.5 + l*exp(-im*π/4))
    Γ = ΓD ∪ Γ1 ∪ ΓUi ∪ ΓU ∪ Γ3 ∪ Γ4 ∪ ΓL ∪ ΓLi ∪ Γ6

    Θ(z) = (2*z/3)*(sqrt(-x)^3)*(4*z^2-3)

    D(z)     = [1-s1*s3 0; 0 1/(1-s1*s3)]
    S1(z)    = [1 0; s1*exp(im*Θ(z)) 1]
    Ui(z)    = [1 s3*exp(-im*Θ(z))/(1-s1*s3); 0 1]
    U(z)     = [1 -s3*exp(-im*Θ(z))/(1-s1*s3); 0 1]
    S3(z)    = [1 0; s3*exp(im*Θ(z)) 1]
    S4(z)    = [1 -s1*exp(-im*Θ(z)); 0 1]
    L(z)     = [1 0; s1*exp(im*Θ(z))/(1-s1*s3) 1]
    Li(z)    = [1 0; -s1*exp(im*Θ(z))/(1-s1*s3) 1]
    S6(z)    = [1 -s3*exp(-im*Θ(z)); 0 1]

    G = Fun( z -> if z in component(Γ, 1)    D(z)
              elseif z in component(Γ, 2)    S1(z)
              elseif z in component(Γ, 3)    Ui(z)
              elseif z in component(Γ, 4)    U(z)
              elseif z in component(Γ, 5)    S3(z)
              elseif z in component(Γ, 6)    S4(z)
              elseif z in component(Γ, 7)    L(z)
              elseif z in component(Γ, 8)    Li(z)
              elseif z in component(Γ, 9)    S6(z)
              end, Γ);

    Φ = transpose(rhsolve(transpose(G), n))

    R = 0.1; l = 0.6
    Γ1  = Segment(0.5 + R*exp(im*π/4), 0.5 + R*exp(im*π/4) + l*exp(im*π/4))
    ΓUi = Segment(0.5 + R*exp(im*3π/4), 0.5 + R*exp(im*3π/4) + l*exp(im*3π/4))
    ΓU  = Segment(-0.5 + R*exp(im*π/4), -0.5 + R*exp(im*π/4) + l*exp(im*π/4))
    Γ3  = Segment(-0.5 + R*exp(im*3π/4), -0.5 + R*exp(im*3π/4) + l*exp(im*3π/4))
    Γ4  = Segment(-0.5 + R*exp(-im*3π/4), -0.5 + R*exp(-im*3π/4) + l*exp(-im*3π/4))
    ΓL  = Segment(-0.5 + R*exp(-im*π/4), -0.5 + R*exp(-im*π/4) + l*exp(-im*π/4))
    ΓLi = Segment(0.5 + R*exp(-im*3π/4), 0.5 + R*exp(-im*3π/4) + l*exp(-im*3π/4))
    Γ6  = Segment(0.5 + R*exp(-im*π/4), 0.5 + R*exp(-im*π/4) + l*exp(-im*π/4))

    C11 = Segment(0.5 + R*exp(im*π/4), 0.5 + R*exp(-im*π/4))
    C12 = Segment(0.5 + R*exp(-im*π/4), 0.5 + R*exp(-im*3π/4))
    C13 = Segment(0.5 + R*exp(-im*3π/4),0.5 + R*exp(im*3π/4))
    C15 = Segment(0.5 + R*exp(3im*π/4), 0.5 + R*exp(im*π/4))

    C21 = Segment(-0.5 + R*exp(im*-3π/4), -0.5 + R*exp(im*-5π/4))
    C22 = Segment(-0.5 + R*exp(im*3π/4), -0.5 + R*exp(im*π/4))
    C23 = Segment(-0.5 + R*exp(im*π/4), -0.5 + R*exp(-im*π/4))
    C25 = Segment(-0.5 + R*exp(-im*π/4), -0.5 + R*exp(-im*3π/4))

    Γ = Γ1 ∪ ΓUi ∪ ΓU ∪ Γ3 ∪ Γ4 ∪ ΓL ∪ ΓLi ∪ Γ6 ∪
        C11 ∪ C12 ∪ C13 ∪ C15 ∪ C21 ∪ C22 ∪ C23 ∪ C25

    Θ(z) = (2*z/3)*(sqrt(-x)^3)*(4*z^2-3)
    f(z) = ((1+2z)/(2z-1))^(im/(2*π))

    D(z)     = [1-s1*s3 0; 0 1/(1-s1*s3)]
    S1(z)    = [1 0; s1*exp(im*Θ(z)) 1]
    Ui(z)    = [1 s3*exp(-im*Θ(z))/(1-s1*s3); 0 1]
    U(z)     = [1 -s3*exp(-im*Θ(z))/(1-s1*s3); 0 1]
    S3(z)    = [1 0; s3*exp(im*Θ(z)) 1]
    S3i(z)    = [1 0; -s3*exp(im*Θ(z)) 1]
    S4(z)    = [1 -s1*exp(-im*Θ(z)); 0 1]
    S4i(z)    = [1 s1*exp(-im*Θ(z)); 0 1]
    L(z)     = [1 0; s1*exp(im*Θ(z))/(1-s1*s3) 1]
    Li(z)    = [1 0; -s1*exp(im*Θ(z))/(1-s1*s3) 1]
    S6(z)    = [1 -s3*exp(-im*Θ(z)); 0 1]

    P(z)     = [f(z)^(log(1-s1*s3)) 0; 0 f(z)^(log(1/(1-s1*s3)))]
    Pi(z)    = [f(z)^(log(1/(1-s1*s3))) 0; 0 f(z)^(log(1-s1*s3))]

    G = Fun( z -> if z in component(Γ, 1)     P(z)S1(z)Pi(z)
              elseif z in component(Γ, 2)     P(z)Ui(z)Pi(z)
              elseif z in component(Γ, 3)     P(z)U(z)Pi(z)
              elseif z in component(Γ, 4)     P(z)S3(z)Pi(z)
              elseif z in component(Γ, 5)     P(z)S4(z)Pi(z)
              elseif z in component(Γ, 6)     P(z)L(z)Pi(z)
              elseif z in component(Γ, 7)     P(z)Li(z)Pi(z)
              elseif z in component(Γ, 8)     P(z)S6(z)Pi(z)

              elseif z in component(Γ, 9)     Li(z)S6(z)Pi(z)
              elseif z in component(Γ, 10)    Li(z)Pi(z)
              elseif z in component(Γ, 11) && imag(z) ≤ 0    Pi(z)
              elseif z in component(Γ, 11) && imag(z) > 0    D(z)Pi(z)
              elseif z in component(Γ, 12)    Li(z)S6(z)S1(z)Pi(z)

              elseif z in component(Γ, 13)    Li(z)S4i(z)Pi(z)
              elseif z in component(Γ, 14)    Li(z)S4i(z)S3i(z)Pi(z)
              elseif z in component(Γ, 15) && imag(z) > 0    D(z)Pi(z)
              elseif z in component(Γ, 15) && imag(z) ≤ 0    Pi(z)
              elseif z in component(Γ, 16)    Li(z)Pi(z)
              else error("not defined at $z")
              end, Γ);

    n = ncomponents(Γ) * 100
    Φ = transpose(rhsolve(transpose(G), n))
    z = Fun(ℂ)
    2(z*Φ[1,2])(Inf)*sqrt(-x)
end


text:
using WebAssembly, IRTools.All, Test
using WebAssembly: irfunc, f64, i64, i32

add = let
  ir = IR()
  a = argument!(ir, f64)
  b = argument!(ir, f64)
  c = push!(ir, stmt(xcall(f64.add, a, b), type = f64))
  return!(ir, c)
end

func = irfunc(:add, copy(add))

@test WebAssembly.interpretwasm(func, Dict(), [2, 3]) == [5.0]

relu = let
  ir = IR()
  cond = block(ir, 1)
  els = block!(ir)
  x = argument!(ir, f64)
  ltz = push!(cond, stmt(xcall(f64.gt, x, 0.0), type = i32))
  branch!(cond, els, unless = ltz)
  return!(cond, x)
  return!(els, 0.0)
  ir
end

func = irfunc(:relu, copy(relu))

@test WebAssembly.interpretwasm(func, Dict(), [5]) == [5]
@test WebAssembly.interpretwasm(func, Dict(), [-5]) == [0]

pow = let
  ir = IR()
  x = argument!(ir, f64)
  n = argument!(ir, i32)
  cond = block!(ir)
  body = block!(ir)
  ret  = block!(ir)
  n = argument!(cond, n)
  r = argument!(cond, 1.0)
  branch!(cond, ret, unless = push!(cond, stmt(xcall(i32.gt_s, n, Int32(0)), type = i32)))
  n = push!(body, stmt(xcall(i32.sub, n, Int32(1)), type = i32))
  r′ = push!(body, stmt(xcall(f64.mul, r, x), type = f64))
  branch!(body, cond, n, r′)
  return!(ret, r)
  ir
end

func = irfunc(:pow, copy(pow))
@test WebAssembly.interpretwasm(func, Dict(), [2, 3]) == [8]


text:
struct Ray
    origin::AbstractVec
    direction::AbstractVec
end

function point_at(ray::Ray, t::AbstractFloat)::AbstractVec
    ray.origin + t * ray.direction
end

max_stars_repo_name: goooxu/JuliaRayTracing

max_issues_repo_name: goooxu/JuliaRayTracing

max_forks_repo_name: goooxu/JuliaRayTracing


text:
function dfs(graph,v)
    n = nv(graph)
    visited = zeros(Bool, n)
    list = Int64[]
    cycleclosures = Vector{Int64}[]
    _dfs(graph,v,0,list,cycleclosures,visited)

    cycleclosures = cycleclosures[sortperm(sort.(cycleclosures))[1:2:length(cycleclosures)]] # removes double entries of cycle connections and keeps the first found pair

    return list, cycleclosures
end

function _dfs(graph,v,p,list,cycleclosures,visited)
    if !visited[v]
        visited[v] = true
        for node in all_neighbors(graph,v)
            if node != p && visited[node]
                push!(cycleclosures,[v;node])
            end
            _dfs(graph,node,v,list,cycleclosures,visited)
        end
        push!(list,v)
    end

    return
end

function split_adjacency(A)
    graphs = SimpleGraph{Int64}[]

    subinds = connected_components(Graph(A))
    for subset in subinds
        subA = zeros(Int64,size(A)...)
        subA[subset,subset] = A[subset,subset]
        push!(graphs, Graph(subA))
    end

    roots = [subinds[i][1] for i=1:length(subinds)]

    return graphs, roots
end

function cycle_parent_children(cyclic_members, parents)
    parent = -1
    for member in cyclic_members
        !isempty(parents[member]) && parents[member][1] ∈ cyclic_members && continue
        parent = member
        break
    end

    return parent, setdiff(cyclic_members, parent)
end

function lump_cycles!(cycles, cyclic_children)
    inds = Int64[]
    for (i,cycle) in enumerate(cycles)
        if cyclic_children ⊆ cycle
            return
        elseif cyclic_children ⊇ cycle
            append!(inds,i)
        end
    end
    push!(cycles, cyclic_children)
    deleteat!(cycles, inds)
    return
end


text:
module CFITSIO

using CFITSIO_jll

export FITSFile,
    FITSMemoryHandle,
    fits_assert_open,
    fits_clobber_file,
    fits_close_file,
    fits_copy_image_section,
    fits_create_ascii_tbl,
    fits_create_binary_tbl,
    fits_create_file,
    fits_create_img,
    fits_delete_file,
    fits_delete_key,
    fits_delete_record,
    fits_delete_rows,
    fits_file_mode,
    fits_file_name,
    fits_get_hdrspace,
    fits_get_hdu_num,
    fits_get_hdu_type,
    fits_get_img_dim,
    fits_get_img_equivtype,
    fits_get_img_size,
    fits_get_img_type,
    fits_get_num_cols,
    fits_get_num_hdus,
    fits_get_num_rows,
    fits_get_rowsize,
    fits_get_colnum,
    fits_get_coltype,
    fits_get_eqcoltype,
    fits_get_version,
    fits_read_tdim,
    fits_hdr2str,
    fits_insert_rows,
    fits_movabs_hdu,
    fits_movrel_hdu,
    fits_movnam_hdu,
    fits_open_data,
    fits_open_file,
    fits_open_image,
    fits_open_table,
    fits_open_memfile,
    fits_read_col,
    fits_read_descript,
    fits_read_keyn,
    fits_read_key_str,
    fits_read_key_lng,
    fits_read_keys_lng,
    fits_read_keyword,
    fits_read_pix,
    fits_read_record,
    fits_read_subset,
    fits_update_key,
    fits_write_col,
    fits_write_date,
    fits_write_comment,
    fits_write_history,
    fits_write_key,
    fits_write_pix,
    fits_write_record,
    fits_write_tdim,
    libcfitsio_version,
    cfitsio_typecode,
    bitpix_from_type,
    type_from_bitpix

"""
    cfitsio_typecode(::Type) -> Cint

Return the CFITSIO type code for the given Julia type
"""
cfitsio_typecode

"""
    bitpix_from_type(::Type) -> Cint

Return the FITS BITPIX code for the given Julia type
"""
bitpix_from_type

"""
    type_from_bitpix(::Integer) -> Type

Return the Julia type from the FITS BITPIX code
"""
type_from_bitpix

for (T, code) in (
    (UInt8, 11),
    (Int8, 12),
    (Bool, 14),
    (String, 16),
    (Cushort, 20),
    (Cshort, 21),
    (Cuint, 30),
    (Cint, 31),
    (Int64, 81),
    (Float32, 42),
    (Float64, 82),
    (ComplexF32, 83),
    (ComplexF64, 163),
)
    @eval cfitsio_typecode(::Type{$T}) = Cint($code)
end

for (T, code) in ((UInt8,     8), # BYTE_IMG
                  (Int16,    16), # SHORT_IMG
                  (Int32,    32), # LONG_IMG
                  (Int64,    64), # LONGLONG_IMG
                  (Float32, -32), # FLOAT_IMG
                  (Float64, -64), # DOUBLE_IMG
                  (Int8,     10), # SBYTE_IMG
                  (UInt16,   20), # USHORT_IMG
                  (UInt32,   40)) # ULONG_IMG
    local value = Cint(code)
    @eval begin
        bitpix_from_type(::Type{$T}) = $value
        type_from_bitpix(::Val{$value}) = $T
    end
end
type_from_bitpix(code::Integer) = type_from_bitpix(Val(Cint(code)))

# Above, we don't define a method for Clong because it is either Cint (Int32)
# or Int64 depending on the platform, and those methods are already defined.
# Culong is either UInt64 or Cuint depending on platform. Only define it if
# not already defined.
if Culong !== Cuint
    cfitsio_typecode(::Type{Culong}) = Cint(40)
end

# -----------------------------------------------------------------------------
# FITSFile type

mutable struct FITSFile
    ptr::Ptr{Cvoid}

    FITSFile(ptr::Ptr{Cvoid}) = finalizer(fits_close_file, new(ptr))
end

# FITS wants to be able to update the ptr, so keep them
# in a mutable struct
mutable struct FITSMemoryHandle
    ptr::Ptr{Cvoid}
    size::Csize_t
end
FITSMemoryHandle() = FITSMemoryHandle(C_NULL, 0)

# -----------------------------------------------------------------------------
# error messaging

function fits_assert_open(f::FITSFile)
    if f.ptr == C_NULL
        error("attempt to access closed FITS file")
    end
end

function fits_get_errstatus(status::Cint)
    msg = Vector{UInt8}(undef, 31)
    ccall((:ffgerr, libcfitsio), Cvoid, (Cint, Ptr{UInt8}), status, msg)
    unsafe_string(pointer(msg))
end

function fits_assert_ok(status::Cint, filename = nothing)
    if status != 0
        prefix = filename == nothing ? "" : "While processing file `$filename`: "
        error(string(prefix, fits_get_errstatus(status)))
    end
end

fits_assert_isascii(str::String) =
    !isascii(str) && error("FITS file format accepts ASCII strings only")

fits_get_version() = ccall((:ffvers, libcfitsio), Cfloat, (Ref{Cfloat},), 0.0)

# -----------------------------------------------------------------------------
# file access & info functions

"""
    fits_create_file(filename::AbstractString)

Create and open a new empty output `FITSFile`.
"""
function fits_create_file(filename::AbstractString)
    ptr = Ref{Ptr{Cvoid}}()
    status = Ref{Cint}(0)
    ccall(
        (:ffinit, libcfitsio),
        Cint,
        (Ref{Ptr{Cvoid}}, Ptr{UInt8}, Ref{Cint}),
        ptr,
        filename,
        status,
    )
    fits_assert_ok(status[], filename)
    FITSFile(ptr[])
end

"""
    fits_clobber_file(filename::AbstractString)

Like [`fits_create_file`](@ref), but overwrites `filename` if it exists.
"""
fits_clobber_file(filename::AbstractString) = fits_create_file("!" * filename)

"""
    fits_open_data(filename::String)

Open an existing data file (like [`fits_open_file`](@ref)) and move to the first HDU
containing either an image or a table.
"""
fits_open_data

"""
    fits_open_file(filename::String)

Open an existing data file.
"""
fits_open_file

"""
    fits_open_image(filename::String)

Open an existing data file (like [`fits_open_file`](@ref)) and move to the first
HDU containing an image.
"""
fits_open_image

"""
    fits_open_table(filename::String)

Open an existing data file (like [`fits_open_file`](@ref)) and move to the first
HDU containing either an ASCII or a binary table.
"""
fits_open_table

for (a, b) in (
    (:fits_open_data, "ffdopn"),
    (:fits_open_file, "ffopen"),
    (:fits_open_image, "ffiopn"),
    (:fits_open_table, "fftopn"),
)
    @eval begin
        function ($a)(filename::AbstractString, mode::Integer = 0)
            ptr = Ref{Ptr{Cvoid}}()
            status = Ref{Cint}(0)
            ccall(
                ($b, libcfitsio),
                Cint,
                (Ref{Ptr{Cvoid}}, Ptr{UInt8}, Cint, Ref{Cint}),
                ptr,
                filename,
                mode,
                status,
            )
            fits_assert_ok(status[], filename)
            FITSFile(ptr[])
        end
    end
end

# filename is ignored by the C library
function fits_open_memfile(data::Vector{UInt8}, mode::Integer = 0, filename = "")
    # Only reading is supported right now
    @assert mode == 0
    ptr = Ref{Ptr{Cvoid}}(C_NULL)
    status = Ref{Cint}(0)
    handle = FITSMemoryHandle(pointer(data), length(data))
    dataptr = Ptr{Ptr{Cvoid}}(pointer_from_objref(handle))
    sizeptr = Ptr{Csize_t}(dataptr + sizeof(Ptr{Cvoid}))
    ccall(
        ("ffomem", libcfitsio),
        Cint,
        (
            Ptr{Ptr{Cvoid}},
            Ptr{UInt8},
            Cint,
            Ptr{Ptr{UInt8}},
            Ptr{Csize_t},
            Csize_t,
            Ptr{Cvoid},
            Ptr{Cint},
        ),
        ptr,
        filename,
        mode,
        dataptr,
        sizeptr,
        2880,
        C_NULL,
        status,
    )
    fits_assert_ok(status[])
    FITSFile(ptr[]), handle
end

"""
    fits_close_file(f::FITSFile)

Close a previously opened FITS file.
"""
fits_close_file

"""
    fits_delete_file(f::FITSFile)

Close an opened FITS file (like [`fits_close_file`](@ref)) and removes it
from the disk.
"""
fits_delete_file

for (a, b) in ((:fits_close_file, "ffclos"), (:fits_delete_file, "ffdelt"))
    @eval begin
        function ($a)(f::FITSFile)

            # fits_close_file() is called during garbage collection, but file
            # may already be closed by user, so we need to check if it is open.
            if (ptr = f.ptr) != C_NULL
                f.ptr = C_NULL # avoid closing twice even if an error occurs
                status = Ref{Cint}(0)
                ccall(($b, libcfitsio), Cint, (Ptr{Cvoid}, Ref{Cint}), ptr, status)
                fits_assert_ok(status[])
            end
        end
    end
end

Base.close(f::FITSFile) = fits_close_file(f)

"""
    fits_file_name(f::FITSFile)

Return the name of the file associated with object `f`.
"""
function fits_file_name(f::FITSFile)
    value = Vector{UInt8}(undef, 1025)
    status = Ref{Cint}(0)
    ccall(
        (:ffflnm, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        value,
        status,
    )
    fits_assert_ok(status[])
    unsafe_string(pointer(value))
end

function fits_file_mode(f::FITSFile)
    result = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        ("ffflmd", libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        result,
        status,
    )
    fits_assert_ok(status[])
    result[]
end

# -----------------------------------------------------------------------------
# header access functions

"""
    fits_get_hdrspace(f::FITSFile) -> (keysexist, morekeys)

Return the number of existing keywords (not counting the END keyword)
and the amount of space currently available for more keywords.
"""
function fits_get_hdrspace(f::FITSFile)
    keysexist = Ref{Cint}(0)
    morekeys = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffghsp, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ref{Cint}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        keysexist,
        morekeys,
        status,
    )
    fits_assert_ok(status[])
    (keysexist[], morekeys[])
end

function fits_read_key_str(f::FITSFile, keyname::String)
    value = Vector{UInt8}(undef, 71)
    comment = Vector{UInt8}(undef, 71)
    status = Ref{Cint}(0)
    ccall(
        (:ffgkys, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        keyname,
        value,
        comment,
        status,
    )
    fits_assert_ok(status[])
    unsafe_string(pointer(value)), unsafe_string(pointer(comment))
end

function fits_read_key_lng(f::FITSFile, keyname::String)
    value = Ref{Clong}(0)
    comment = Vector{UInt8}(undef, 71)
    status = Ref{Cint}(0)
    ccall(
        (:ffgkyj, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ref{Clong}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        keyname,
        value,
        comment,
        status,
    )
    fits_assert_ok(status[])
    value[], unsafe_string(pointer(comment))
end

function fits_read_keys_lng(f::FITSFile, keyname::String, nstart::Integer, nmax::Integer)
    value = Vector{Clong}(undef, nmax - nstart + 1)
    nfound = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffgknj, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Cint, Cint, Ptr{Clong}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        keyname,
        nstart,
        nmax,
        value,
        nfound,
        status,
    )
    fits_assert_ok(status[])
    value, nfound[]
end

"""
    fits_read_keyword(f::FITSFile, keyname::String) -> (value, comment)

yields the specified keyword value and commend (as a tuple of strings),
throws and error if the keyword is not found.

"""
function fits_read_keyword(f::FITSFile, keyname::String)
    value = Vector{UInt8}(undef, 71)
    comment = Vector{UInt8}(undef, 71)
    status = Ref{Cint}(0)
    ccall(
        (:ffgkey, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        keyname,
        value,
        comment,
        status,
    )
    fits_assert_ok(status[])
    unsafe_string(pointer(value)), unsafe_string(pointer(comment))
end

"""
    fits_read_record(f::FITSFile, keynum::Int) -> String

Return the nth header record in the CHU. The first keyword in the
header is at `keynum = 1`.
"""
function fits_read_record(f::FITSFile, keynum::Integer)
    card = Vector{UInt8}(undef, 81)
    status = Ref{Cint}(0)
    ccall(
        (:ffgrec, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        keynum,
        card,
        status,
    )
    fits_assert_ok(status[])
    unsafe_string(pointer(card))
end

"""
    fits_read_keyn(f::FITSFile, keynum::Int) -> (name, value, comment)

Return the nth header record in the CHU. The first keyword in the header is at `keynum = 1`.
"""
function fits_read_keyn(f::FITSFile, keynum::Integer)
    keyname = Vector{UInt8}(undef, 9)
    value = Vector{UInt8}(undef, 71)
    comment = Vector{UInt8}(undef, 71)
    status = Ref{Cint}(0)
    ccall(
        (:ffgkyn, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        keynum,
        keyname,
        value,
        comment,
        status,
    )
    fits_assert_ok(status[])
    (
        unsafe_string(pointer(keyname)),
        unsafe_string(pointer(value)),
        unsafe_string(pointer(comment)),
    )
end

"""
    fits_write_key(f::FITSFile, keyname::String, value, comment::String)

Write a keyword of the appropriate data type into the CHU.
"""
function fits_write_key(
    f::FITSFile,
    keyname::String,
    value::Union{Real,String},
    comment::String,
)
    fits_assert_isascii(keyname)
    fits_assert_isascii(comment)
    cvalue = isa(value, String) ? value :
        isa(value, Bool) ? Cint[value] : reinterpret(UInt8, [value])
    status = Ref{Cint}(0)
    ccall(
        (:ffpky, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        cfitsio_typecode(typeof(value)),
        keyname,
        cvalue,
        comment,
        status,
    )
    fits_assert_ok(status[])
end

function fits_write_date(f::FITSFile)
    status = Ref{Cint}(0)
    ccall((:ffpdat, libcfitsio), Cint, (Ptr{Cvoid}, Ref{Cint}), f.ptr, status)
    fits_assert_ok(status[])
end

function fits_write_comment(f::FITSFile, comment::String)
    fits_assert_isascii(comment)
    status = Ref{Cint}(0)
    ccall(
        (:ffpcom, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        comment,
        status,
    )
    fits_assert_ok(status[])
end

function fits_write_history(f::FITSFile, history::String)
    fits_assert_isascii(history)
    status = Ref{Cint}(0)
    ccall(
        (:ffphis, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        history,
        status,
    )
    fits_assert_ok(status[])
end

# update key: if already present, update it, otherwise add it.
for (a, T, S) in (
    ("ffukys", :String, :(Ptr{UInt8})),
    ("ffukyl", :Bool, :Cint),
    ("ffukyj", :Integer, :Int64),
)
    @eval begin
        function fits_update_key(
            f::FITSFile,
            key::String,
            value::$T,
            comment::Union{String,Ptr{Cvoid}} = C_NULL,
        )
            isa(value, String) && fits_assert_isascii(value)
            isa(comment, String) && fits_assert_isascii(comment)
            status = Ref{Cint}(0)
            ccall(
                ($a, libcfitsio),
                Cint,
                (Ptr{Cvoid}, Ptr{UInt8}, $S, Ptr{UInt8}, Ref{Cint}),
                f.ptr,
                key,
                value,
                comment,
                status,
            )
            fits_assert_ok(status[])
        end
    end
end

function fits_update_key(
    f::FITSFile,
    key::String,
    value::AbstractFloat,
    comment::Union{String,Ptr{Cvoid}} = C_NULL,
)
    isa(comment, String) && fits_assert_isascii(comment)
    status = Ref{Cint}(0)
    ccall(
        ("ffukyd", libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Cdouble, Cint, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        key,
        value,
        -15,
        comment,
        status,
    )
    fits_assert_ok(status[])
end

function fits_update_key(
    f::FITSFile,
    key::String,
    value::Nothing,
    comment::Union{String,Ptr{Cvoid}} = C_NULL,
)
    isa(comment, String) && fits_assert_isascii(comment)
    status = Ref{Cint}(0)
    ccall(
        ("ffukyu", libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        key,
        comment,
        status,
    )
    fits_assert_ok(status[])
end

"""
    fits_write_record(f::FITSFile, card::String)

Write a user specified keyword record into the CHU.
"""
function fits_write_record(f::FITSFile, card::String)
    fits_assert_isascii(card)
    status = Ref{Cint}(0)
    ccall(
        (:ffprec, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        card,
        status,
    )
    fits_assert_ok(status[])
end

"""
    fits_delete_record(f::FITSFile, keynum::Int)

Delete the keyword record at the specified index.
"""
function fits_delete_record(f::FITSFile, keynum::Integer)
    status = Ref{Cint}(0)
    ccall((:ffdrec, libcfitsio), Cint, (Ptr{Cvoid}, Cint, Ref{Cint}), f.ptr, keynum, status)
    fits_assert_ok(status[])
end

"""
    fits_delete_key(f::FITSFile, keyname::String)

Delete the keyword named `keyname`.
"""
function fits_delete_key(f::FITSFile, keyname::String)
    status = Ref{Cint}(0)
    ccall(
        (:ffdkey, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{UInt8}, Ref{Cint}),
        f.ptr,
        keyname,
        status,
    )
    fits_assert_ok(status[])
end

"""
    fits_hdr2str(f::FITSFile, nocomments::Bool=false)

Return the header of the CHDU as a string. If `nocomments` is `true`, comment
cards are stripped from the output.
"""
function fits_hdr2str(f::FITSFile, nocomments::Bool = false)
    status = Ref{Cint}(0)
    header = Ref{Ptr{UInt8}}()
    nkeys = Ref{Cint}(0)
    ccall(
        (:ffhdr2str, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ref{Ptr{UInt8}}, Cint, Ptr{Ptr{UInt8}}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        nocomments,
        C_NULL,
        0,
        header,
        nkeys,
        status,
    )
    result = unsafe_string(header[])

    # free header pointer allocated by cfitsio (result is a copy)
    ccall((:fffree, libcfitsio), Ref{Cint}, (Ptr{UInt8}, Ref{Cint}), header[], status)
    fits_assert_ok(status[])
    result
end

# -----------------------------------------------------------------------------
# HDU info functions and moving the current HDU

function hdu_int_to_type(hdu_type_int)
    if hdu_type_int == 0
        return :image_hdu
    elseif hdu_type_int == 1
        return :ascii_table
    elseif hdu_type_int == 2
        return :binary_table
    end

    :unknown
end

"""
    fits_movabs_hdu(f::FITSFile, hduNum::Integer)

Change the current HDU to the value specified by `hduNum`, and return a symbol
describing the type of the HDU.

Possible symbols are: `image_hdu`, `ascii_table`, or `binary_table`.
The value of `hduNum` must range between 1 and the value returned by
[`fits_get_num_hdus`](@ref).
"""
fits_movabs_hdu

"""
    fits_movrel_hdu(f::FITSFile, hduNum::Integer)

Change the current HDU by moving forward or backward by `hduNum` HDUs
(positive means forward), and return the same as [`fits_movabs_hdu`](@ref).
"""
fits_movrel_hdu
for (a, b) in ((:fits_movabs_hdu, "ffmahd"), (:fits_movrel_hdu, "ffmrhd"))
    @eval begin
        function ($a)(f::FITSFile, hduNum::Integer)
            hdu_type = Ref{Cint}(0)
            status = Ref{Cint}(0)
            ccall(
                ($b, libcfitsio),
                Cint,
                (Ptr{Cvoid}, Cint, Ref{Cint}, Ref{Cint}),
                f.ptr,
                hduNum,
                hdu_type,
                status,
            )
            fits_assert_ok(status[])
            hdu_int_to_type(hdu_type[])
        end
    end
end

"""
    fits_movnam_hdu(f::FITSFile, extname::String, extver::Integer=0,
          hdu_type_int::Integer=-1)

Change the current HDU by moving to the (first) HDU which has the specified
extension type and EXTNAME and EXTVER keyword values (or HDUNAME and HDUVER keywords).

If `extver` is 0 (the default) then the EXTVER keyword is ignored and the first HDU
with a matching EXTNAME (or HDUNAME) keyword will be found. If `hdu_type_int`
is -1 (the default) only the extname and extver values will be used to locate the
correct extension. If no matching HDU is found in the file, the current HDU will
remain unchanged.
"""
function fits_movnam_hdu(
    f::FITSFile,
    extname::String,
    extver::Integer = 0,
    hdu_type::Integer = -1,
)
    status = Ref{Cint}(0)
    ccall(
        (:ffmnhd, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{UInt8}, Cint, Ref{Cint}),
        f.ptr,
        hdu_type,
        extname,
        extver,
        status,
    )
    fits_assert_ok(status[])
end

function fits_get_hdu_num(f::FITSFile)
    hdunum = Ref{Cint}(0)
    ccall((:ffghdn, libcfitsio), Cint, (Ptr{Cvoid}, Ref{Cint}), f.ptr, hdunum)
    hdunum[]
end

function fits_get_hdu_type(f::FITSFile)
    hdutype = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffghdt, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        hdutype,
        status,
    )
    fits_assert_ok(status[])
    hdu_int_to_type(hdutype[])
end

# -----------------------------------------------------------------------------
# image HDU functions

"""
    fits_get_img_size(f::FITSFile)

Get the dimensions of the image.
"""
fits_get_img_size
for (a, b) in (
    (:fits_get_img_type, "ffgidt"),
    (:fits_get_img_equivtype, "ffgiet"),
    (:fits_get_img_dim, "ffgidm"),
)
    @eval function ($a)(f::FITSFile)
        result = Ref{Cint}(0)
        status = Ref{Cint}(0)
        ccall(
            ($b, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Ref{Cint}, Ref{Cint}),
            f.ptr,
            result,
            status,
        )
        fits_assert_ok(status[])
        result[]
    end
end

"""
    fits_create_img(f::FITSFile, t::Type, naxes::Vector{Int})

Create a new primary array or IMAGE extension with a specified data type and size.
"""
function fits_create_img(f::FITSFile, ::Type{T}, naxes::Vector{S}) where {T,S<:Integer}
    status = Ref{Cint}(0)
    ccall(
        (:ffcrimll, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Cint, Ptr{Int64}, Ref{Cint}),
        f.ptr,
        bitpix_from_type(T),
        length(naxes),
        Vector{Int64}(naxes),
        status,
    )
    fits_assert_ok(status[])
end

"""
    fits_write_pix(f::FITSFile, fpixel::Vector{Int}, nelements::Int, data::StridedArray)

Write pixels from `data` into the FITS file.
The data needs to be stored contiguously in memory.
"""
function fits_write_pix(
    f::FITSFile,
    fpixel::Vector{S},
    nelements::Integer,
    data::StridedArray{T},
) where {S<:Integer,T}

    status = Ref{Cint}(0)
    ccall(
        (:ffppxll, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{Int64}, Int64, Ptr{Cvoid}, Ref{Cint}),
        f.ptr,
        cfitsio_typecode(T),
        Vector{Int64}(fpixel),
        nelements,
        data,
        status,
    )
    fits_assert_ok(status[])
end

function fits_write_pix(f::FITSFile, data::StridedArray)
    fits_write_pix(f, ones(Int64, length(size(data))), length(data), data)
end

function fits_read_pix(
    f::FITSFile,
    fpixel::Vector{S},
    nelements::Int,
    nullval::T,
    data::StridedArray{T},
) where {S<:Integer,T}
    anynull = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffgpxvll, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{Int64}, Int64, Ref{Cvoid}, Ptr{Cvoid}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        cfitsio_typecode(T),
        Vector{Int64}(fpixel),
        nelements,
        nullval,
        data,
        anynull,
        status,
    )
    fits_assert_ok(status[])
    anynull[]
end

"""
    fits_read_pix(f::FITSFile, fpixel::Vector{Int}, nelements::Int, data::Array)

Read pixels from the FITS file into `data`.
"""
function fits_read_pix(
    f::FITSFile,
    fpixel::Vector{S},
    nelements::Int,
    data::StridedArray{T},
) where {S<:Integer,T}
    anynull = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffgpxvll, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{Int64}, Int64, Ptr{Cvoid}, Ptr{Cvoid}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        cfitsio_typecode(T),
        Vector{Int64}(fpixel),
        nelements,
        C_NULL,
        data,
        anynull,
        status,
    )
    fits_assert_ok(status[])
    anynull[]
end

function fits_read_pix(f::FITSFile, data::StridedArray)
    fits_read_pix(f, ones(Int64, length(size(data))), length(data), data)
end

function fits_read_subset(
    f::FITSFile,
    fpixel::Vector{S1},
    lpixel::Vector{S2},
    inc::Vector{S3},
    data::StridedArray{T},
) where {S1<:Integer,S2<:Integer,S3<:Integer,T}

    anynull = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffgsv, libcfitsio),
        Cint,
        (
            Ptr{Cvoid},
            Cint,
            Ptr{Clong},
            Ptr{Clong},
            Ptr{Clong},
            Ptr{Cvoid},
            Ptr{Cvoid},
            Ref{Cint},
            Ref{Cint},
        ),
        f.ptr,
        cfitsio_typecode(T),
        Vector{Clong}(fpixel),
        Vector{Clong}(lpixel),
        Vector{Clong}(inc),
        C_NULL,
        data,
        anynull,
        status,
    )
    fits_assert_ok(status[])
    anynull[]
end

function fits_copy_image_section(fin::FITSFile, fout::FITSFile, section::String)
    status = Ref{Cint}(0)
    ccall(
        (:fits_copy_image_section, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{UInt8}, Ref{Cint}),
        fin.ptr,
        fout.ptr,
        section,
        status,
    )
    fits_assert_ok(status[])
end

# -----------------------------------------------------------------------------
# ASCII/binary table HDU functions

# The three fields are: ttype, tform, tunit (CFITSIO's terminology)
const ColumnDef = Tuple{String,String,String}

"""
    fits_create_binary_tbl(f::FITSFile, numrows::Integer, coldefs::Array{ColumnDef},
                 extname::String)

Append a new HDU containing a binary table. The meaning of the parameters is the same
as in a call to [`fits_create_ascii_tbl`](@ref).

In general, one should pick this function for creating tables in a new HDU,
as binary tables require less space on the disk and are more efficient to read and write.
(Moreover, a few datatypes are not supported in ASCII tables).
"""
fits_create_binary_tbl

"""
    fits_create_ascii_tbl(f::FITSFile, numrows::Integer, coldefs::Array{ColumnDef},
                extname::String)

Append a new HDU containing an ASCII table.

The table will have `numrows` rows (this parameter can be set to zero), each
initialized with the default value. In order to create a table, the programmer
must specify the characteristics of each column. The columns are specified by the
`coldefs` variable, which is an array of tuples.
Each tuple must have three string fields:

1. The name of the column.
2. The data type and the repetition count. It must be a string made by a number
(the repetition count) followed by a letter specifying the type (in the example
above, `D` stands for `Float64`, `E` stands for `Float32`, `A` stands for `Char`).
Refer to the CFITSIO documentation for more information about the syntax of this
parameter.
3. The measure unit of this field. This is used only as a comment.

The value of `extname` sets the "extended name" of the table, i.e., a string
that in some situations can be used to refer to the HDU itself.

Note that, unlike for binary tables, CFITSIO puts some limitations to the
types that can be used in an ASCII table column. Refer to the CFITSIO manual
for further information.

See also [`fits_create_binary_tbl`](@ref) for a similar function which
creates binary tables.
"""
fits_create_ascii_tbl
for (a, b) in ((:fits_create_binary_tbl, 2), (:fits_create_ascii_tbl, 1))
    @eval begin
        function ($a)(
            f::FITSFile,
            numrows::Integer,
            coldefs::Array{ColumnDef},
            extname::String,
        )

            # Ensure that extension name, column names and units are
            # ASCII, as these get written to the file. We don't check
            # need to check that tform is ASCII because presumably
            # cfitsio will thrown an appropriate error if it doesn't
            # recognize the tform string.
            fits_assert_isascii(extname)
            for coldef in coldefs
                fits_assert_isascii(coldef[1])
                fits_assert_isascii(coldef[3])
            end

            # get length and convert coldefs to three arrays of Ptr{Uint8}
            ntype = length(coldefs)
            ttype = [pointer(x[1]) for x in coldefs]
            tform = [pointer(x[2]) for x in coldefs]
            tunit = [pointer(x[3]) for x in coldefs]
            status = Ref{Cint}(0)

            ccall(
                ("ffcrtb", libcfitsio),
                Cint,
                (
                    Ptr{Cvoid},
                    Cint,
                    Int64,
                    Cint,
                    Ptr{Ptr{UInt8}},
                    Ptr{Ptr{UInt8}},
                    Ptr{Ptr{UInt8}},
                    Ptr{UInt8},
                    Ref{Cint},
                ),
                f.ptr,
                $b,
                numrows,
                ntype,
                ttype,
                tform,
                tunit,
                extname,
                status,
            )
            fits_assert_ok(status[])
        end
    end
end

"""
    fits_get_num_hdus(f::FITSFile)

Return the number of HDUs in the file.
"""
fits_get_num_hdus
for (a, b, T) in (
    (:fits_get_num_cols, "ffgncl", :Cint),
    (:fits_get_num_hdus, "ffthdu", :Cint),
    (:fits_get_rowsize, "ffgrsz", :Clong),
)
    @eval begin
        function ($a)(f::FITSFile)
            result = Ref{$T}(0)
            status = Ref{Cint}(0)
            ccall(
                ($b, libcfitsio),
                Cint,
                (Ptr{Cvoid}, Ref{$T}, Ref{Cint}),
                f.ptr,
                result,
                status,
            )
            fits_assert_ok(status[])
            result[]
        end
    end
end

function fits_get_colnum(f::FITSFile, tmplt::String; case_sensitive::Bool = true)
    result = Ref{Cint}(0)
    status = Ref{Cint}(0)

    # Second argument is case-sensitivity of search: 0 = case-insensitive
    #                                                1 = case-sensitive
    ccall(
        ("ffgcno", libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Ptr{UInt8}, Ref{Cint}, Ref{Cint}),
        f.ptr,
        case_sensitive,
        tmplt,
        result,
        status,
    )
    fits_assert_ok(status[])
    return result[]
end

# The following block are all functions that have separate variants for Clong
# and 64-bit integers in cfitsio. Rather than providing both of these, we
# provide only one according to the native integer type on the platform.
if promote_type(Int, Clong) == Clong
    T = Clong
    ffgtdm = "ffgtdm"
    ffgnrw = "ffgnrw"
    ffptdm = "ffptdm"
    ffgtcl = "ffgtcl"
    ffeqty = "ffeqty"
    ffgdes = "ffgdes"
    ffgisz = "ffgisz"
else
    T = Int64
    ffgtdm = "ffgtdmll"
    ffgnrw = "ffgnrwll"
    ffptdm = "ffptdmll"
    ffgtcl = "ffgtclll"
    ffeqty = "ffeqtyll"
    ffgdes = "ffgdesll"
    ffgisz = "ffgiszll"
end

"""
    fits_get_coltype(f::FITSFile, colnum::Integer)

Provided that the current HDU contains either an ASCII or binary table, return
information about the column at position `colnum` (counting from 1).

Return is a tuple containing

- `typecode`: CFITSIO integer type code of the column.
- `repcount`: Repetition count for the column.
- `width`: Width of an individual element.
"""
fits_get_coltype

@eval begin
    function fits_get_coltype(ff::FITSFile, colnum::Integer)
        typecode = Ref{Cint}(0)
        repcnt = Ref{$T}(0)
        width = Ref{$T}(0)
        status = Ref{Cint}(0)
        ccall(
            ($ffgtcl, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Cint, Ref{Cint}, Ref{$T}, Ref{$T}, Ref{Cint}),
            ff.ptr,
            colnum,
            typecode,
            repcnt,
            width,
            status,
        )
        fits_assert_ok(status[])
        return Int(typecode[]), Int(repcnt[]), Int(width[])
    end

    function fits_get_eqcoltype(ff::FITSFile, colnum::Integer)
        typecode = Ref{Cint}(0)
        repcnt = Ref{$T}(0)
        width = Ref{$T}(0)
        status = Ref{Cint}(0)
        ccall(
            ($ffeqty, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Cint, Ref{Cint}, Ref{$T}, Ref{$T}, Ref{Cint}),
            ff.ptr,
            colnum,
            typecode,
            repcnt,
            width,
            status,
        )
        fits_assert_ok(status[])
        return Int(typecode[]), Int(repcnt[]), Int(width[])
    end

    function fits_get_img_size(f::FITSFile)
        ndim = fits_get_img_dim(f)
        naxes = Vector{$T}(undef, ndim)
        status = Ref{Cint}(0)
        ccall(
            ($ffgisz, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Cint, Ptr{$T}, Ref{Cint}),
            f.ptr,
            ndim,
            naxes,
            status,
        )
        fits_assert_ok(status[])
        naxes
    end

    function fits_get_num_rows(f::FITSFile)
        result = Ref{$T}(0)
        status = Ref{Cint}(0)
        ccall(
            ($ffgnrw, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Ref{$T}, Ref{Cint}),
            f.ptr,
            result,
            status,
        )
        fits_assert_ok(status[])
        return Int(result[])
    end

    # `fits_read_tdim` returns the dimensions of a table column in a
    # binary table. Normally this information is given by the TDIMn
    # keyword, but if this keyword is not present then this routine
    # returns `[r]` with `r` equals to the repeat count in the TFORM
    # keyword.
    function fits_read_tdim(ff::FITSFile, colnum::Integer)
        naxes = Vector{$T}(undef, 99)  # 99 is the maximum allowed number of axes
        naxis = Ref{Cint}(0)
        status = Ref{Cint}(0)
        ccall(
            ($ffgtdm, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Cint, Cint, Ref{Cint}, Ptr{$T}, Ref{Cint}),
            ff.ptr,
            colnum,
            length(naxes),
            naxis,
            naxes,
            status,
        )
        fits_assert_ok(status[])
        return naxes[1:naxis[]]
    end

    function fits_write_tdim(ff::FITSFile, colnum::Integer, naxes::Array{$T})
        status = Ref{Cint}(0)
        ccall(
            ($ffptdm, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Cint, Cint, Ptr{$T}, Ref{Cint}),
            ff.ptr,
            colnum,
            length(naxes),
            naxes,
            status,
        )
        fits_assert_ok(status[])
    end

    function fits_read_descript(f::FITSFile, colnum::Integer, rownum::Integer)
        repeat = Ref{$T}(0)
        offset = Ref{$T}(0)
        status = Ref{Cint}(0)
        ccall(
            ($ffgdes, libcfitsio),
            Cint,
            (Ptr{Cvoid}, Cint, Int64, Ref{$T}, Ref{$T}, Ref{Cint}),
            f.ptr,
            colnum,
            rownum,
            repeat,
            offset,
            status,
        )
        fits_assert_ok(status[])
        return Int(repeat[]), Int(offset[])
    end
end

"""
    fits_read_col(f, colnum, firstrow, firstelem, data)

Read data from one column of an ASCII/binary table and convert the data into the
specified type `T`.

### Arguments ###

* `f::FITSFile`: the file to be read.
* `colnum::Integer`: the column number, where the value of the first column is `1`.
* `firstrow::Integer`: the elements to be read start from this row.
* `firstelem::Integer`: specifies which is the first element to be read, when each
cell contains more than one element (i.e., the "repetition count" of the field is
greater than one).
* `data::Array`: at the end of the call, this will be filled with the elements read
from the column. The length of the array gives the overall number of elements.
"""
function fits_read_col(
    f::FITSFile,
    colnum::Integer,
    firstrow::Integer,
    firstelem::Integer,
    data::Array{String},
)

    # get width: number of characters in each string
    typecode, repcount, width = fits_get_eqcoltype(f, colnum)

    # ensure that data are strings, otherwise cfitsio will try to write
    # formatted strings, which have widths given by fits_get_col_display_width
    # not by the repeat value from fits_get_coltype.
    abs(typecode) == 16 || error("not a string column")

    # create an array of character buffers of the correct width
    buffers = [Vector{UInt8}(undef, width) for i in 1:length(data)]

    # Call the CFITSIO function
    anynull = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffgcvs, libcfitsio),
        Cint,
        (
            Ptr{Cvoid},
            Cint,
            Int64,
            Int64,
            Int64,
            Ptr{UInt8},
            Ptr{Ptr{UInt8}},
            Ref{Cint},
            Ref{Cint},
        ),
        f.ptr,
        colnum,
        firstrow,
        firstelem,
        length(data),
        " ",
        buffers,
        anynull,
        status,
    )
    fits_assert_ok(status[])

    # Create strings out of the buffers, terminating at null characters.
    # Note that `String(x)` does not copy the buffer x.
    for i in 1:length(data)
        zeropos = something(findfirst(isequal(0x00), buffers[i]), 0)
        data[i] = (zeropos >= 1) ? String(buffers[i][1:(zeropos-1)]) : String(buffers[i])
    end
end

function fits_read_col(
    f::FITSFile,
    colnum::Integer,
    firstrow::Integer,
    firstelem::Integer,
    data::Array{T},
) where {T}
    anynull = Ref{Cint}(0)
    status = Ref{Cint}(0)
    ccall(
        (:ffgcv, libcfitsio),
        Cint,
        (
            Ptr{Cvoid},
            Cint,
            Cint,
            Int64,
            Int64,
            Int64,
            Ptr{Cvoid},
            Ptr{Cvoid},
            Ref{Cint},
            Ref{Cint},
        ),
        f.ptr,
        cfitsio_typecode(T),
        colnum,
        firstrow,
        firstelem,
        length(data),
        C_NULL,
        data,
        anynull,
        status,
    )
    fits_assert_ok(status[])
end

"""
    fits_write_col(f, colnum, firstrow, firstelem, data)

Write some data in one column of a ASCII/binary table.

If there is no room for the elements, new rows will be created. (It is therefore
useless to call [`fits_insert_rows`](@ref) if you only need to *append* elements
to the end of a table.)

* `f::FITSFile`: the file in which data will be written.
* `colnum::Integer`: the column number, where the value of the first column is `1`.
* `firstrow::Integer`: the data wil be written from this row onwards.
* `firstelem::Integer`: specifies the position in the row where the first element
will be written.
* `data::Array`: contains the elements that are to be written to the column of the table.
"""
function fits_write_col(
    f::FITSFile,
    colnum::Integer,
    firstrow::Integer,
    firstelem::Integer,
    data::Array{String},
)
    for el in data
        fits_assert_isascii(el)
    end
    status = Ref{Cint}(0)
    ccall(
        (:ffpcls, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Int64, Int64, Int64, Ptr{Ptr{UInt8}}, Ref{Cint}),
        f.ptr,
        colnum,
        firstrow,
        firstelem,
        length(data),
        data,
        status,
    )
    fits_assert_ok(status[])
end

function fits_write_col(
    f::FITSFile,
    colnum::Integer,
    firstrow::Integer,
    firstelem::Integer,
    data::Array{T},
) where {T}
    status = Ref{Cint}(0)
    ccall(
        (:ffpcl, libcfitsio),
        Cint,
        (Ptr{Cvoid}, Cint, Cint, Int64, Int64, Int64, Ptr{Cvoid}, Ref{Cint}),
        f.ptr,
        cfitsio_typecode(T),
        colnum,
        firstrow,
        firstelem,
        length(data),
        data,
        status,
    )
    fits_assert_ok(status[])
end

"""
    fits_insert_rows(f::FITSFile, firstrow::Integer, nrows::Integer)

Insert a number of rows equal to `nrows` after the row number `firstrow`.

The elements in each row are initialized to their default value: you can
modify them later using [`fits_write_col`](@ref).

Since the first row is at position 1, in order to insert rows *before*
the first one `firstrow` must be equal to zero.
"""
fits_insert_rows

"""
    fits_delete_rows(f::FITSFile, firstrow::integer, nrows::Integer)

Delete `nrows` rows, starting from the one at position `firstrow`. The index of
the first row is 1.
"""
fits_delete_rows

for (a, b) in ((:fits_insert_rows, "ffirow"), (:fits_delete_rows, "ffdrow"))
    @eval begin
        function ($a)(f::FITSFile, firstrow::Integer, nrows::Integer)
            status = Ref{Cint}(0)
            ccall(
                ($b, libcfitsio),
                Cint,
                (Ptr{Cvoid}, Int64, Int64, Ref{Cint}),
                f.ptr,
                firstrow,
                nrows,
                status,
            )
            fits_assert_ok(status[])
        end
    end
end

"""
    libcfitsio_version() -> VersionNumber

Return the version of the underlying CFITSIO library

# Example

```julia
julia> libcfitsio_version()
v"3.37.0"
```

"""
function libcfitsio_version(version = fits_get_version())
    # fits_get_version returns a float. e.g., 3.341f0. We parse that
    # into a proper version number. E.g., 3.341 -> v"3.34.1"
    v = Int(round(1000 * version))
    x = div(v, 1000)
    y = div(rem(v, 1000), 10)
    z = rem(v, 10)
    VersionNumber(x, y, z)
end

end # module


text:
module FEOperatorsTests

end # module


text:
module SzeneManager

end #SzeneManager

max_stars_repo_name: Gilga/JGE

max_issues_repo_name: Gilga/JGE

max_forks_repo_name: Gilga/JGE


text:
"""
    listbenchmarks( keyword::Symbol = :all, returnlist = false )

Print a list of all registered control benchmarks that have `keyword` in their keyword list, or
simply all registered benchmarks if `:all` is specified.
The set of `ControlBenchmarkInfo` objects containing all the information printed is optionally
returned if `returnlist` is `true`.

# Example
```
julia> listbenchmarks( :ifac )
Benchmarks with keyword ":ifac":
┌────────┬──────────────────────────────┬─────────────────────────────────────────────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────────┐
│ ID     │ Name                         │ Description                                     │ Configurable │ Keywords                                                                            │
├────────┼──────────────────────────────┼─────────────────────────────────────────────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────────┤
│ ifac01 │ Binary distillation column   │ A distillation column to separate two compounds │ No           │ [:continuous, :ifac, :linear, :process, :stable]                                    │
│ ifac02 │ Drum boiler                  │ A drum boiler where water is converted to steam │ No           │ [:continuous, :ifac, :linear, :nonminimum_phase, :process, :unstable]               │
│ ifac06 │ Boeing 767                   │ Boeing 767 at flutter condition                 │ No           │ [:aerospace, :continuous, :ifac, :linear, :nonminimum_phase, :nonnormal, :unstable] │
│ ifac07 │ Hydraulic positioning system │ Hydraulically controlled carriage on a track    │ Yes          │ [:continuous, :ifac, :linear, :mechanical, :stable]                                 │
└────────┴──────────────────────────────┴─────────────────────────────────────────────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────────┘

```
"""
function listbenchmarks( keyword::Symbol = :all, returnlist = false )
    foundbenchmarks = Set{ControlBenchmarkInfo}()

    header = ["ID" "Name" "Description" "Configurable" "Keywords"]
    list   = Array{Any, 2}(undef, 0, 5)

    for (T, b) in benchmarkinfo
        if keyword in b.keywords || keyword == :all
            push!( foundbenchmarks, b )
            config = b.configurable ? "Yes" : "No"
            keyw   = sort(collect(b.keywords))
            list = vcat( list, ["$(b.id)" "$(b.name)" "$(b.desc)" "$(config)" "$(keyw)"] )
        end
    end

    if isempty( foundbenchmarks )
        println( "No benchmarks found with keyword \":$keyword\"")
    else
        list = sortslices( list; dims=1 )

        if keyword == :all
            println( "All registered benchmarks:" )
        else
            println( "Benchmarks with keyword \":$keyword\":" )
        end

        pretty_table( list, header; alignment=:l )
    end

    if returnlist
        return foundbenchmarks
    else
        return nothing
    end
end

"""
    getbenchmark( id::string )

Get an instance of the problem type given by `id`. This type can then be modified (if the problem
is configurable), and used to create the benchmark problem data by calling [`controlbenchmark`](@ref).

# Example
```jldoctest
julia> prob = getbenchmark( "lin01" )
BallOnPlate()
```
"""
function getbenchmark( id::String )
    if( !haskey( benchmarkmap, id ) )
        error( "$id is not a registered benchmark problem" )
    end

    benchmarkmap[id]()
end

"""
    controlbenchmark( id::String )

Gets the default benchmark problem data for the problem given by `id`.

# Example
```jldoctest
julia> (sys, statebounds, inputbounds) = controlbenchmark( "lin01" )
(ControlSystems.StateSpace{Float64,Array{Float64,2}}
A =
 1.0  0.01
 0.0  1.0
B =
 -0.0004
 -0.0701
C =
 1.0  0.0
 0.0  1.0
D =
 0.0
 0.0

Sample Time: 0.01 (seconds)
Discrete-time state-space model, LazySets.Interval[LazySets.Interval{Float64,IntervalArithmetic.Interval{Float64}}([-0.200001, 0.0100001]), LazySets.Interval{Float64,IntervalArithmetic.Interval{Float64}}([-0.100001, 0.100001])], LazySets.Interval[LazySets.Interval{Float64,IntervalArithmetic.Interval{Float64}}([-0.0524001, 0.0524001])])
```
"""
function controlbenchmark( id::String )
    b = getbenchmark( id )
    controlbenchmark( b )
end


text:
using Test
using MarriageMarkets

include("setup_tests.jl") # multiprocess model computations

@testset "MarriageMarkets" begin

	@testset "Static model" begin

		@testset "Unidimensional model" begin
			include("test_static_unidim.jl")
		end

		@testset "Multidimensional model" begin
			include("test_static_multidim.jl")
		end

	end # static model

	@testset "Search model" begin

		@testset "Basic Shimer-Smith model" begin
			include("test_search_base.jl")
		end

		@testset "Stochastic model" begin
			include("test_search_stoch.jl")
		end

		@testset "Multidimensional types" begin
			include("test_search_multidim.jl")
		end

	end # search model

end # MarriageMarkets


text:
@testset "SimQueue FIFO/LIFO, Resource FIFO/LIFO" begin
    function qfifo(fq, fr)
        tmp = []
        q = SimQueue{Int}(max_queue = 5, max_requests = 3,
                          fifo_queue = fq, fifo_requests = fr)
        s = Scheduler()

        @test request!(s, q, (x, y) -> push!(tmp, (1, y)))
        @test request!(s, q, (x, y) -> push!(tmp, (2, y)))
        @test request!(s, q, (x, y) -> push!(tmp, (3, y)))
        @test !request!(s, q, (x, y) -> push!(tmp, (4, y)))
        @test length(q.requests) == 3
        @test provide!(s, q, 1)
        s.now = 1.0
        @test provide!(s, q, 2)
        s.now = 2.0
        @test provide!(s, q, 3)
        @test length(q.queue) == 0
        @test length(s.event_queue) == 3
        @test length(q.requests) == 0
        @test provide!(s, q, 4)
        @test provide!(s, q, 5)
        @test provide!(s, q, 6)
        @test provide!(s, q, 7)
        @test provide!(s, q, 8)
        @test !provide!(s, q, 9)
        @test length(q.queue) == 5
        s.now = 3.0
        @test request!(s, q, (x, y) -> push!(tmp, (4, y)))
        s.now = 4.0
        @test request!(s, q, (x, y) -> push!(tmp, (5, y)))
        s.now = 5.0
        @test request!(s, q, (x, y) -> push!(tmp, (6, y)))
        @test length(q.queue) == 2

        go!(s, 6.0)
        tmp
    end

    @test qfifo(true, true) == [(1,1), (2,2), (3,3), (4,4), (5,5), (6,6)]
    @test qfifo(true, false) == [(3,1), (2,2), (1,3), (4,4), (5,5), (6,6)]
    @test qfifo(false, true) == [(1,1), (2,2), (3,3), (4,8), (5,7), (6,6)]
    @test qfifo(false, false) == [(3,1), (2,2), (1,3), (4,8), (5,7), (6,6)]
end

@testset "SimQueue waive! test" begin
    s = Scheduler()
    q = SimQueue{Int}()
    @test !waive!(q, identity)

    f1 = x -> println(1)
    f2 = x -> println(2)
    f3 = x -> println(3)
    request!(s, q, f1)
    request!(s, q, f2)
    request!(s, q, f3)

    @test length(q.requests) == 3
    @test waive!(q, f2)
    @test length(q.requests) == 2
    @test !waive!(q, f2)
    @test waive!(q, f1)
    @test waive!(q, f3)
    @test isempty(q.requests)
end

@testset "SimQueue withdraw! test" begin
    s = Scheduler()
    q = SimQueue{Int}()
    @test !withdraw!(q, 1)

    provide!(s, q, 1)
    provide!(s, q, 2)
    provide!(s, q, 3)

    @test length(q.queue) == 3
    @test withdraw!(q, 2)
    @test length(q.queue) == 2
    @test !withdraw!(q, 2)
    @test withdraw!(q, 1)
    @test withdraw!(q, 3)
    @test isempty(q.queue)
end


text:
using VortexDistributions

# simple test field
Nx = 400; Ny = 400
Lx = 200; Ly = Lx
x = LinRange(-Lx/2,Lx/2, Nx+1)[1:end-1]; y = LinRange(-Ly/2,Ly/2, Ny+1)[1:end-1];
psi0 = one.(x*y') |> complex

psi = Torus(copy(psi0), x, y);

xp = 0.0
yp = 0.0
vp = PointVortex(xp,yp,1)
sp = ScalarVortex(vp)

vortex!(psi, sp)

vfound = findvortices(psi)

vortm = keep_vortices(vfound)

@test length(vfound) == length(vortm)


text:
#=
Copyright (c) 2015, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=#

module Latte

using Base.Cartesian
using Iterators
using ArrayViews
using CompilerTools.AstWalker
using JLD

export init_mpi

include("util.jl")

pkg_root = joinpath(dirname(@__FILE__), "..")
latte_library_path = "$pkg_root/deps"
latte_include_path = "$pkg_root/deps/include"
libsuffix = @osx ? ".dylib" : ".so"
libIO = "$latte_library_path/build/libLatteIO$libsuffix"
libComm = "$latte_library_path/build/libLatteComm$libsuffix"

type LatteException <: Exception end

LATTE_BATCH_DROPOUT = false
if haskey(ENV, "LATTE_BATCH_DROPOUT")
    LATTE_BATCH_DROPOUT = true
end

LATTE_MPI = false
if haskey(ENV, "LATTE_MPI")
    LATTE_MPI = true

    @eval ccall((:init, $libComm), Void, ())
    log_info("Finished initializing comm library")
end
@eval ccall((:init, $libIO), Void, (Cuchar,), LATTE_MPI)
atexit(() -> @eval ccall((:clean_up, $libIO), Void, ()))

@enum Phase TrainTest Train Test
export TrainTest, Train, Test

include("neuron.jl")

include("types.jl")
include("param.jl")
include("net.jl")
include("ensembles.jl")
include("comm.jl")

include("stdlib/solvers.jl")
include("stdlib/stdlib.jl")

end # module Latte


text:
using Symbolics
using Test

@variables t x
D1 = Difference(t; dt=0.01)
D2 = Difference(t; dt=0.01)

@test D1 == D2
@test Base.isequal(D1, D2)
@test Base.hash(D1) == Base.hash(D2)

@test D1(x) isa Num
@test repr(D1) == "Difference(t; dt=0.01)"


text:
function graphics_config()
    Plots.plotly()
    Plots.default(size=(1300, 800),
        guidefont=("times", 10),
        tickfont=("times", 10))
end

function mem_estimate(num::Numerics)
    return 2 * num.T / num.dt * num.S / num.ds * 128
end

function describe_simulation(sim::Simulation, app::Apparatus, state::InitialState)

end

max_stars_repo_name: lorenzifrancesco/soliton-BEC

max_issues_repo_name: lorenzifrancesco/soliton-BEC

max_forks_repo_name: lorenzifrancesco/soliton-BEC


text:
mutable struct ScaledFit{T, fitType}
    μ::T
    σ::T
    fit_func::fitType
end
ScaledFit(μ, σ, fit_func) = ScaledFit{typeof(μ), typeof(fit_func)}(
                                μ, σ, fit_func)

function (f::ScaledFit)(x) f.fit_func((x-f.μ)/f.σ) end
function der(f::ScaledFit, query; order=1)
    der(f.fit_func, order=order)((query-f.μ)/f.σ)/f.σ^order
end
"""
Alias for polyder when fit function is polynomial
"""
function der(f::Poly; order=1) polyder(f, order) end
function der(f::Poly, query; order=1) polyder(f, order)(query) end
"""
Macro for call work-around See JuliaLang Pull #23168
https://github.com/JuliaLang/julia/pull/23168
"""
t_info(ex::Symbol) = (ex, tuple())
t_info(ex::Expr) = ex.head == :(<:) ? t_info(ex.args[1]) : (ex, ex.args[2:end])
macro ev(fsig)
    @assert fsig.head == :type
    tname, tparams = t_info(fsig.args[2])
    sym = esc(:x)
    return quote
        $(esc(fsig))
        ($sym::$tname)(args...) = prop_call($sym, args...)
    end
end


text:
macro aimport(mod)
    if isdefined(mod)
        quote
            import $mod
        end
    else
        esc(quote
            arequire(string($(QuoteNode(mod))))
            import $mod
        end)
    end
end

macro ausing(mod)
    if isdefined(mod)
        quote
            using $mod
        end
    else
        esc(quote
            arequire(string($(QuoteNode(mod))))
            using $mod
        end)
    end
end