text:
#=

    Reading input

=#
function read_input()::Array{Int32}

    input::Array{Int32} = []
    open("./input", "r") do f
        while ! eof(f)
            s::String = readline(f)
            push!(input,tryparse(Int32,s))
        end
    end

    return input
end
input = read_input()

#=

PART 1

Before you leave, the Elves in accounting just need you to fix your
expense report (your puzzle input); apparently, something isn't quite adding up.
Specifically, they need you to find the two entries that sum to 2020 and then
multiply those two numbers together.

For example, suppose your expense report contained the following:
1721
979
366
299
675
1456

In this list, the two entries that sum to 2020 are 1721 and 299. Multiplying them
together produces 1721 * 299 = 514579, so the correct answer is 514579.

Of course, your expense report is much larger. Find the two entries that sum to 2020;
what do you get if you multiply them together?
=#

EXPECTED_SUM = 2020

function separate_evens_from_odds(input::Array{Int32})::Tuple{Array{Int32}, Array{Int32}}

    evens::Array{Int32} = []
    odds::Array{Int32} = []
    for i in input
        if mod(i,2) == 1
            push!(odds,i)
        else
            push!(evens,i)
        end
    end
    return (evens, odds)
end

# -1: sum is smaller than the expected sum
# 0: sum is the expected sum
# 1: sum is larger than the expected sum
function check_sum(a::Int32, b::Int32)::Int8
    s::Int32 = a+b
    if s > EXPECTED_SUM
        return 1
    elseif s < EXPECTED_SUM
        return -1
    else
        println("Found solution: ", a, " and ", b)
        return 0
    end
end

# -1: result is impossible for given array
function matrix_search(array::Array{Int32})

    array_size::Int32 = size(array,1)
    if array_size < 2
        return -1
    end

    array_split::Int32 = div(array_size,2)
    columns_idx::UnitRange{Int32} = 1:array_split
    rows_idx::UnitRange{Int32} = array_split+1:array_size
    i::Int32 = 1
    j::Int32 = 1

    result = check_sum(array[rows_idx[i]], array[columns_idx[j]])

    if result > 0
        return matrix_search(array[columns_idx])
    elseif result < 0
        return downward_flow(array,i+1,j+1,columns_idx,rows_idx)
    else
        return array[rows_idx[i]] * array[columns_idx[j]]
    end
end

# -1: result is impossible for given array
function downward_flow(array,i,j,columns_idx,rows_idx)

    while(j<=last(columns_idx))
        result = check_sum(array[rows_idx[i]], array[columns_idx[j]])
        if result == 0
            return array[rows_idx[i]] * array[columns_idx[j]]
        elseif result < 0
            i+=1
            j+=1
        else

            # Do the search in the line
            jj = j-1
            while jj >= 1
                result = check_sum(array[rows_idx[i]], array[columns_idx[jj]])
                if result == 0
                    return array[rows_idx[i]] * array[columns_idx[jj]]
                end
                jj-=1
            end

            # Do the search in the up line
            i-=1
            jj = last(columns_idx)
            while jj > j
                result = check_sum(array[rows_idx[i]], array[columns_idx[jj]])
                if result == 0
                    return array[rows_idx[i]] * array[columns_idx[jj]]
                end
                jj-=1
            end
            result = matrix_search(array[rows_idx])
            if result == -1
                return matrix_search(array[columns_idx]) end
        end
    end

    if i<last(rows_idx)
    # Walks horizontally through the whole last line of the matrix
        i = size(rows_idx,1)
        j = 1
        while j<=last(columns_idx)
            result = check_sum(array[rows_idx[i]], array[columns_idx[j]])
            if result < 0
                return matrix_search(array[rows_idx]) # Make the matrix search with highest values
            elseif result == 0
                return array[rows_idx[i]] * array[columns_idx[j]]
            end
            j+=1
        end
    end

    return matrix_search(array[rows_idx]) # Make the matrix search with highest values
end

evens, odds = separate_evens_from_odds(input)
result = matrix_search(sort(evens))
if result == -1
    println("\n\nSum not found in evens. Trying with odds...")
    result = matrix_search(sort(odds))
    if result == -1
        println("Impossible solution for given array!")
    end
else
    println(result)
end

#=

PART 2

The Elves in accounting are thankful for your help; one of them even offers you
a starfish coin they had left over from a past vacation. They offer you a second
one if you can find three numbers in your expense report that meet the same criteria.

Using the above example again, the three entries that sum to 2020 are 979, 366, and
675. Multiplying them together produces the answer, 241861950.

In your expense report, what is the product of the three entries that sum to 2020?

=#

# -1: sum is smaller than the expected sum
# 0: sum is the expected sum
# 1: sum is larger than the expected sum
function check_sum(a::Int32, b::Int32, c::Int32)::Int8
    s::Int32 = a+b+c
    if s > EXPECTED_SUM
        return 1
    elseif s < EXPECTED_SUM
        return -1
    else
        println("Found solution: ", a, " and ", b)
        return 0
    end
end

function search_sum(input)
# Dont know how to optimize significantly.. it's gonna be O(n^3)
    for i in 1:size(input,1)
        for j in 1:size(input,1)
            if j == i
                continue
            end
            for k in 1:size(input,1)
                if k == i || k == j
                    continue
                end

                result = check_sum(input[i], input[j], input[k])
                if result == 0
                    println("Success in the 3 number sum search: ", input[i], ", ", input[j], " and ", input[k])
                    return input[i] * input[j] * input[k]
                end
            end
        end
    end
    return -1
end

result = search_sum(input)
if result == -1
    println("3 digits sum not found in given array!")
else
    println("Result: ", result)
end


text:
# Autogenerated wrapper script for Tcl_jll for x86_64-w64-mingw32
export libtcl

using Zlib_jll
JLLWrappers.@generate_wrapper_header("Tcl")
JLLWrappers.@declare_library_product(libtcl, "tcl86.dll")
function __init__()
    JLLWrappers.@generate_init_header(Zlib_jll)
    JLLWrappers.@init_library_product(
        libtcl,
        "bin\\tcl86.dll",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()

ext: jl


text:
using LibGEOS: distance, envelope
import LibGEOS: interpolate
using Combinatorics: permutations, combinations
using GeoInterface: coordinates, xcoord, ycoord
import Base: convert

# type conversion
const BorderType = Union{LibGEOS.MultiLineString, LibGEOS.LineString}
BorderType(ls::GeoInterface.LineString) = LibGEOS.LineString(ls)
BorderType(ls::GeoInterface.MultiLineString) = LibGEOS.MultiLineString(ls)

const RegionType = Union{LibGEOS.Polygon, LibGEOS.MultiPolygon}
convert(::Type{RegionType}, p::GeoInterface.AbstractPolygon) = LibGEOS.Polygon(p)
convert(::Type{RegionType}, p::GeoInterface.AbstractMultiPolygon) = LibGEOS.MultiPolygon(p)

function interpolate(border::LibGEOS.MultiLineString, dist::Real)
    ptr = border.ptr
    interp_ptr = LibGEOS.interpolate(ptr, dist)
    interp = LibGEOS.Point(interp_ptr)
    return interp
end
function sentinels(border::BorderType, nsent::Int)
    sentinel_distances = range(0, stop=LibGEOS.geomLength(border), length=nsent)
    sentinel_points = [interpolate(border,q) for q in sentinel_distances]
    sentinel_coords = [coordinates(p) for p in sentinel_points]
    sentinels_x = [p[1] for p in sentinel_coords]
    sentinels_y = [p[2] for p in sentinel_coords]
    Xb = [sentinels_x' ; sentinels_y']
    return Xb
end

function inregion(point::GeoInterface.Point, region::GeoInterface.MultiPolygon)
    xy = coordinates(point)
    poly_coords = coordinates(region)
    isin = false
    for poly in poly_coords
        if inpoly(xy, hcat(poly_coords)...)
            isin = true
        end
    end
    return isin
end
function raw_border(A_T::RegionType, A_C::RegionType, buffer::Float64)
    A_T_boundary = LibGEOS.boundary(A_T)
    A_C_buffered = LibGEOS.buffer(A_C, buffer)
    border = LibGEOS.intersection(A_T_boundary, A_C_buffered)
    return border
end

"""
    get_border(A_T::RegionType, A_C::RegionType, [buffer::Float64=0.0])

Obtain the border between two regions. The treatment region (`A_T`)
and control region (`A_C`) are of type `Polygon` or `MultiPolygon`.

The `buffer` argument controls how much the control region is
buffered by (expanded) before it is interesected with the boundary
of the treatment area. This is useful when adjacent polygons don't have
perfectly equal corner coordinates; otherwise use `buffer=0`.

The border is then postprocessed to merge and order adjacent segments.
"""
function get_border(A_T::RegionType, A_C::RegionType, buffer::Float64)
    if distance(envelope(A_T), envelope(A_C)) > buffer
        return nothing
    end
    if distance(A_T, A_C) > buffer
        return nothing
    end
    border = raw_border(A_T, A_C, buffer)
    if !(typeof(border) <: BorderType)
        return nothing
    end
    border = rearrange_lines(border)
    return border
end
function get_border(A_T::P1, A_C::P2, buffer::Float64) where {
                P1 <: Union{GeoInterface.AbstractPolygon, GeoInterface.AbstractMultiPolygon},
                P2 <: Union{GeoInterface.AbstractPolygon, GeoInterface.AbstractMultiPolygon}}
    A_T_libgeos = convert(RegionType, A_T)
    A_C_libgeos = convert(RegionType, A_C)
    return get_border(A_T_libgeos, A_C_libgeos)
end
get_border(A_T, A_C) = get_border(A_T, A_C, 0.0)

"""
    region_grid(region::RegionType, gridspace::Float64)

Obtain a regular grid of points within `region`, with space
between points controlled by the `gridspace` argument.
"""
function region_grid(region::RegionType, gridspace::Float64)
    env = envelope(region)
    env_coord = coordinates(env)[1]
    X1_min = minimum(xcoord(p) for p in env_coord)
    X1_max = maximum(xcoord(p) for p in env_coord)
    X2_min = minimum(ycoord(p) for p in env_coord)
    X2_max = maximum(ycoord(p) for p in env_coord)

    # Obtain grid of points that covers the region
    X1_grid = X1_min:gridspace:X1_max
    X2_grid = X2_min:gridspace:X2_max
    gridpoints = LibGEOS.MultiPoint([
        [x, y]
        for x in X1_grid
        for y in X2_grid
       ])
    inregion_gridpoints = LibGEOS.intersection(region, gridpoints)
    grid_coords = coordinates(inregion_gridpoints)
    grid_mat = hcat(grid_coords...)
    return grid_mat
end

### What follows is tedious code to simplify a collection of linestrings
### into a multistring such that hops between adjacents linestrings
### are as short as possible
function merge_adjacent(segments)
    point_dict = Dict{Vector{Float64}, Vector{Vector{Float64}}}()
    start_dict = Dict{Vector{Float64}, Vector{Vector{Float64}}}()
    for seg in segments
        for new_point in (seg[1], seg[end])
            if haskey(point_dict, new_point)
                other_seg = point_dict[new_point]
                delete!(start_dict, other_seg[1])
                delete!(point_dict, other_seg[1])
                delete!(point_dict, other_seg[end])
                if seg[1] == other_seg[1]
                    # two segments have the same start
                    seg = vcat(reverse(seg), other_seg[2:end])
                elseif seg[end] == other_seg[1]
                    seg = vcat(seg, other_seg[2:end])
                elseif seg[1] == other_seg[end]
                    seg = vcat(other_seg[1:end-1], seg)
                elseif seg[end] == other_seg[end]
                    seg = vcat(other_seg[1:end-1], reverse(seg))
                else
                    throw("this makes no sense")
                end
            end
        end
        start_dict[seg[1]] = seg
        point_dict[seg[1]] = seg
        point_dict[seg[end]] = seg
    end
    return collect(values(start_dict))
end
""" Find all pairwise distances between segments.
    The distance between two segments is the minimum distances
    between their endpoints.
"""
function prep_distances(segments)
    nseg = length(segments)
    distances = Array{Float64}(undef, nseg, nseg)
    for j in 1:length(segments)
        for i in 1:length(segments)
            iseg = segments[i]
            jseg = segments[j]
            distances[i, j] = sqrt(
                min(
                    sum(x->x^2, iseg[1].-jseg[1]),
                    sum(x->x^2, iseg[1].-jseg[end]),
                    sum(x->x^2, iseg[end].-jseg[1]),
                    sum(x->x^2, iseg[end].-jseg[end])
                )
            )
        end
    end
    return distances
end
function travel_distance(distmat::Matrix{Float64}, order::AbstractVector{Int})
    nseg = length(order)
    sum(distmat[order[i], order[i+1]] for i in 1:nseg-1)
end
function _shortest_path_brute(distmat::Matrix{Float64})
    nseg = size(distmat, 1)
    mind = Inf
    shortest = collect(1:nseg)
    for order in permutations(1:nseg)
        d = travel_distance(distmat, order)
        if d < mind
            shortest = order
            mind = d
        end
    end
    return shortest
end
function _shortest_path_greedy(distmat::Matrix{Float64})
    nseg = size(distmat, 1)
    order = collect(1:nseg)
    mind = travel_distance(distmat, order)
    converged = false
    while !converged
        converged = true
        for swap_pair in combinations(1:nseg, 2)
            i, j = swap_pair
            order[i], order[j] = order[j], order[i]
            d = travel_distance(distmat, order)
            if d < mind
                mind = d
                converged = false
                break
            end
            # Put things back where they were if we didn't make progress.
            order[i], order[j] = order[j], order[i]
        end
    end
    return order
end
function shortest_path(distmat::Matrix{Float64})
    nseg = size(distmat, 1)
    if nseg <= 10
        return _shortest_path_brute(distmat)
    else
        return _shortest_path_greedy(distmat)
    end
end
function dist_orientation(segments, updown)
    nseg = length(segments)
    d = 0.0
    for i in 1:nseg-1
        if updown[i] # i is upright
            iend = segments[i][end]
        else # i us upside down
            iend = segments[i][1]
        end
        if updown[i+1]
            jstart = segments[i+1][1]
        else
            jstart = segments[i+1][end]
        end
        @assert length(iend) == 2
        @assert length(jstart) == 2
        d += sqrt(sum(x->x^2, iend.-jstart))
    end
    return d
end

"""
    Represent a combination as a boolean vector
    (`true` for indices present in the combination,
     `false` otherwise)
"""
function get_updown(nseg, up)
    updown = zeros(Bool, nseg)
    updown[up] .= true
    return updown
end
function best_orientation(segments)
    nseg = length(segments)
    shortest_updown = zeros(Bool, nseg)
    mindo = dist_orientation(segments, shortest_updown)
    for up in combinations(1:nseg)
        updown = get_updown(nseg, up)
        d = dist_orientation(segments, updown)
        if d < mindo
            shortest_updown = updown
            mindo = d
        end
    end
    return shortest_updown
end
function rearrange_lines(segments)
    merged = merge_adjacent(segments)
    if length(merged) == 1
        # nothing else to do
        return merged
    end
    distmat = prep_distances(merged)
    ordered = merged[shortest_path(distmat)]
    shortest_updown = best_orientation(ordered)
    nseg = length(ordered)
    for i in 1:nseg
        if !shortest_updown[i] # down
            ordered[i] = reverse(ordered[i])
        end
    end
    return ordered
end
function rearrange_lines(lines::T) where T <: GeoInterface.AbstractLineString
    # there's nothing to do if the border is just a single line
    return lines
end
function rearrange_lines(lines::T) where T <: GeoInterface.AbstractMultiLineString
    segments = coordinates(lines)
    ordered = rearrange_lines(segments)
    if length(ordered) == 1
        # type unstable, but better
        return LibGEOS.LineString(ordered[1])
    else
        return T(ordered)
    end
end


text:
"""
    BSplineBasis{T<:AbstractVector{<:Real}}

Type for a B-spline basis with breakpoint vector of type `T`.

Here, a B-spline basis is completely specified by its order ``k`` and breakpoint sequence.
The knot sequence is derived from the breakpoint sequence by duplicating the first and last
breakpoints so they each appear ``k`` times. Knot sequences where the first and last
breakpoints do not appear ``k`` times are not supported by this data type.

!!! note
    Here, unlike in some other B-spline libraries, ``k`` always refers to the *order* of a B-spline, not its *degree* (order = degree + 1).
"""
struct BSplineBasis{T<:AbstractVector{<:Real}}
    order::Int
    breakpoints::T

    function BSplineBasis{T}(order, breakpoints) where T<:AbstractVector{<:Real}
        order ≥ 1 || throw(DomainError(order, "order of B-splines must be positive."))
        length(breakpoints) ≥ 2 || throw(ArgumentError("length of breakpoint vector must be at least 2."))
        has_offset_axes(breakpoints) && throw(ArgumentError("breakpoint vector must not have offset axes."))
        new(order, breakpoints)
    end
end

"""
    BSplineBasis(order, breakpoints)

Create a B-spline basis with order `order` and breakpoint vector `breakpoints`. The
breakpoint vector is assumed to be sorted.
"""
BSplineBasis(order, breakpoints) = BSplineBasis{typeof(breakpoints)}(order, breakpoints)

Base.:(==)(x::BSplineBasis, y::BSplineBasis) =
    order(x) == order(y) && breakpoints(x) == breakpoints(y)

Base.hash(x::BSplineBasis, h::UInt) =
    hash(breakpoints(x), hash(order(x), hash(:BSplineBasis, h)))

function Base.checkbounds(b::BSplineBasis, i)
    checkbounds(Bool, b, i) || Base.throw_boundserror(b, i)
    nothing
end
Base.checkbounds(::Type{Bool}, b::BSplineBasis, i) = checkindex(Bool, eachindex(b), i)

Base.eltype(T::Type{<:BSplineBasis}) = BSpline{T}

Base.length(b::BSplineBasis) = Int(length(breakpoints(b))) + order(b) - 2

Base.iterate(b::BSplineBasis, i=1) = i-1 < length(b) ? (@inbounds b[i], i+1) : nothing

Base.firstindex(b::BSplineBasis) = 1
Base.lastindex(b::BSplineBasis)  = length(b)

Base.keys(b::BSplineBasis) = Base.OneTo(lastindex(b))

@propagate_inbounds function Base.getindex(b::BSplineBasis, i::Integer)
    @boundscheck checkbounds(b, i)
    @inbounds BSpline(b, i)
end

Base.iterate(r::Iterators.Reverse{<:BSplineBasis}, i=length(r)) =
    iszero(i) ? nothing : (@inbounds r.itr[i], i-1)

function Base.show(io::IO, ::MIME"text/plain", basis::BSplineBasis)
    summary(io, basis); println(io, ':')
    println(io, " order: ", order(basis))
    print(io, " breakpoints: ")
    show(IOContext(io, :compact=>true), breakpoints(basis))
end

Base.summary(io::IO, basis::BSplineBasis) =
    print(io, length(basis), "-element ", typeof(basis))

"""
    breakpoints(basis::BSplineBasis)

Return the breakpoint sequence of the B-spline basis.

# Examples

```jldoctest
julia> breakpoints(BSplineBasis(3, 0:5))
0:5

julia> breakpoints(BSplineBasis(4, [1.0, 1.5, 2.5, 4.0]))
4-element $(Vector{Float64}):
 1.0
 1.5
 2.5
 4.0
```
"""
breakpoints(b::BSplineBasis) = b.breakpoints

"""
    knots(basis::BSplineBasis)

Return the knot sequence of the B-spline basis.

The knot sequence is the breakpoint sequence except that the first and last values are
duplicated so they appear `order(basis)` times.

# Examples

```jldoctest
julia> knots(BSplineBasis(3, 0:5))
10-element $(BSplines.KnotVector{Int64, UnitRange{Int64}}):
 0
 0
 0
 1
 2
 3
 4
 5
 5
 5
```
"""
knots(b::BSplineBasis) = @inbounds KnotVector(breakpoints(b), order(b)-1)

"""
    order(spline::Spline)
    order(basis::BSplineBasis)

Return the order of a spline or a B-spline basis.

# Examples

```jldoctest
julia> order(BSplineBasis(3, 0:5))
3

julia> order(BSplineBasis(4, [1.0, 1.5, 2.5, 4.0]))
4
```
"""
function order end

order(b::BSplineBasis) = b.order

"""
    intervalindex(vec, x[, start])

If `v` is an `AbstractVector`, return the largest index `i` so that `vec[i] ≤ x ≤ vec[end]`
and `vec[i] < vec[end]`. Return `nothing` if no such index exists. The vector `vec` is
assumed to be sorted in ascending order.

If `vec` is a [`BSplineBasis`](@ref), return `intervalindex(knots(vec), x[, start])`.

If `start` is given, a linear search is performed, starting from the index `start` going
forward or backward. If `start` is not given, a binary search is performed.

# Examples

```jldoctest
julia> intervalindex([1,1,2,3,4,4,4,5,6,6], 2.5)
3

julia> intervalindex([1,1,2,3,4,4,4,5,6,6], 7) # returns nothing

julia> intervalindex([1,1,2,3,4,4,4,5,6,6], 1)
2

julia> intervalindex([1,1,2,3,4,4,4,5,6,6], 4)
7

julia> intervalindex([1,1,2,3,4,4,4,5,6,6], 6.0)
8
```
"""
function intervalindex end

function intervalindex(vec::AbstractVector, x)
    isnan(x) && return nothing
    lastelement = last(vec)
    x < first(vec) && return nothing
    x > lastelement && return nothing
    if x == lastelement
        for i = lastindex(vec)-1:-1:firstindex(vec)
            vec[i] < lastelement && return Int(i)
        end
        return nothing
    end
    return Int(searchsortedlast(vec, x))
end

function intervalindex(vec::AbstractRange, x)
    isnan(x) && return nothing
    firstelement = first(vec)
    lastelement = last(vec)
    x < firstelement && return nothing
    x > lastelement && return nothing
    firstelement == lastelement && return nothing
    x == lastelement && return Int(lastindex(vec)-1)
    return Int(searchsortedlast(vec, x))
end

function intervalindex(vec::AbstractVector, x, start::Integer)
    if vec[start] ≤ x
        ind = findnext(v -> v > x, vec, start+1)
        ind !== nothing && return Int(ind-1)
        lastelement = last(vec)
        if x == lastelement
            for i = lastindex(vec)-1:-1:firstindex(vec)
                vec[i] < lastelement && return Int(i)
            end
        end
        return nothing
    else
        ind = findprev(v -> v ≤ x, vec, start-1)
        ind === nothing && return nothing
        return Int(ind)
    end
end

function intervalindex(vec::AbstractRange, x, start::Integer)
    if vec[start] ≤ x
        ind = findnext(v -> v > x, vec, start+1)
        ind !== nothing && return Int(ind-1)
        lastelement = last(vec)
        first(vec) == lastelement && return nothing
        x == lastelement && return Int(lastindex(vec)-1)
        return nothing
    else
        ind = findprev(v -> v ≤ x, vec, start-1)
        ind === nothing && return nothing
        return Int(ind)
    end
end

intervalindex(b::BSplineBasis, args...) = intervalindex(knots(b), args...)

"""
    check_intervalindex(knots, x, index)

Throw an `ArgumentError` if `index` is not equal to `intervalindex(knots, x)`. (not
exported)
"""
function check_intervalindex(knots, x, index)
    if !check_intervalindex(Bool, knots, x, index)
        throw(ArgumentError("wrong intervalindex: $(repr(index))"))
    end
end

"""
    check_intervalindex(Bool, knots, x, index)

Return `true` if `index` is equal to `intervalindex(knots, x)`. (not exported)
"""
check_intervalindex(::Type{Bool}, knots, x, index)

check_intervalindex(::Type{Bool}, knots::AbstractVector, x, ::Nothing) =
    (x < first(knots)) | (x > last(knots)) | isnan(x)

function check_intervalindex(::Type{Bool}, knots::AbstractVector, x, index::Integer)
    checkbounds(Bool, knots, index)   || return false
    checkbounds(Bool, knots, index+1) || return false
    tl = knots[index]
    tr = knots[index+1]
    (tl ≤ x) & (tl < tr) & ((x < tr) | (x == tr == last(knots)))
end

check_intervalindex(::Type{Bool}, b::BSplineBasis, x, index) =
    check_intervalindex(Bool, knots(b), x, index)

struct IntervalIndices{T<:AbstractVector{<:Real}}
    vec::T
    indices::UnitRange{Int}
    offset::Int
end

"""
    IntervalIndices(vec, indices, offset)

Return an iterator that yields the numbers
`(i + offset for i = indices[1:end-1] if vec[i] < vec[i+1])`. (not exported)
"""
IntervalIndices(vec::T, indices, offset) where T<:AbstractVector{<:Real} =
    IntervalIndices{T}(vec, indices, offset)

Base.IteratorSize(::Type{<:IntervalIndices}) = Base.SizeUnknown()

Base.eltype(::Type{<:IntervalIndices}) = Int

function Base.iterate(i::IntervalIndices, (index,value)=(first(i.indices),i.vec[first(i.indices)]))
    while index < last(i.indices)
        nextindex = index + 1
        nextvalue = i.vec[nextindex]
        value < nextvalue && return (index+i.offset, (nextindex, nextvalue))
        index = nextindex
    end
    return nothing
end

function Base.iterate(i::Iterators.Reverse{<:IntervalIndices})
    isempty(i.itr.indices) && return nothing
    index = last(i.itr.indices)
    iterate(i, (index,i.itr.vec[index]))
end
function Base.iterate(i::Iterators.Reverse{<:IntervalIndices}, (index,value))
    while index > first(i.itr.indices)
        nextindex = index - 1
        nextvalue = i.itr.vec[nextindex]
        value > nextvalue && return (nextindex+i.itr.offset, (nextindex, nextvalue))
        index = nextindex
    end
    return nothing
end

"""
    intervalindices(basis::BSplineBasis, indices=eachindex(basis))

Return an iterator that yields the indices of all intervals on which `basis` is defined,
i.e., it produces all indices `ind` (in ascending order) for which
`(knots(basis)[ind], knots(basis)[ind+1])` is such an interval.

If a range of `indices` is supplied, the iterator yields only those intervals on which *at
least one* of the B-splines `basis[j] for j=indices` is non-zero.

# Examples

```jldoctest
julia> intervalindices(BSplineBasis(3, 0:5))
3:7

julia> intervalindices(BSplineBasis(3, 0:5), 1:4)
3:6

julia> intervalindices(BSplineBasis(4, [1,2,3,4,4,4,5,6]))
$(BSplines.IntervalIndices{Vector{Int64}})([1, 2, 3, 4, 4, 4, 5, 6], 1:8, 3)

julia> collect(ans)
5-element $(Vector{Int64}):
  4
  5
  6
  9
 10
```
"""
intervalindices(::BSplineBasis, ::Union{AbstractUnitRange,Colon}=Colon())

function intervalindices(basis::BSplineBasis, indices::Colon=Colon())
    bps = breakpoints(basis)
    _intervalindices(bps, eachindex(bps), order(basis)-1)
end

function intervalindices(basis::BSplineBasis, indices::AbstractUnitRange)
    checkbounds(basis, indices)
    bps = breakpoints(basis)
    km1 = order(basis)-1
    if isempty(indices)
        _intervalindices(bps, 1:0, km1)
    else
        firstknotindex = max(first(indices)-km1, firstindex(bps))
        lastknotindex  = min(last(indices)+1, lastindex(bps))
        _intervalindices(bps, firstknotindex:lastknotindex, km1)
    end
end

"""
    intervalindices(basis::BSplineBasis, i, j, ...)

For integers `i`, `j`, …, return an iterator that yields the indices of all intervals on
which *all* of the B-splines `basis[i]`, `basis[j]`, … are non-zero, i.e., it produces all
indices `ind` (in ascending order) for which `(knots(basis)[ind], knots(basis)[ind+1])` is
such an interval.

# Examples

```jldoctest
julia> intervalindices(BSplineBasis(3, 0:5), 3)
3:5

julia> intervalindices(BSplineBasis(3, 0:5), 4, 5)
5:6

julia> intervalindices(BSplineBasis(3, 0:5), 2, 6) # B-splines do not overlap
6:5

julia> intervalindices(BSplineBasis(3, 0:5), 3, 5, 4)
5:5

julia> intervalindices(BSplineBasis(4, [1,2,3,4,4,4,5,6]), 3, 5)
$(BSplines.IntervalIndices{Vector{Int64}})([1, 2, 3, 4, 4, 4, 5, 6], 2:4, 3)

julia> collect(ans)
2-element $(Vector{Int64}):
 5
 6
```
"""
function intervalindices(basis::BSplineBasis, indices::Integer...)
    for i in indices
        checkbounds(basis, i)
    end
    bps = breakpoints(basis)
    km1 = order(basis)-1
    firstknotindex = max(max(indices...)-km1, firstindex(bps))
    lastknotindex  = min(min(indices...)+1, lastindex(bps))
    _intervalindices(bps, firstknotindex:lastknotindex, km1)
end

_intervalindices(vec, indices, offset) = IntervalIndices(vec, indices, offset)

_intervalindices(vec::AbstractRange, indices, offset) =
    (iszero(step(vec)) ? indices[1:0] : indices[1:end-1]) .+ offset

length_indices(basis::BSplineBasis, ::Colon) = length(basis)
length_indices(basis::BSplineBasis, indices) = length(indices)

range_indices(basis::BSplineBasis, ::Colon) = eachindex(basis)
range_indices(basis::BSplineBasis, indices) = indices

"""
    support(basis::BSplineBasis) -> a, b

Return the interval ``[a,b]`` on which the B-spline basis is defined, i.e., `a` is the first
and `b` the last breakpoint of the `basis`.

# Examples

```jldoctest
julia> support(BSplineBasis(3, 0:5))
(0, 5)

julia> support(BSplineBasis(4, [1.0, 1.5, 2.5, 4.0]))
(1.0, 4.0)
```
"""
support(b::BSplineBasis) = (bps = breakpoints(b); (first(bps), last(bps)))

"""
    bsplines(basis, x[, derivative]; kwargs...)

Calculate the values and/or derivatives of all non-zero B-splines of `basis` at `x`.

If all B-splines of `basis` are zero at `x`, `nothing` is returned. If any B-splines are
non-zero at `x`, an `OffsetArray` is returned. Its size and contents depend on the optional
`derivative` argument:
* If `derivative` is not given, the returned `OffsetArray` contains the value of the `i`-th
  B-spline at the index `i`.
* If `derivative == Derivative(N)`, the returned `OffsetArray` contains the `N`-th
  derivative of the `i`-th B-spline at the index `i`.
* If `derivative == AllDerivatives(N)`, the returned `OffsetArray` contains the `m`-th
  derivative (`0 ≤ m < N`) of the `i`-th B-spline at the index `i, m`.

Two optional keyword arguments can be used to increase performance:
* `derivspace`: When calculating derivatives, some coefficients are stored in a matrix of
  size `(order(basis), order(basis))`. By default, the function allocates a new matrix. To
  avoid this, a pre-allocated matrix can be supplied with the `derivspace` keyword. It can
  only be used when calculating derivatives, i.e., with `Derivative(N)` where `N ≥ 1` or
  `AllDerivatives(N)` where `N ≥ 2`. To also pre-allocate the array that contains the
  result, use the [`bsplines!`](@ref) function instead.
* `leftknot`: If the index of the relevant interval (i.e.,
  `intervalindex(basis(spline), x)`) is already known, it can be supplied with the
  `leftknot` keyword.

# Examples

```jldoctest
julia> basis = BSplineBasis(4, 0:5);

julia> bsplines(basis, 2.4)
4-element OffsetArray(::$(Vector{Float64}), 3:6) with eltype Float64 with indices 3:6:
 0.03600000000000002
 0.5386666666666667
 0.41466666666666663
 0.01066666666666666

julia> bsplines(basis, 2.4, Derivative(1), derivspace=zeros(4,4))
4-element OffsetArray(::$(Vector{Float64}), 3:6) with eltype Float64 with indices 3:6:
 -0.18000000000000005
 -0.5599999999999999
  0.66
  0.07999999999999996

julia> bsplines(basis, 6) # returns nothing

julia> bsplines(basis, 17//5, leftknot=7)
4-element OffsetArray(::$(Vector{Rational{Int64}}), 4:7) with eltype Rational{Int64} with indices 4:7:
   9//250
 202//375
 307//750
   2//125

julia> bsplines(basis, 2.4, AllDerivatives(3))
4×3 OffsetArray(::$(Matrix{Float64}), 3:6, 0:2) with eltype Float64 with indices 3:6×0:2:
 0.036      -0.18   0.6
 0.538667   -0.56  -0.8
 0.414667    0.66  -0.2
 0.0106667   0.08   0.4
```
"""
function bsplines(basis::BSplineBasis, x, drv=NoDerivative();
                  leftknot=intervalindex(basis, x), derivspace=nothing)
    check_intervalindex(basis, x, leftknot)
    check_derivspace(basis, drv, derivspace)
    leftknot === nothing && return nothing
    dest = bsplines_destarray(basis, x, drv, derivspace)
    offset = @inbounds _bsplines!(dest, derivspace, basis, x, leftknot, drv)
    bsplines_offsetarray(dest, offset, drv)
end

"""
    bsplines!(dest, basis, x[, derivative]; kwargs...)

Calculate the values and/or derivatives of all non-zero B-splines of `basis` at `x` in-place
(i.e., in `dest`).

If all B-splines of `basis` are zero at `x`, `nothing` is returned. If any B-splines are
non-zero at `x`, an `OffsetArray` that wraps `dest` is returned. Its size and contents
depend on the optional `derivative` argument:
* If `derivative` is not given, the returned `OffsetArray` contains the value of the `i`-th
  B-spline at the index `i`. In this case, `dest` must be a vector of length `order(basis)`.
* If `derivative == Derivative(N)`, the returned `OffsetArray` contains the `N`-th
  derivative of the `i`-th B-spline at the index `i`. Again, `dest` must be a vector of
  length `order(basis)`.
* If `derivative == AllDerivatives(N)`, the returned `OffsetArray` contains the `m`-th
  derivative (`0 ≤ m < N`) of the `i`-th B-spline at the index `i, m`. In this case, `dest`
  must be a matrix of size `(order(basis), N)`.

Two optional keyword arguments can be used to increase performance:
* `derivspace`: When calculating derivatives, some coefficients are stored in a matrix of
  size `(order(basis), order(basis))`. By default, the function allocates a new matrix. To
  avoid this, a pre-allocated matrix can be supplied with the `derivspace` keyword. It can
  only be used when calculating derivatives, i.e., with `Derivative(N)` where `N ≥ 1` or
  `AllDerivatives(N)` where `N ≥ 2`.
* `leftknot`: If the index of the relevant interval (i.e.,
  `intervalindex(basis(spline), x)`) is already known, it can be supplied with the
  `leftknot` keyword.

# Examples

```jldoctest
julia> basis = BSplineBasis(4, 0:5);

julia> dest = zeros(4);

julia> bsplines!(dest, basis, 2.4)
4-element OffsetArray(::$(Vector{Float64}), 3:6) with eltype Float64 with indices 3:6:
 0.03600000000000002
 0.5386666666666667
 0.41466666666666663
 0.01066666666666666

julia> parent(ans) === dest
true

julia> bsplines!(dest, basis, -1.0) # returns nothing

julia> dest = zeros(4, 3);

julia> bsplines!(dest, basis, 3.75, AllDerivatives(3))
4×3 OffsetArray(::$(Matrix{Float64}), 4:7, 0:2) with eltype Float64 with indices 4:7×0:2:
 0.00260417  -0.03125    0.25
 0.315104    -0.65625    0.25
 0.576823     0.265625  -1.625
 0.105469     0.421875   1.125

julia> parent(ans) === dest
true
```
"""
function bsplines!(dest, basis::BSplineBasis, x, drv=NoDerivative();
                   leftknot=intervalindex(basis, x), derivspace=nothing)
    check_intervalindex(basis, x, leftknot)
    check_destarray_axes(dest, basis, drv)
    check_derivspace(basis, drv, derivspace)
    leftknot === nothing && return nothing
    offset = @inbounds _bsplines!(dest, derivspace, basis, x, leftknot, drv)
    bsplines_offsetarray(dest, offset, drv)
end

check_derivspace(basis, drv::NoDerivUnion, ::Nothing) = nothing
check_derivspace(basis, drv::NoDerivUnion, derivspace) =
    throw(ArgumentError("derivspace keyword cannot be used with $drv."))

check_derivspace(basis, drv, ::Nothing) = nothing
function check_derivspace(basis, drv, derivspace)
    got = axes(derivspace)
    exp = (Base.OneTo(order(basis)), Base.OneTo(order(basis)))
    if got != exp
        throw(DimensionMismatch("derivspace array has wrong axes: expected $exp, got $got"))
    end
    nothing
end

bsplines_destarray(basis, x, drv, derivspace) = bsplines_destarray(eltype(derivspace), basis, drv)
bsplines_destarray(basis, x, drv, ::Nothing)  = bsplines_destarray(bspline_returntype(basis, x), basis, drv)

bsplines_destarray(T, basis, ::Derivative)                = Vector{T}(undef, order(basis))
bsplines_destarray(T, basis, ::AllDerivatives{N}) where N = Matrix{T}(undef, order(basis), N)

bsplines_offsetarray(arr, offset, ::Derivative)     = OffsetArray(arr, offset)
bsplines_offsetarray(arr, offset, ::AllDerivatives) = OffsetArray(arr, offset, -1)

function check_destarray_axes(dest, basis, drv)
    got = axes(dest)
    exp = destarray_axes(basis, drv)
    if got != exp
        throw(DimensionMismatch("destination array has wrong axes: expected $exp, got $got"))
    end
    nothing
end

destarray_axes(basis, ::Derivative)                = (Base.OneTo(order(basis)),)
destarray_axes(basis, ::AllDerivatives{N}) where N = (Base.OneTo(order(basis)), Base.OneTo(N))

# The implementation of this method (and the method iterate_bsplines! it calls) is adapted
# from the Fortran subroutine BSPLVB from Carl de Boor’s book *A practical Guide to
# Splines* [^deBoor1978].
#
# [^deBoor1978]:
#     Carl de Boor, *A Practical Guide to Splines*, New York, N.Y.: Springer-Verlag, 1978.
@propagate_inbounds function _bsplines!(dest, _, basis, x, leftknot::Integer, ::NoDerivUnion)
    t = knots(basis)
    k = order(basis)
    xtyped = convert(eltype(dest), x)
    dest[1] = oneunit(eltype(dest))
    for j = 1:k-1
        iterate_bsplines!(dest, dest, t, j, xtyped, leftknot)
    end
    return leftknot-k
end

# The implementation of this method (and the methods iterate_bsplines! and
# iterate_derivatives! it calls) is adapted from the Fortran subroutine BSPLVD from Carl de
# Boor’s book *A practical Guide to Splines* [^deBoor1978].
#
# [^deBoor1978]:
#     Carl de Boor, *A Practical Guide to Splines*, New York, N.Y.: Springer-Verlag, 1978.
@propagate_inbounds function _bsplines!(dest, derivspace, basis, x, leftknot::Integer, ::AllDerivatives{N}) where N
    t = knots(basis)
    k = order(basis)
    xtyped = convert(eltype(dest), x)
    N′ = min(k, N)
    # Set k-th and higher derivatives to zero
    dest[:, N′+1:N] .= zero(eltype(dest))
    # Successively calculate values of B-splines and store them in dest
    lastcol = @view dest[N′:k, N′]
    lastcol[1] = oneunit(eltype(dest))
    for j = 1:k-N′
        iterate_bsplines!(lastcol, lastcol, t, j, xtyped, leftknot)
    end
    newcol = lastcol
    for j = k-N′+1:k-1
        oldcol = newcol
        newcol = @view dest[k-j:k, k-j]
        iterate_bsplines!(newcol, oldcol, t, j, xtyped, leftknot)
    end
    # Successively calculate coefficients for derivatives and combine them with B-splines
    drvcoeffs = prepare_drvcoeffs(eltype(dest), order(basis), derivspace)
    for col = 2:N′
        # Column `col` contains `col-1`-th derivatives
        iterate_derivatives!(drvcoeffs, t, k, col-1, leftknot)
        for row = 1:k
            sum = zero(eltype(dest))
            for j = max(row,col):k
                sum += oftype(sum, drvcoeffs[j, row] * dest[j, col])
            end
            dest[row, col] = sum
        end
    end
    return leftknot-k
end

prepare_drvcoeffs(T, k, ::Nothing) = Matrix{T}(I, k, k)
function prepare_drvcoeffs(_, k, derivspace)
    fill!(derivspace, 0)
    for i = diagind(derivspace)
        derivspace[i] = 1
    end
    derivspace
end

# The implementation of this method (and the methods iterate_bsplines! and
# iterate_derivatives! it calls) is adapted from the Fortran subroutine BSPLVD from Carl de
# Boor’s book *A practical Guide to Splines* [^deBoor1978].
#
# [^deBoor1978]:
#     Carl de Boor, *A Practical Guide to Splines*, New York, N.Y.: Springer-Verlag, 1978.
@propagate_inbounds function _bsplines!(dest, derivspace, basis, x, leftknot::Integer, ::Derivative{N}) where N
    t = knots(basis)
    k = order(basis)
    xtyped = convert(eltype(dest), x)
    if N ≥ k
        # k-th and higher derivatives are zero
        dest .= zero(eltype(dest))
    else
        # Calculate B-splines of order k-N and store them in dest[N+1:k]
        col = @view dest[N+1:k]
        # col = uview(dest, N+1:k)
        col[1] = oneunit(eltype(dest))
        for j = 1:k-N-1
            iterate_bsplines!(col, col, t, j, xtyped, leftknot)
        end
        # Calculate coefficients for derivatives and combine them with B-splines
        drvcoeffs = prepare_drvcoeffs(eltype(dest), order(basis), derivspace)
        for drv = 1:N
            iterate_derivatives!(drvcoeffs, t, k, drv, leftknot)
        end
        # Combine coefficients with B-splines
        for i = 1:k
            sum = zero(eltype(dest))
            for j = max(i,N+1):k
                sum += oftype(sum, drvcoeffs[j,i] * dest[j])
            end
            dest[i] = sum
        end
    end
    return leftknot-k
end

# Take the B-splines of order `j` from `src[1:j]` and write the B-splines of order `j+1` to `dest[1:j+1]`
@propagate_inbounds function iterate_bsplines!(dest::AbstractArray{T}, src::AbstractArray{T}, knots, j, x::T, leftknot) where T
    saved = zero(T)
    for r = 1:j
        tₗ₊ᵣ   = convert(T, knots[leftknot+r])
        tₗ₊ᵣ₋ⱼ = convert(T, knots[leftknot+r-j])
        term = src[r] / (tₗ₊ᵣ - tₗ₊ᵣ₋ⱼ)
        dest[r] = saved + (tₗ₊ᵣ - x) * term
        saved = oftype(saved, (x - tₗ₊ᵣ₋ⱼ) * term)
    end
    dest[j+1] = saved
end

# Iterate the coefficients in `coeffs` from the `deriv-1`-th to the `deriv`-th derivative
@propagate_inbounds function iterate_derivatives!(coeffs::AbstractArray{T}, knots, k, deriv, leftknot) where T
    kmd = k-deriv
    for j = 1:kmd
        lp1mj = leftknot+1-j
        factor = kmd / (convert(T, knots[lp1mj+kmd]) - convert(T, knots[lp1mj]))
        row = k+1-j
        for col = 1:row
            coeffs[row, col] = (coeffs[row, col] - coeffs[row-1, col]) * factor
        end
    end
end

"""
    basismatrix(basis::BSplineBasis, xvalues; indices=eachindex(basis))

Calculate the matrix `[basis[i](x) for x=xvalues, i=indices]`.

# Examples

```jldoctest
julia> basis = BSplineBasis(3, 0:5);

julia> x = [0.3, 1.5, 3.2, 4.5];

julia> basismatrix(basis, x)
4×7 $(Matrix{Float64}):
 0.49  0.465  0.045  0.0    0.0    0.0    0.0
 0.0   0.125  0.75   0.125  0.0    0.0    0.0
 0.0   0.0    0.0    0.32   0.66   0.02   0.0
 0.0   0.0    0.0    0.0    0.125  0.625  0.25
```
"""
function basismatrix(basis::BSplineBasis, xvalues; indices=Colon())
    checkbounds(Bool, basis, indices) || throw(ArgumentError("invalid indices for basis: $indices"))
    T = bspline_returntype(basis, eltype(xvalues))
    dest = zeros(T, length(xvalues), length_indices(basis, indices))
    derivspace = similar(dest, order(basis))
    _basismatrix!(dest, derivspace, basis, xvalues, indices)
    dest
end

"""
    basismatrix!(dest, basis::BSplineBasis, xvalues; indices=eachindex(basis))

Calculate the matrix `[basis[i](x) for x=xvalues, i=indices]` and store it in `dest`.

# Examples

```jldoctest
julia> basis = BSplineBasis(3, 0:5);

julia> x = [0.3, 1.5, 3.2, 4.5];

julia> dest = Array{Float64}(undef, length(x), length(basis));

julia> basismatrix!(dest, basis, x)
4×7 $(Matrix{Float64}):
 0.49  0.465  0.045  0.0    0.0    0.0    0.0
 0.0   0.125  0.75   0.125  0.0    0.0    0.0
 0.0   0.0    0.0    0.32   0.66   0.02   0.0
 0.0   0.0    0.0    0.0    0.125  0.625  0.25
```
"""
function basismatrix!(dest, basis::BSplineBasis, xvalues; indices=Colon())
    has_offset_axes(dest) && throw(ArgumentError("destination matrix may not have offset axes."))
    checkbounds(Bool, basis, indices) || throw(ArgumentError("invalid indices for basis: $indices"))
    expected_size = (length(xvalues), length_indices(basis, indices))
    size(dest) == expected_size || throw(DimensionMismatch("destination array has size $(size(dest)), expected $expected_size"))
    workspace = similar(dest, order(basis))
    fill!(dest, zero(eltype(dest)))
    _basismatrix!(dest, workspace, basis, xvalues, indices)
    dest
end

function _basismatrix!(dest, workspace, basis::BSplineBasis, xvalues, indices::AbstractUnitRange)
    indicesoffset = first(indices) - 1
    start = 1
    for (xindex, x) = enumerate(xvalues)
        leftknot = intervalindex(basis, x, start)
        @assert leftknot !== nothing "xvalue outside of the support of basis: $x."
        start = leftknot
        bspl = bsplines!(workspace, basis, x, leftknot=leftknot)
        for index = axes(bspl, 1) ∩ indices
            dest[xindex, index-indicesoffset] = bspl[index]
        end
    end
end

function _basismatrix!(dest, workspace, basis::BSplineBasis, xvalues, ::Colon)
    start = 1
    for (xindex, x) = enumerate(xvalues)
        leftknot = intervalindex(basis, x, start)
        @assert leftknot !== nothing "xvalue outside of the support of basis: $x."
        start = leftknot
        bspl = bsplines!(workspace, basis, x, leftknot=leftknot)
        for index = axes(bspl, 1)
            dest[xindex, index] = bspl[index]
        end
    end
end


text:
struct CartesianVIndex{N,T} <: Base.AbstractCartesianIndex{N}
    I::T
    @inline CartesianVIndex(I::T) where {N, T <: Tuple{Vararg{<:Any,N}}} = new{N,T}(I)
end
Base.length(::CartesianVIndex{N}) where {N} = N
Base.length(::Type{<:CartesianVIndex{N}}) where {N} = N
Base.Tuple(i::CartesianVIndex) = i.I
function Base.:(:)(I::CartesianVIndex{N}, J::CartesianVIndex{N}) where {N}
   CartesianIndices(map((i,j) -> i:j, I.I, J.I))
end


text:
using YAML

include("extract_sprites_meta.jl")

atlases = Dict{String, Dict{String, SpriteHolder}}()

drawingAlpha = 1
fileNotFoundSurface = Assets.missingImage

spriteSurfaceCache = Dict{String, Tuple{Number, Cairo.CairoSurface, String}}()
spriteYAMLCache = Dict{String, Tuple{Number, Bool, Any}}()

function getSpriteSurface(resource::String, filename::String, atlas::String="Gameplay")
    # If we have a filename use that, otherwise look for the resource
    filename = isempty(filename) ? findExternalSprite(resource) : filename
    filename = isa(filename, String) ? filename : ""

    if isfile(filename)
        # Check if we have a cached version from zipfile
        cacheInfo = get(spriteSurfaceCache, resource, nothing)
        fileModified = mtime(filename)

        if cacheInfo !== nothing && cacheInfo[1] <= fileModified
            return cacheInfo[2], cacheInfo[3]
        end

        if hasExt(filename, ".png")
            surface = open(Cairo.read_from_png, filename)
            spriteSurfaceCache[resource] = (fileModified, surface, filename)

            return surface, filename

        elseif hasExt(filename, ".zip")
            # Cheaper to fix resource name than every filename from zip
            # Always uses Unix path
            resourcePath = "Graphics/Atlases/$atlas/" * splitext(resource)[1] * ".png"
            surface = fileNotFoundSurface

            zipfh = ZipFile.Reader(filename)

            for file in zipfh.files
                if file.name == resourcePath
                    surface = Cairo.read_from_png(file)

                    break
                end
            end

            close(zipfh)

            spriteSurfaceCache[resource] = (fileModified, surface, filename)

            return surface, filename
        end

    else
        return fileNotFoundSurface, filename
    end
end

function getSpriteMeta(resource::String, filename::String, atlas::String="Gameplay")
    if !get(config, "load_image_meta_yaml", false)
        return false, false
    end

    # If we have a filename use that, otherwise look for the resource
    filename = isempty(filename) ? findExternalSprite(resource) : filename
    filename = isa(filename, String) ? filename : ""

    if resource !== nothing && isfile(filename)
        cacheInfo = get(spriteYAMLCache, resource, nothing)

        if hasExt(filename, ".png")
            yamlFilename = splitext(filename)[1] * ".meta.yaml"
            fileModified = mtime(yamlFilename)

            if isfile(yamlFilename)
                if cacheInfo !== nothing && cacheInfo[1] <= fileModified && fileModified != 0
                    return cacheInfo[2], cacheInfo[3]
                end

                try
                    data = open(YAML.load, yamlFilename)
                    spriteYAMLCache[resource] = (fileModified, true, data)

                    return true, data

                catch
                    spriteYAMLCache[resource] = (fileModified, false, "Invalid YAML data")

                    return false, "Invalid YAML data"
                end
            end

        elseif hasExt(filename, ".zip")
            fileModified = mtime(filename)

            if cacheInfo !== nothing && cacheInfo[1] <= fileModified
                return cacheInfo[2], cacheInfo[3]
            end

            success = true
            res = nothing

            zipfh = ZipFile.Reader(filename)

            # Cheaper to fix resource name than every filename from zip
            # Always uses Unix path
            resourcePath = "Graphics/Atlases/$atlas/" * splitext(resource)[1] * ".meta.yaml"

            for file in zipfh.files
                if file.name == resourcePath
                    try
                        res = YAML.load(String(read(file)))

                    catch
                        res = "Invalid YAML data"
                        success = false
                    end

                    break
                end
            end

            close(zipfh)

            spriteYAMLCache[resource] = (fileModified, success, res)

            return success, res
        end
    end

    return false, false
end

function addSprite!(resource::String, filename::String=""; atlas::String="Gameplay", force::Bool=false)
    spriteAtlas = getAtlas(atlas)
    spriteHolder = get(spriteAtlas, resource, nothing)

    if spriteHolder === nothing || spriteHolder.sprite.surface === fileNotFoundSurface || spriteHolder.sprite.width == 0 || spriteHolder.sprite.height == 0 || mtime(spriteHolder.path) > spriteHolder.readtime || mtime(spriteHolder.path) == 0 || force
        surface, filename = getSpriteSurface(resource, filename)
        success, meta = getSpriteMeta(resource, filename, atlas)
        hasMeta = isa(meta, Dict)

        if !success && isa(meta, String)
            println(Base.stderr, "Problem with YAML file for resource '$resource', at file '$filename' - '$meta'")
        end

        imWidth = Int(width(surface))
        imHeight = Int(height(surface))

        realWidth = hasMeta ? get(meta, "Width", imWidth) : imWidth
        realHeight = hasMeta ? get(meta, "Height", imHeight) : imHeight

        offsetX = hasMeta ? -get(meta, "X", 0) : 0
        offsetY = hasMeta ? -get(meta, "Y", 0) : 0

        spriteAtlas[resource] = SpriteHolder(
            Sprite(
                0,
                0,

                imWidth,
                imHeight,

                offsetX,
                offsetY,
                realWidth,
                realHeight,

                surface,
                filename
            ),

            time(),
            filename
        )
    end

    return spriteAtlas[resource].sprite
end

function getAtlas(atlas::String="Gameplay")
    if !haskey(atlases, atlas)
        atlases[atlas] = Dict{String, SpriteHolder}()
    end

    return atlases[atlas]
end

function getAtlasResourceNames(atlas::String="Gameplay")
    atlas = getAtlas(atlas)

    return collect(keys(atlas))
end

# Gets a resource or loads a modded one
function getSprite(name::String, atlas::String="Gameplay"; force::Bool=false)
    return addSprite!(name, atlas=atlas, force=force)
end

function loadExternalSprites!(force::Bool=false)
    externalSprites = findExternalSprites()
    for (texture, raw, atlas) in externalSprites
        addSprite!(fixTexturePath(texture), raw, atlas=atlas, force=force)
    end
end

# remove .png and convert to unix paths
function fixTexturePath(texture::String)
    return replace(Base.splitext(texture)[1], "\\" => "/")
end

function getTextureSprite(texture::String, atlas::String="Gameplay")
    return getSprite(fixTexturePath(texture), atlas)
end

function setGlobalAlpha!(alpha::Number=1)
    global drawingAlpha = alpha
end

getGlobalAlpha() = drawingAlpha

# This is probably one of the stupid ways to do it.
# Look into this if it turns out being slow

# Image based #

function drawImage(ctx::Cairo.CairoContext, surface::Cairo.CairoSurface, x::Number, y::Number, quadX::Number, quadY::Number, width::Number, height::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing)
    if tint !== nothing
        tinted = CairoImageSurface(zeros(UInt32, height, width), Cairo.FORMAT_ARGB32)
        tintedCtx = CairoContext(tinted)

        # Don't pass tint or alpha
        drawImage(tintedCtx, surface, 0, 0, quadX, quadY, width, height)
        tinted = tintSurfaceMultiplicative(tinted, tint)

        drawImage(ctx, tinted, x, y, 0, 0, width, height, alpha=alpha)

    else
        Cairo.save(ctx)

        rectangle(ctx, x, y, width, height)
        clip(ctx)

        set_source_surface(ctx, surface, x - quadX, y - quadY)
        pattern_set_filter(get_source(ctx), Cairo.FILTER_NEAREST)

        paint_with_alpha(ctx, alpha)

        restore(ctx)
    end
end

function drawImage(ctx::Cairo.CairoContext, surface::Cairo.CairoSurface, x::Number, y::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing)
    drawImage(ctx, surface, x, y, 0, 0, Int(surface.width), Int(surface.height); alpha=alpha, tint=tint)
end

drawImage(canvas::Gtk.GtkCanvas, name::String, x::Number, y::Number, quadX::Number, quadY::Number, width::Number, height::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing, atlas::String="Gameplay") = drawImage(canvas, getSprite(name, atlas), x, y, quadX, quadY, width, height, alpha=alpha, tint=tint)
drawImage(ctx::Cairo.CairoContext, name::String, x::Number, y::Number, quadX::Number, quadY::Number, width::Number, height::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing, atlas::String="Gameplay") = drawImage(ctx, getSprite(name, atlas), x, y, quadX, quadY, width, height, alpha=alpha, tint=tint)
drawImage(canvas::Gtk.GtkCanvas, sprite::Sprite, x::Number, y::Number, quadX::Number, quadY::Number, width::Number, height::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing) = drawImage(Gtk.getgc(canvas), sprite.surface, x, y, quadX, quadY, width, height, alpha=alpha, tint=tint)

function drawImage(ctx::Cairo.CairoContext, sprite::Sprite, x::Number, y::Number, quadX::Number, quadY::Number, width::Number, height::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing)
    if tint !== nothing
        tinted = CairoImageSurface(zeros(UInt32, height, width), Cairo.FORMAT_ARGB32)
        tintedCtx = CairoContext(tinted)

        # Don't pass tint or alpha
        drawImage(tintedCtx, sprite, 0, 0, quadX, quadY, width, height)
        tinted = tintSurfaceMultiplicative(tinted, tint)

        drawImage(ctx, tinted, x, y, 0, 0, width, height, alpha=alpha)

    else
        Cairo.save(ctx)

        rectangle(ctx, x, y, width, height)
        clip(ctx)

        set_source_surface(ctx, sprite.surface, x - quadX - sprite.x, y - quadY - sprite.y)
        pattern_set_filter(get_source(ctx), Cairo.FILTER_NEAREST)

        paint_with_alpha(ctx, alpha)

        restore(ctx)
    end
end

drawImage(canvas::Gtk.GtkCanvas, name::String, x::Number, y::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing, atlas::String="Gameplay") = drawImage(canvas, getSprite(name, atlas), x, y, alpha=alpha, tint=tint)
drawImage(ctx::Cairo.CairoContext, name::String, x::Number, y::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing, atlas::String="Gameplay") = drawImage(ctx, getSprite(name, atlas), x, y, alpha=alpha, tint=tint)
drawImage(canvas::Gtk.GtkCanvas, sprite::Sprite, x::Number, y::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing) = drawImage(Gtk.getgc(canvas), sprite, x, y, alpha=alpha, tint=tint)

function drawImage(ctx::Cairo.CairoContext, sprite::Sprite, x::Number, y::Number; alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing)
    drawImage(ctx, sprite, x, y, 0, 0, Int(sprite.width), Int(sprite.height), alpha=alpha, tint=tint)
end

# TODO - Only scale 1/-1 works as intended
function drawSprite(ctx::Cairo.CairoContext, texture::String, x::Number, y::Number; jx::Number=0.5, jy::Number=0.5, sx::Number=1, sy::Number=1, rot::Number=0, alpha::Number=getGlobalAlpha(), tint::Union{Nothing, rgbaTupleType}=nothing, atlas::String="Gameplay")
    sprite = getTextureSprite(texture, atlas)

    width, height = sprite.realWidth, sprite.realHeight
    drawX, drawY = floor(Int, x - width * jx - sprite.offsetX) , floor(Int, y - height * jy - sprite.offsetY)

    Cairo.save(ctx)

    scale(ctx, sx, sy)
    translate(ctx, sx > 0 ? 0 : -2 * x, sy > 0 ? 0 : -2 * y)
    translate(ctx, drawX, drawY)
    rotate(ctx, rot)
    drawImage(ctx, sprite, 0, 0, alpha=alpha, tint=tint)

    restore(ctx)
end

# Create image from matrix
# Bitmap colors are 0 index based (false = 0, true = 1)
function matrixToSurface(bitmap::Array{T, 2}, colors::Array{rgbaTupleType, 1}=rgbaTupleType[(0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)]) where T <: Integer
    height, width = size(bitmap)
    pixels = width * height

    data = Array{Cairo.ARGB32, 1}(undef, pixels)

    ptr = 1
    for x in 1:width, y in 1:height
        data[ptr] = Cairo.ARGB32(colors[bitmap[y, x] + 1]...)

        ptr += 1
    end

    data = permutedims(reshape(data, (height, width)))

    return CairoImageSurface(data)
end

# Image tinting, super inefficient

function tintSurfaceAdditive(surface::Cairo.CairoSurface, color::rgbaTupleType, width::Number=width(surface), height::Number=height(surface))
    for x in 1:floor(Int, width), y in 1:floor(Int, height)
        pixelColor = argb32ToRGBATuple(surface.data[x, y]) ./ 255
        surface.data[x, y] = normRGBA32Tuple2ARGB32(min.(1.0, pixelColor .+ color))
    end

    return surface
end

function tintSurfaceMultiplicative(surface::Cairo.CairoSurface, color::rgbaTupleType, width::Number=width(surface), height::Number=height(surface))
    for x in 1:floor(Int, width), y in 1:floor(Int, height)
        pixelColor = argb32ToRGBATuple(surface.data[x, y]) ./ 255
        surface.data[x, y] = normRGBA32Tuple2ARGB32(pixelColor .* color)
    end

    return surface
end

# Shapes #

function setSourceColor(ctx, c::colorTupleType)
    return length(c) == 3 ? set_source_rgb(ctx, c...) : set_source_rgba(ctx, c...)
end

function paintSurface(ctx::Cairo.CairoContext, c::colorTupleType=(1.0, 1.0, 1.0, 1.0))
    setSourceColor(ctx, c)
    paint(ctx)
end

paintSurface(surface::Cairo.CairoSurface, c::colorTupleType=(1.0, 1.0, 1.0, 1.0)) = paintSurface(getSurfaceContext(surface), c)

function clearArea(ctx::Cairo.CairoContext, x::Integer, y::Integer, width::Integer, height::Integer)
    Cairo.save(ctx)

    setSourceColor(ctx, (0.0, 0.0, 0.0, 1.0))
    set_operator(ctx, Cairo.OPERATOR_CLEAR)
    rectangle(ctx, x, y, width, height)
    fill(ctx)

    restore(ctx)
end

clearSurface(surface::Cairo.CairoSurface, x::Integer, y::Integer, width::Integer, height::Integer) = clearSurface(getSurfaceContext(surface), x, y, width, height)

function clearSurface(ctx::Cairo.CairoContext)
    Cairo.save(ctx)

    setSourceColor(ctx, (0.0, 0.0, 0.0, 0.0))
    set_operator(ctx, Cairo.OPERATOR_SOURCE)
    paint(ctx)

    restore(ctx)
end

clearSurface(surface::Cairo.CairoSurface) = clearSurface(getSurfaceContext(surface))

# With border
function drawRectangle(ctx::Cairo.CairoContext, x::Number, y::Number, w::Number, h::Number, fc::colorTupleType=(0.0, 0.0, 0.0), rc::colorTupleType=(0.0, 0.0, 0.0, 0.0))
    Cairo.save(ctx)

    setSourceColor(ctx, fc)
    rectangle(ctx, x, y, w, h)
    fill_preserve(ctx)
    setSourceColor(ctx, rc)
    stroke(ctx)

    restore(ctx)
end

drawRectangle(ctx::Cairo.CairoContext, rect::Rectangle, fc::colorTupleType=(0.0, 0.0, 0.0), rc::colorTupleType=(0.0, 0.0, 0.0)) = drawRectangle(ctx, rect.x, rect.y, rect.w, rect.h, fc, rc)

function drawLines(ctx::Cairo.CairoContext, nodes::Array{T, 1}, sc::colorTupleType=(0.0, 0.0, 0.0); thickness::Integer=2, filled::Bool=false, fc::colorTupleType=sc) where T <: Tuple{Number, Number}
    if length(nodes) < 2
        return
    end

    Cairo.save(ctx)

    set_antialias(ctx, thickness)
    set_line_width(ctx, thickness)

    move_to(ctx, nodes[1]...)

    for node in nodes[2:end]
        line_to(ctx, node...)
    end

    if filled
        setSourceColor(ctx, fc)
        fill_preserve(ctx)
    end

    setSourceColor(ctx, sc)
    stroke(ctx)
    restore(ctx)
end

function drawSimpleCurve(ctx::Cairo.CairoContext, curve::SimpleCurve, c::colorTupleType=(0.0, 0.0, 0.0); resolution::Integer=25, thickness::Integer=2)
    points = [getPoint(curve, i / resolution) for i in 0:resolution]

    drawLines(ctx, points, c, thickness=thickness)
end

function drawArc(ctx::Cairo.CairoContext, x::Number, y::Number, r::Number, alpha::Number, beta::Number, c::colorTupleType=(0.0, 0.0, 0.0))
    Cairo.save(ctx)

    setSourceColor(ctx, c)
    arc(ctx, x, y, r, alpha, beta)
    stroke(ctx)

    restore(ctx)
end

function drawCircle(ctx::Cairo.CairoContext, x::Number, y::Number, r::Number, c::colorTupleType=(0.0, 0.0, 0.0))
    drawArc(ctx, x, y, r, 0, 2 * pi, c)
end

function drawArrow(ctx::Cairo.CairoContext, x1::Number, y1::Number, x2::Number, y2::Number, c::colorTupleType=(0.0, 0.0, 0.0); filled::Bool=true, headLength::Number=15, headTheta::Number=pi / 6)
    theta = atan(y2 - y1, x2 - x1)
    len = sqrt((x1 - x2)^2 + (y1 - y2)^2)

    shaftLen = len - headLength
    sideLen = tan(headTheta) * headLength

    shaftX = shaftLen * cos(theta) + x1
    shaftY = shaftLen * sin(theta) + y1

    dx = sideLen * cos(theta + pi / 2)
    dy = sideLen * sin(theta + pi / 2)

    nodes = Tuple{Float64, Float64}[
        (x1, y1),
        (shaftX, shaftY),
        (shaftX + dx, shaftY + dy),
        (x2, y2),
        (shaftX - dx, shaftY - dy),
        (shaftX, shaftY),
    ]

    return drawLines(ctx, nodes, c, filled=filled)
end


text:
export cwt

"""
    cwt(signal, Λ)

Calculate the continuous wavelet transform of a signal given filter bank Λ.

# Arguments
 - `signal::Vector{Float64}`: Time series.
 - `Λ::Array{Float64, 2}`: Fourier transformed filters arranged by [frequency, scale].
"""
function cwt(signal::Vector{Float64}, Λ::Array{Float64,2})
    signal_ft = fft(signal, 1)
    W = ifft(signal_ft.*Λ, 1)
    return W
end

"""
    cwt(signal, Λ, ω0, δt, δj)

Convenience method for cwt that constructs the filter bank Λ given a signal.

# Arguments
 - `signal::Vector{Float64}`: Time series.
 - `F::FilterBank1dBlock`: Fourier transformed filters and metadata
"""
function cwt(signal::Vector{Float64}, F::FilterBank1dBlock)
    W = cwt(signal, F.Λ)
    return W
end


text:
# Copyright (c) 2013: Clp.jl contributors
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

const Clp_Solve = Cvoid

"""
    Clp_Version()

Clp library version number as string.
"""
function Clp_Version()
    ccall((:Clp_Version, libClp), Cstring, ())
end

"""
    Clp_VersionMajor()

Major number of Clp library version.
"""
function Clp_VersionMajor()
    ccall((:Clp_VersionMajor, libClp), Cint, ())
end

"""
    Clp_VersionMinor()

Minor number of Clp library version.
"""
function Clp_VersionMinor()
    ccall((:Clp_VersionMinor, libClp), Cint, ())
end

"""
    Clp_VersionRelease()

Release number of Clp library version.
"""
function Clp_VersionRelease()
    ccall((:Clp_VersionRelease, libClp), Cint, ())
end

const Clp_Simplex = Cvoid

"""
    Clp_newModel()

Default constructor
"""
function Clp_newModel()
    ccall((:Clp_newModel, libClp), Ptr{Clp_Simplex}, ())
end

"""
    Clp_deleteModel(model)

Destructor
"""
function Clp_deleteModel(model)
    ccall((:Clp_deleteModel, libClp), Cvoid, (Ptr{Clp_Simplex},), model)
end

# no prototype is found for this function at Clp_C_Interface.h:77:35, please use with caution
"""
    ClpSolve_new()

Default constructor
"""
function ClpSolve_new()
    ccall((:ClpSolve_new, libClp), Ptr{Clp_Solve}, ())
end

"""
    ClpSolve_delete(solve)

Destructor
"""
function ClpSolve_delete(solve)
    ccall((:ClpSolve_delete, libClp), Cvoid, (Ptr{Clp_Solve},), solve)
end

const CoinBigIndex = Cint

"""
    Clp_loadProblem(model, numcols, numrows, start, index, value, collb, colub, obj, rowlb, rowub)

Loads a problem (the constraints on the rows are given by lower and upper bounds). If a pointer is NULL then the following values are the default:

 colub: all columns have upper bound infinity
 collb: all columns have lower bound 0
 rowub: all rows have upper bound infinity
 rowlb: all rows have lower bound -infinity
 obj: all variables have 0 objective coefficient

Just like the other loadProblem() method except that the matrix is given in a standard column major ordered format (without gaps).
"""
function Clp_loadProblem(model, numcols, numrows, start, index, value, collb, colub, obj, rowlb, rowub)
    ccall((:Clp_loadProblem, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cint, Ptr{CoinBigIndex}, Ptr{Cint}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}), model, numcols, numrows, start, index, value, collb, colub, obj, rowlb, rowub)
end

function Clp_loadQuadraticObjective(model, numberColumns, start, column, element)
    ccall((:Clp_loadQuadraticObjective, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Ptr{CoinBigIndex}, Ptr{Cint}, Ptr{Cdouble}), model, numberColumns, start, column, element)
end

"""
    Clp_readMps(model, filename, keepNames, ignoreErrors)

Read an mps file from the given filename
"""
function Clp_readMps(model, filename, keepNames, ignoreErrors)
    ccall((:Clp_readMps, libClp), Cint, (Ptr{Clp_Simplex}, Cstring, Cint, Cint), model, filename, keepNames, ignoreErrors)
end

"""
    Clp_writeMps(model, filename, formatType, numberAcross, objSense)

Write an mps file to the given filename

Format type is 0 = normal, 1 = extra or 2 = hex. Number across is 1 or 2. Use objSense = -1D to flip the objective function around.
"""
function Clp_writeMps(model, filename, formatType, numberAcross, objSense)
    ccall((:Clp_writeMps, libClp), Cint, (Ptr{Clp_Simplex}, Cstring, Cint, Cint, Cdouble), model, filename, formatType, numberAcross, objSense)
end

"""
    Clp_copyInIntegerInformation(model, information)

Copy in integer informations
"""
function Clp_copyInIntegerInformation(model, information)
    ccall((:Clp_copyInIntegerInformation, libClp), Cvoid, (Ptr{Clp_Simplex}, Cstring), model, information)
end

"""
    Clp_deleteIntegerInformation(model)

Drop integer informations
"""
function Clp_deleteIntegerInformation(model)
    ccall((:Clp_deleteIntegerInformation, libClp), Cvoid, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_resize(model, newNumberRows, newNumberColumns)

Resizes rim part of model
"""
function Clp_resize(model, newNumberRows, newNumberColumns)
    ccall((:Clp_resize, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cint), model, newNumberRows, newNumberColumns)
end

"""
    Clp_deleteRows(model, number, which)

Deletes rows
"""
function Clp_deleteRows(model, number, which)
    ccall((:Clp_deleteRows, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Ptr{Cint}), model, number, which)
end

"""
    Clp_addRows(model, number, rowLower, rowUpper, rowStarts, columns, elements)

Add rows
"""
function Clp_addRows(model, number, rowLower, rowUpper, rowStarts, columns, elements)
    ccall((:Clp_addRows, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{CoinBigIndex}, Ptr{Cint}, Ptr{Cdouble}), model, number, rowLower, rowUpper, rowStarts, columns, elements)
end

"""
    Clp_deleteColumns(model, number, which)

Deletes columns
"""
function Clp_deleteColumns(model, number, which)
    ccall((:Clp_deleteColumns, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Ptr{Cint}), model, number, which)
end

"""
    Clp_addColumns(model, number, columnLower, columnUpper, objective, columnStarts, rows, elements)

Add columns
"""
function Clp_addColumns(model, number, columnLower, columnUpper, objective, columnStarts, rows, elements)
    ccall((:Clp_addColumns, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{CoinBigIndex}, Ptr{Cint}, Ptr{Cdouble}), model, number, columnLower, columnUpper, objective, columnStarts, rows, elements)
end

"""
    Clp_chgRowLower(model, rowLower)

Change row lower bounds
"""
function Clp_chgRowLower(model, rowLower)
    ccall((:Clp_chgRowLower, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, rowLower)
end

"""
    Clp_chgRowUpper(model, rowUpper)

Change row upper bounds
"""
function Clp_chgRowUpper(model, rowUpper)
    ccall((:Clp_chgRowUpper, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, rowUpper)
end

"""
    Clp_chgColumnLower(model, columnLower)

Change column lower bounds
"""
function Clp_chgColumnLower(model, columnLower)
    ccall((:Clp_chgColumnLower, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, columnLower)
end

"""
    Clp_chgColumnUpper(model, columnUpper)

Change column upper bounds
"""
function Clp_chgColumnUpper(model, columnUpper)
    ccall((:Clp_chgColumnUpper, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, columnUpper)
end

"""
    Clp_chgObjCoefficients(model, objIn)

Change objective coefficients
"""
function Clp_chgObjCoefficients(model, objIn)
    ccall((:Clp_chgObjCoefficients, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, objIn)
end

function Clp_modifyCoefficient(model, row, column, newElement, keepZero)
    ccall((:Clp_modifyCoefficient, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cint, Cdouble, Bool), model, row, column, newElement, keepZero)
end

"""
    Clp_dropNames(model)

Drops names - makes lengthnames 0 and names empty
"""
function Clp_dropNames(model)
    ccall((:Clp_dropNames, libClp), Cvoid, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_copyNames(model, rowNames, columnNames)

Copies in names
"""
function Clp_copyNames(model, rowNames, columnNames)
    ccall((:Clp_copyNames, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cstring}, Ptr{Cstring}), model, rowNames, columnNames)
end

"""
    Clp_numberRows(model)

Number of rows
"""
function Clp_numberRows(model)
    ccall((:Clp_numberRows, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_numberColumns(model)

Number of columns
"""
function Clp_numberColumns(model)
    ccall((:Clp_numberColumns, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_primalTolerance(model)

Primal tolerance to use
"""
function Clp_primalTolerance(model)
    ccall((:Clp_primalTolerance, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setPrimalTolerance(model, value)
    ccall((:Clp_setPrimalTolerance, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_dualTolerance(model)

Dual tolerance to use
"""
function Clp_dualTolerance(model)
    ccall((:Clp_dualTolerance, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setDualTolerance(model, value)
    ccall((:Clp_setDualTolerance, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_dualObjectiveLimit(model)

Dual objective limit
"""
function Clp_dualObjectiveLimit(model)
    ccall((:Clp_dualObjectiveLimit, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setDualObjectiveLimit(model, value)
    ccall((:Clp_setDualObjectiveLimit, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_objectiveOffset(model)

Objective offset
"""
function Clp_objectiveOffset(model)
    ccall((:Clp_objectiveOffset, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setObjectiveOffset(model, value)
    ccall((:Clp_setObjectiveOffset, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_problemName(model, maxNumberCharacters, array)

Fills in array with problem name
"""
function Clp_problemName(model, maxNumberCharacters, array)
    ccall((:Clp_problemName, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cstring), model, maxNumberCharacters, array)
end

function Clp_setProblemName(model, maxNumberCharacters, array)
    ccall((:Clp_setProblemName, libClp), Cint, (Ptr{Clp_Simplex}, Cint, Cstring), model, maxNumberCharacters, array)
end

"""
    Clp_numberIterations(model)

Number of iterations
"""
function Clp_numberIterations(model)
    ccall((:Clp_numberIterations, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

function Clp_setNumberIterations(model, numberIterations)
    ccall((:Clp_setNumberIterations, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, numberIterations)
end

"""
    maximumIterations(model)

Maximum number of iterations
"""
function maximumIterations(model)
    ccall((:maximumIterations, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

function Clp_setMaximumIterations(model, value)
    ccall((:Clp_setMaximumIterations, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, value)
end

"""
    Clp_maximumSeconds(model)

Maximum time in seconds (from when set called)
"""
function Clp_maximumSeconds(model)
    ccall((:Clp_maximumSeconds, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setMaximumSeconds(model, value)
    ccall((:Clp_setMaximumSeconds, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_hitMaximumIterations(model)

Returns true if hit maximum iterations (or time)
"""
function Clp_hitMaximumIterations(model)
    ccall((:Clp_hitMaximumIterations, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_status(model)

Status of problem: 0 - optimal 1 - primal infeasible 2 - dual infeasible 3 - stopped on iterations etc 4 - stopped due to errors
"""
function Clp_status(model)
    ccall((:Clp_status, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_setProblemStatus(model, problemStatus)

Set problem status
"""
function Clp_setProblemStatus(model, problemStatus)
    ccall((:Clp_setProblemStatus, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, problemStatus)
end

"""
    Clp_secondaryStatus(model)

Secondary status of problem - may get extended 0 - none 1 - primal infeasible because dual limit reached 2 - scaled problem optimal - unscaled has primal infeasibilities 3 - scaled problem optimal - unscaled has dual infeasibilities 4 - scaled problem optimal - unscaled has both dual and primal infeasibilities
"""
function Clp_secondaryStatus(model)
    ccall((:Clp_secondaryStatus, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

function Clp_setSecondaryStatus(model, status)
    ccall((:Clp_setSecondaryStatus, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, status)
end

"""
    Clp_optimizationDirection(model)

Direction of optimization (1 - minimize, -1 - maximize, 0 - ignore
"""
function Clp_optimizationDirection(model)
    ccall((:Clp_optimizationDirection, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setOptimizationDirection(model, value)
    ccall((:Clp_setOptimizationDirection, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_primalRowSolution(model)

Primal row solution
"""
function Clp_primalRowSolution(model)
    ccall((:Clp_primalRowSolution, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_primalColumnSolution(model)

Primal column solution
"""
function Clp_primalColumnSolution(model)
    ccall((:Clp_primalColumnSolution, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_dualRowSolution(model)

Dual row solution
"""
function Clp_dualRowSolution(model)
    ccall((:Clp_dualRowSolution, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_dualColumnSolution(model)

Reduced costs
"""
function Clp_dualColumnSolution(model)
    ccall((:Clp_dualColumnSolution, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_rowLower(model)

Row lower
"""
function Clp_rowLower(model)
    ccall((:Clp_rowLower, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_rowUpper(model)

Row upper
"""
function Clp_rowUpper(model)
    ccall((:Clp_rowUpper, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_objective(model)

Objective
"""
function Clp_objective(model)
    ccall((:Clp_objective, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_columnLower(model)

Column Lower
"""
function Clp_columnLower(model)
    ccall((:Clp_columnLower, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_columnUpper(model)

Column Upper
"""
function Clp_columnUpper(model)
    ccall((:Clp_columnUpper, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getNumElements(model)

Number of elements in matrix
"""
function Clp_getNumElements(model)
    ccall((:Clp_getNumElements, libClp), CoinBigIndex, (Ptr{Clp_Simplex},), model)
end

function Clp_getVectorStarts(model)
    ccall((:Clp_getVectorStarts, libClp), Ptr{CoinBigIndex}, (Ptr{Clp_Simplex},), model)
end

function Clp_getIndices(model)
    ccall((:Clp_getIndices, libClp), Ptr{Cint}, (Ptr{Clp_Simplex},), model)
end

function Clp_getVectorLengths(model)
    ccall((:Clp_getVectorLengths, libClp), Ptr{Cint}, (Ptr{Clp_Simplex},), model)
end

function Clp_getElements(model)
    ccall((:Clp_getElements, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_objectiveValue(model)

Objective value
"""
function Clp_objectiveValue(model)
    ccall((:Clp_objectiveValue, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_integerInformation(model)

Integer information
"""
function Clp_integerInformation(model)
    ccall((:Clp_integerInformation, libClp), Cstring, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_infeasibilityRay(model)

Gives Infeasibility ray.

Use [`Clp_freeRay`](@ref) to free the returned array.

### Returns
infeasibility ray, or NULL returned if none/wrong.
"""
function Clp_infeasibilityRay(model)
    ccall((:Clp_infeasibilityRay, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_unboundedRay(model)

Gives ray in which the problem is unbounded.

Use [`Clp_freeRay`](@ref) to free the returned array.

### Returns
unbounded ray, or NULL returned if none/wrong.
"""
function Clp_unboundedRay(model)
    ccall((:Clp_unboundedRay, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_freeRay(model, ray)

Frees a infeasibility or unbounded ray.
"""
function Clp_freeRay(model, ray)
    ccall((:Clp_freeRay, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, ray)
end

"""
    Clp_statusExists(model)

See if status array exists (partly for OsiClp)
"""
function Clp_statusExists(model)
    ccall((:Clp_statusExists, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_statusArray(model)

Return address of status array (char[numberRows+numberColumns])
"""
function Clp_statusArray(model)
    ccall((:Clp_statusArray, libClp), Ptr{Cuchar}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_copyinStatus(model, statusArray)

Copy in status vector
"""
function Clp_copyinStatus(model, statusArray)
    ccall((:Clp_copyinStatus, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cuchar}), model, statusArray)
end

function Clp_getColumnStatus(model, sequence)
    ccall((:Clp_getColumnStatus, libClp), Cint, (Ptr{Clp_Simplex}, Cint), model, sequence)
end

function Clp_getRowStatus(model, sequence)
    ccall((:Clp_getRowStatus, libClp), Cint, (Ptr{Clp_Simplex}, Cint), model, sequence)
end

function Clp_setColumnStatus(model, sequence, value)
    ccall((:Clp_setColumnStatus, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cint), model, sequence, value)
end

function Clp_setRowStatus(model, sequence, value)
    ccall((:Clp_setRowStatus, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cint), model, sequence, value)
end

"""
    Clp_setUserPointer(model, pointer)

User pointer for whatever reason
"""
function Clp_setUserPointer(model, pointer)
    ccall((:Clp_setUserPointer, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cvoid}), model, pointer)
end

function Clp_getUserPointer(model)
    ccall((:Clp_getUserPointer, libClp), Ptr{Cvoid}, (Ptr{Clp_Simplex},), model)
end

# typedef void ( COINLINKAGE_CB * clp_callback ) ( Clp_Simplex * model , int msgno , int ndouble , const double * dvec , int nint , const int * ivec , int nchar , char * * cvec )
const clp_callback = Ptr{Cvoid}

"""
    Clp_registerCallBack(model, userCallBack)

Pass in Callback function. Message numbers up to 1000000 are Clp, Coin ones have 1000000 added
"""
function Clp_registerCallBack(model, userCallBack)
    ccall((:Clp_registerCallBack, libClp), Cvoid, (Ptr{Clp_Simplex}, clp_callback), model, userCallBack)
end

"""
    Clp_clearCallBack(model)

Unset Callback function
"""
function Clp_clearCallBack(model)
    ccall((:Clp_clearCallBack, libClp), Cvoid, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_setLogLevel(model, value)

Amount of print out: 0 - none 1 - just final 2 - just factorizations 3 - as 2 plus a bit more 4 - verbose above that 8,16,32 etc just for selective debug
"""
function Clp_setLogLevel(model, value)
    ccall((:Clp_setLogLevel, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, value)
end

function Clp_logLevel(model)
    ccall((:Clp_logLevel, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_lengthNames(model)

length of names (0 means no names0
"""
function Clp_lengthNames(model)
    ccall((:Clp_lengthNames, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_rowName(model, iRow, name)

Fill in array (at least lengthNames+1 long) with a row name
"""
function Clp_rowName(model, iRow, name)
    ccall((:Clp_rowName, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cstring), model, iRow, name)
end

"""
    Clp_columnName(model, iColumn, name)

Fill in array (at least lengthNames+1 long) with a column name
"""
function Clp_columnName(model, iColumn, name)
    ccall((:Clp_columnName, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cstring), model, iColumn, name)
end

"""
    Clp_setRowName(model, iRow, name)

Set row name - Nice if they are short - 8 chars or less I think
"""
function Clp_setRowName(model, iRow, name)
    ccall((:Clp_setRowName, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cstring), model, iRow, name)
end

"""
    Clp_setColumnName(model, iColumn, name)

Set column name - Nice if they are short - 8 chars or less I think
"""
function Clp_setColumnName(model, iColumn, name)
    ccall((:Clp_setColumnName, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint, Cstring), model, iColumn, name)
end

"""
    Clp_initialSolve(model)

General solve algorithm which can do presolve. See ClpSolve.hpp for options
"""
function Clp_initialSolve(model)
    ccall((:Clp_initialSolve, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_initialSolveWithOptions(model, arg2)

Pass solve options. (Exception to direct analogue rule)
"""
function Clp_initialSolveWithOptions(model, arg2)
    ccall((:Clp_initialSolveWithOptions, libClp), Cint, (Ptr{Clp_Simplex}, Ptr{Clp_Solve}), model, arg2)
end

"""
    Clp_initialDualSolve(model)

Dual initial solve
"""
function Clp_initialDualSolve(model)
    ccall((:Clp_initialDualSolve, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_initialPrimalSolve(model)

Primal initial solve
"""
function Clp_initialPrimalSolve(model)
    ccall((:Clp_initialPrimalSolve, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_initialBarrierSolve(model)

Barrier initial solve
"""
function Clp_initialBarrierSolve(model)
    ccall((:Clp_initialBarrierSolve, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_initialBarrierNoCrossSolve(model)

Barrier initial solve, no crossover
"""
function Clp_initialBarrierNoCrossSolve(model)
    ccall((:Clp_initialBarrierNoCrossSolve, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_dual(model, ifValuesPass)

Dual algorithm - see ClpSimplexDual.hpp for method
"""
function Clp_dual(model, ifValuesPass)
    ccall((:Clp_dual, libClp), Cint, (Ptr{Clp_Simplex}, Cint), model, ifValuesPass)
end

"""
    Clp_primal(model, ifValuesPass)

Primal algorithm - see ClpSimplexPrimal.hpp for method
"""
function Clp_primal(model, ifValuesPass)
    ccall((:Clp_primal, libClp), Cint, (Ptr{Clp_Simplex}, Cint), model, ifValuesPass)
end

"""
    Clp_idiot(model, tryhard)

Solve the problem with the idiot code
"""
function Clp_idiot(model, tryhard)
    ccall((:Clp_idiot, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, tryhard)
end

"""
    Clp_scaling(model, mode)

Sets or unsets scaling, 0 -off, 1 equilibrium, 2 geometric, 3, auto, 4 dynamic(later)
"""
function Clp_scaling(model, mode)
    ccall((:Clp_scaling, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, mode)
end

"""
    Clp_scalingFlag(model)

Gets scalingFlag
"""
function Clp_scalingFlag(model)
    ccall((:Clp_scalingFlag, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_crash(model, gap, pivot)

Crash - at present just aimed at dual, returns -2 if dual preferred and crash basis created -1 if dual preferred and all slack basis preferred 0 if basis going in was not all slack 1 if primal preferred and all slack basis preferred 2 if primal preferred and crash basis created.

if gap between bounds <="gap" variables can be flipped

If "pivot" is 0 No pivoting (so will just be choice of algorithm) 1 Simple pivoting e.g. gub 2 Mini iterations
"""
function Clp_crash(model, gap, pivot)
    ccall((:Clp_crash, libClp), Cint, (Ptr{Clp_Simplex}, Cdouble, Cint), model, gap, pivot)
end

"""
    Clp_primalFeasible(model)

If problem is primal feasible
"""
function Clp_primalFeasible(model)
    ccall((:Clp_primalFeasible, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_dualFeasible(model)

If problem is dual feasible
"""
function Clp_dualFeasible(model)
    ccall((:Clp_dualFeasible, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_dualBound(model)

Dual bound
"""
function Clp_dualBound(model)
    ccall((:Clp_dualBound, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setDualBound(model, value)
    ccall((:Clp_setDualBound, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_infeasibilityCost(model)

Infeasibility cost
"""
function Clp_infeasibilityCost(model)
    ccall((:Clp_infeasibilityCost, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setInfeasibilityCost(model, value)
    ccall((:Clp_setInfeasibilityCost, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

"""
    Clp_perturbation(model)

Perturbation: 50 - switch on perturbation 100 - auto perturb if takes too long (1.0e-6 largest nonzero) 101 - we are perturbed 102 - don't try perturbing again default is 100 others are for playing
"""
function Clp_perturbation(model)
    ccall((:Clp_perturbation, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

function Clp_setPerturbation(model, value)
    ccall((:Clp_setPerturbation, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, value)
end

"""
    Clp_algorithm(model)

Current (or last) algorithm
"""
function Clp_algorithm(model)
    ccall((:Clp_algorithm, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_setAlgorithm(model, value)

Set algorithm
"""
function Clp_setAlgorithm(model, value)
    ccall((:Clp_setAlgorithm, libClp), Cvoid, (Ptr{Clp_Simplex}, Cint), model, value)
end

"""
    Clp_sumDualInfeasibilities(model)

Sum of dual infeasibilities
"""
function Clp_sumDualInfeasibilities(model)
    ccall((:Clp_sumDualInfeasibilities, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_numberDualInfeasibilities(model)

Number of dual infeasibilities
"""
function Clp_numberDualInfeasibilities(model)
    ccall((:Clp_numberDualInfeasibilities, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_sumPrimalInfeasibilities(model)

Sum of primal infeasibilities
"""
function Clp_sumPrimalInfeasibilities(model)
    ccall((:Clp_sumPrimalInfeasibilities, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_numberPrimalInfeasibilities(model)

Number of primal infeasibilities
"""
function Clp_numberPrimalInfeasibilities(model)
    ccall((:Clp_numberPrimalInfeasibilities, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_saveModel(model, fileName)

Save model to file, returns 0 if success. This is designed for use outside algorithms so does not save iterating arrays etc. It does not save any messaging information. Does not save scaling values. It does not know about all types of virtual functions.
"""
function Clp_saveModel(model, fileName)
    ccall((:Clp_saveModel, libClp), Cint, (Ptr{Clp_Simplex}, Cstring), model, fileName)
end

"""
    Clp_restoreModel(model, fileName)

Restore model from file, returns 0 if success, deletes current model
"""
function Clp_restoreModel(model, fileName)
    ccall((:Clp_restoreModel, libClp), Cint, (Ptr{Clp_Simplex}, Cstring), model, fileName)
end

"""
    Clp_checkSolution(model)

Just check solution (for external use) - sets sum of infeasibilities etc
"""
function Clp_checkSolution(model)
    ccall((:Clp_checkSolution, libClp), Cvoid, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getNumRows(model)

Number of rows
"""
function Clp_getNumRows(model)
    ccall((:Clp_getNumRows, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getNumCols(model)

Number of columns
"""
function Clp_getNumCols(model)
    ccall((:Clp_getNumCols, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getIterationCount(model)

Number of iterations
"""
function Clp_getIterationCount(model)
    ccall((:Clp_getIterationCount, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isAbandoned(model)

Are there a numerical difficulties?
"""
function Clp_isAbandoned(model)
    ccall((:Clp_isAbandoned, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isProvenOptimal(model)

Is optimality proven?
"""
function Clp_isProvenOptimal(model)
    ccall((:Clp_isProvenOptimal, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isProvenPrimalInfeasible(model)

Is primal infeasiblity proven?
"""
function Clp_isProvenPrimalInfeasible(model)
    ccall((:Clp_isProvenPrimalInfeasible, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isProvenDualInfeasible(model)

Is dual infeasiblity proven?
"""
function Clp_isProvenDualInfeasible(model)
    ccall((:Clp_isProvenDualInfeasible, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isPrimalObjectiveLimitReached(model)

Is the given primal objective limit reached?
"""
function Clp_isPrimalObjectiveLimitReached(model)
    ccall((:Clp_isPrimalObjectiveLimitReached, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isDualObjectiveLimitReached(model)

Is the given dual objective limit reached?
"""
function Clp_isDualObjectiveLimitReached(model)
    ccall((:Clp_isDualObjectiveLimitReached, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_isIterationLimitReached(model)

Iteration limit reached?
"""
function Clp_isIterationLimitReached(model)
    ccall((:Clp_isIterationLimitReached, libClp), Cint, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getObjSense(model)

Direction of optimization (1 - minimize, -1 - maximize, 0 - ignore
"""
function Clp_getObjSense(model)
    ccall((:Clp_getObjSense, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_setObjSense(model, objsen)

Direction of optimization (1 - minimize, -1 - maximize, 0 - ignore
"""
function Clp_setObjSense(model, objsen)
    ccall((:Clp_setObjSense, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, objsen)
end

"""
    Clp_getRowActivity(model)

Primal row solution
"""
function Clp_getRowActivity(model)
    ccall((:Clp_getRowActivity, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getColSolution(model)

Primal column solution
"""
function Clp_getColSolution(model)
    ccall((:Clp_getColSolution, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

function Clp_setColSolution(model, input)
    ccall((:Clp_setColSolution, libClp), Cvoid, (Ptr{Clp_Simplex}, Ptr{Cdouble}), model, input)
end

"""
    Clp_getRowPrice(model)

Dual row solution
"""
function Clp_getRowPrice(model)
    ccall((:Clp_getRowPrice, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getReducedCost(model)

Reduced costs
"""
function Clp_getReducedCost(model)
    ccall((:Clp_getReducedCost, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getRowLower(model)

Row lower
"""
function Clp_getRowLower(model)
    ccall((:Clp_getRowLower, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getRowUpper(model)

Row upper
"""
function Clp_getRowUpper(model)
    ccall((:Clp_getRowUpper, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getObjCoefficients(model)

Objective
"""
function Clp_getObjCoefficients(model)
    ccall((:Clp_getObjCoefficients, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getColLower(model)

Column Lower
"""
function Clp_getColLower(model)
    ccall((:Clp_getColLower, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getColUpper(model)

Column Upper
"""
function Clp_getColUpper(model)
    ccall((:Clp_getColUpper, libClp), Ptr{Cdouble}, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_getObjValue(model)

Objective value
"""
function Clp_getObjValue(model)
    ccall((:Clp_getObjValue, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

"""
    Clp_printModel(model, prefix)

Print model for debugging purposes
"""
function Clp_printModel(model, prefix)
    ccall((:Clp_printModel, libClp), Cvoid, (Ptr{Clp_Simplex}, Cstring), model, prefix)
end

function Clp_getSmallElementValue(model)
    ccall((:Clp_getSmallElementValue, libClp), Cdouble, (Ptr{Clp_Simplex},), model)
end

function Clp_setSmallElementValue(model, value)
    ccall((:Clp_setSmallElementValue, libClp), Cvoid, (Ptr{Clp_Simplex}, Cdouble), model, value)
end

function ClpSolve_setSpecialOption(arg1, which, value, extraInfo)
    ccall((:ClpSolve_setSpecialOption, libClp), Cvoid, (Ptr{Clp_Solve}, Cint, Cint, Cint), arg1, which, value, extraInfo)
end

function ClpSolve_getSpecialOption(arg1, which)
    ccall((:ClpSolve_getSpecialOption, libClp), Cint, (Ptr{Clp_Solve}, Cint), arg1, which)
end

"""
    ClpSolve_setSolveType(arg1, method, extraInfo)

method: (see ClpSolve::SolveType) 0 - dual simplex 1 - primal simplex 2 - primal or sprint 3 - barrier 4 - barrier no crossover 5 - automatic 6 - not implemented -- pass extraInfo == -1 for default behavior
"""
function ClpSolve_setSolveType(arg1, method, extraInfo)
    ccall((:ClpSolve_setSolveType, libClp), Cvoid, (Ptr{Clp_Solve}, Cint, Cint), arg1, method, extraInfo)
end

function ClpSolve_getSolveType(arg1)
    ccall((:ClpSolve_getSolveType, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

"""
    ClpSolve_setPresolveType(arg1, amount, extraInfo)

amount: (see ClpSolve::PresolveType) 0 - presolve on 1 - presolve off 2 - presolve number 3 - presolve number cost -- pass extraInfo == -1 for default behavior
"""
function ClpSolve_setPresolveType(arg1, amount, extraInfo)
    ccall((:ClpSolve_setPresolveType, libClp), Cvoid, (Ptr{Clp_Solve}, Cint, Cint), arg1, amount, extraInfo)
end

function ClpSolve_getPresolveType(arg1)
    ccall((:ClpSolve_getPresolveType, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_getPresolvePasses(arg1)
    ccall((:ClpSolve_getPresolvePasses, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_getExtraInfo(arg1, which)
    ccall((:ClpSolve_getExtraInfo, libClp), Cint, (Ptr{Clp_Solve}, Cint), arg1, which)
end

function ClpSolve_setInfeasibleReturn(arg1, trueFalse)
    ccall((:ClpSolve_setInfeasibleReturn, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, trueFalse)
end

function ClpSolve_infeasibleReturn(arg1)
    ccall((:ClpSolve_infeasibleReturn, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_doDual(arg1)
    ccall((:ClpSolve_doDual, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoDual(arg1, doDual)
    ccall((:ClpSolve_setDoDual, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doDual)
end

function ClpSolve_doSingleton(arg1)
    ccall((:ClpSolve_doSingleton, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoSingleton(arg1, doSingleton)
    ccall((:ClpSolve_setDoSingleton, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doSingleton)
end

function ClpSolve_doDoubleton(arg1)
    ccall((:ClpSolve_doDoubleton, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoDoubleton(arg1, doDoubleton)
    ccall((:ClpSolve_setDoDoubleton, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doDoubleton)
end

function ClpSolve_doTripleton(arg1)
    ccall((:ClpSolve_doTripleton, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoTripleton(arg1, doTripleton)
    ccall((:ClpSolve_setDoTripleton, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doTripleton)
end

function ClpSolve_doTighten(arg1)
    ccall((:ClpSolve_doTighten, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoTighten(arg1, doTighten)
    ccall((:ClpSolve_setDoTighten, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doTighten)
end

function ClpSolve_doForcing(arg1)
    ccall((:ClpSolve_doForcing, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoForcing(arg1, doForcing)
    ccall((:ClpSolve_setDoForcing, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doForcing)
end

function ClpSolve_doImpliedFree(arg1)
    ccall((:ClpSolve_doImpliedFree, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoImpliedFree(arg1, doImpliedFree)
    ccall((:ClpSolve_setDoImpliedFree, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doImpliedFree)
end

function ClpSolve_doDupcol(arg1)
    ccall((:ClpSolve_doDupcol, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoDupcol(arg1, doDupcol)
    ccall((:ClpSolve_setDoDupcol, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doDupcol)
end

function ClpSolve_doDuprow(arg1)
    ccall((:ClpSolve_doDuprow, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoDuprow(arg1, doDuprow)
    ccall((:ClpSolve_setDoDuprow, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doDuprow)
end

function ClpSolve_doSingletonColumn(arg1)
    ccall((:ClpSolve_doSingletonColumn, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setDoSingletonColumn(arg1, doSingleton)
    ccall((:ClpSolve_setDoSingletonColumn, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, doSingleton)
end

function ClpSolve_presolveActions(arg1)
    ccall((:ClpSolve_presolveActions, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setPresolveActions(arg1, action)
    ccall((:ClpSolve_setPresolveActions, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, action)
end

function ClpSolve_substitution(arg1)
    ccall((:ClpSolve_substitution, libClp), Cint, (Ptr{Clp_Solve},), arg1)
end

function ClpSolve_setSubstitution(arg1, value)
    ccall((:ClpSolve_setSubstitution, libClp), Cvoid, (Ptr{Clp_Solve}, Cint), arg1, value)
end

const Sbb_Model = Cvoid

const Cbc_Model = Cvoid

# typedef void ( COINLINKAGE_CB * sbb_callback ) ( Sbb_Model * model , int msgno , int ndouble , const double * dvec , int nint , const int * ivec , int nchar , char * * cvec )
"""
typedef for user call back. The cvec are constructed so don't need to be const
"""
const sbb_callback = Ptr{Cvoid}

# typedef void ( COINLINKAGE_CB * cbc_callback ) ( Cbc_Model * model , int msgno , int ndouble , const double * dvec , int nint , const int * ivec , int nchar , char * * cvec )
const cbc_callback = Ptr{Cvoid}

# typedef void ( COINLINKAGE_CB * cbc_cut_callback ) ( void * osiSolver , void * osiCuts , void * appdata )
"""
typedef for cbc cut callback osiSolver needs to be an OsiSolverInterface object, osiCuts is an OsiCuts object and appdata is a pointer that will be passed to the cut  generation, you can use it to point to a data structure with information about the original problem,  for instance
"""
const cbc_cut_callback = Ptr{Cvoid}


text:
using CSV, DataFrames, Statistics

results_dir = ARGS[1]
results = CSV.read("$results_dir/results.csv")

by(results, [:sampler, :Nd], :mu_ess=>mean, :sigma_ess=>mean, :tree_depth=>mean, :epsilon=>mean) |> println


text:
module test_jive_skip_calls

using Jive # Skipped @skip
using Test # @test

empty!(Jive.Skipped.expressions)

function want_to_skip()
sleep(3)
print(:dont_print_it)
end

@skip want_to_skip()
@test Jive.Skipped.expressions == [:call=>:want_to_skip]

ENV["JIVE_SKIP"] = "0"

function non_skip()
end

@skip non_skip()
@test Jive.Skipped.expressions == [:call=>:want_to_skip]

ENV["JIVE_SKIP"] = "1"

end # module test_jive_skip_calls