"""Checks output folders to determine a 4-digit run id number."""
function get_run_id_path(P::Params)

    @unpack output,outpath = P

    if output
        runlist = filter(x->startswith(x,"run"),readdir(outpath))
        existing_runs = [parse(Int,id[4:end]) for id in runlist]
        run_id = maximum(existing_runs)+1
        runpath = joinpath(outpath,@sprintf("run%04d",run_id))
        mkdir(runpath)

        return run_id,runpath
    else
        return 0,"no runpath"
    end
end

function output_initialise( Diag::DiagnosticVariables,   # output grid-space variables only
                            G::GeoSpectral,
                            P::Params)

    @unpack nlon,nlat,nlev = G.geometry
    @unpack lon,lat = G.geometry
    @unpack output_startdate, compression_level = P

    # INITIALISE FOLDER
    run_id,runpath = get_run_id_path(P)
    file_name = @sprintf("run%04d",run_id)

    # TIME
    time_string = "hours since $(Dates.format(output_startdate, "yyyy-mm-dd HH:MM:0.0"))"
    timedim = NcDim("time",0,unlimited=true)
    timevar = NcVar("time",timedim,t=Int32,atts=Dict("units"=>time_string))

    # SPACE
    londim  = NcDim("lon",nlon,values=lon)
    latdim  = NcDim("lat",nlat,values=lat)
    levdim  = NcDim("lev",nlev,values=collect(1:nlev))
    lonvar  = NcVar("lon",londim,t=Float32,atts=Dict("long_name"=>"longitude"))
    latvar  = NcVar("lat",latdim,t=Float32,atts=Dict("long_name"=>"latitude"))
    levvar  = NcVar("lev",levdim,t=Int32,atts=Dict("long_name"=>"model level"))

    # VARIABLES
    uvar        = NcVar("u",[londim,latdim,levdim,timedim],t=Float32,compress=compression_level,
                    atts=Dict("long_name"=>"zonal wind","units"=>"m/s","missing_value"=>-999999f0))
    vvar        = NcVar("v",[londim,latdim,levdim,timedim],t=Float32,compress=compression_level,
                    atts=Dict("long_name"=>"meridional wind","units"=>"m/s","missing_value"=>-999999f0))
    Tvar        = NcVar("T",[londim,latdim,levdim,timedim],t=Float32,compress=compression_level,
                    atts=Dict("long_name"=>"temperature","units"=>"K","missing_value"=>-999999f0))
    humidvar    = NcVar("humid",[londim,latdim,levdim,timedim],t=Float32,compress=compression_level,
                    atts=Dict("long_name"=>"specific humidity","units"=>"1","missing_value"=>-999999f0))
    geopotvar   = NcVar("geopot",[londim,latdim,levdim,timedim],t=Float32,compress=compression_level,
                    atts=Dict("long_name"=>"geopotential height","units"=>"m","missing_value"=>-999999f0))
    logp0var    = NcVar("logp0",[londim,latdim,timedim],t=Float32,compress=compression_level,
                    atts=Dict("long_name"=>"surface pressure","units"=>"Pa","missing_value"=>-999999f0))

    # CREATE NETCDF FILE
    netcdffile = NetCDF.create("$file_name",
                    [timevar,lonvar,latvar,levvar,uvar,vvar,Tvar,humidvar,geopotvar,logp0var],mode=NetCDF.NC_NETCDF4)

    # WRITE INITIAL CONDITIONS TO FILE
    write_output!(0,0,netcdffile,Diag,P)

    return netcdffile
end

function write_output!( iout::Int,
                        t::Real,
                        ncfile::NcFile,
                        Diag::DiagnosticVariables,
                        P::Params)

    @unpack u,v,Tabs,humid,logp0 = Diag.gridvars
    @unpack keepbits = P

    # always convert to float32 for output
    u_output = Float32.(u)
    v_output = Float32.(v)
    T_output = Float32.(Tabs)
    humid_output = Float32.(humid)
    logp0 = Float32.(logp0)

    # ROUNDING FOR ROUND+LOSSLESS COMPRESSION
    for var in (u_output,v_output,T_output,humid_output)
        round!(var,keepbits)
    end

    # WRITE TO FILE
    NetCDF.putvar(ncfile,"u",u_output,start=[1,1,1,iout],count=[-1,-1,-1,1])
    NetCDF.putvar(ncfile,"v",v_output,start=[1,1,1,iout],count=[-1,-1,-1,1])
    NetCDF.putvar(ncfile,"T",T_output,start=[1,1,1,iout],count=[-1,-1,-1,1])
    NetCDF.putvar(ncfile,"humid",humid_output,start=[1,1,1,iout],count=[-1,-1,-1,1])
    NetCDF.putvar(ncfile,"geopot",geopot_output,start=[1,1,1,iout],count=[-1,-1,-1,1])
    NetCDF.putvar(ncfile,"logp0",logp0_output,start=[1,1,iout],count=[-1,-1,1])

    # WRITE TIME
    NetCDF.putvar(ncfile,"time",Int32[t],start=[iout])
end


__precompile__()
module ByteParsers

using Base.Dates: daysinmonth, UTD, totaldays
import Base: getindex, isequal, length, show, unsafe_get

using ArgCheck: @argcheck
using DocStringExtensions: SIGNATURES
using MacroTools: @capture
using Parameters: @unpack

export
    # generic
    parsedtype,
    parsenext,
    MaybeParsed,
    isparsed,
    getpos,
    # parsers
    PosInteger,
    PosFixedInteger,
    DateYYYYMMDD,
    Skip,
    ViewBytes,
    Line

# generic interface

const ByteVector = Vector{UInt8}

"""
A parser that parses to `MaybeParsed{T}` values.

Supports [`parsedtype`](@ref) and [`parsenext`](@ref) methods.
"""
abstract type AbstractParser{T} end

"""
    parsedtype(parser)

Return type of a parser, see [`parsenext`](@ref).

See [`Line`](@ref) for the semantics of skipping types which parse into `Void`.
"""
parsedtype(::AbstractParser{T}) where {T} = T

"""
    parsenext(parser, str, pos, sep)::MaybeParsed{parsedtype(parser)}

Parse `str` starting at `pos` using `parser`. Variable-length fields are parsed
until terminated by `sep`. Return a [`MaybeParsed`](@ref) object, the position
is that of the *next* character after parsing.
"""
function parsenext end

function parsenext(parser, str::AbstractString, pos, sep::Char)
    @argcheck Int(sep) < 0x80
    parsenext(parser, convert(ByteVector, str), pos, UInt8(sep))
end

"""
    MaybeParsed(pos, [value])

When `pos > 0`, it is the position of the character **after** parsing, and
`value` holds the value. When `pos < 0`, it wraps the error code (see
[`pos_to_error`](@ref)).
"""
struct MaybeParsed{T}
    pos::Int
    value::T
    MaybeParsed{T}(pos) where {T} = new{T}(pos)
    MaybeParsed{T}(pos, value::T) where {T} = new{T}(pos, value)
    MaybeParsed(pos, value::T) where {T} = new{T}(pos, value)
end

@inline unsafe_get(x::MaybeParsed) = x.value

"""
    $SIGNATURES

When [`isparsed(x)`](@ref), return the position of the *next* byte after parsing
the value that was returned as `x`.

Otherwise, return the position of the byte where parsing failed (may be outside
the length of the string for end of line errors).
"""
@inline getpos(x::MaybeParsed) = abs(x.pos)

function isequal(x::MaybeParsed{Tx}, y::MaybeParsed{Ty}) where {Tx,Ty}
    if isbits(Tx) && isbits(Ty)
        (x.pos == y.pos) & ifelse(x.pos > 0, isequal(x.value, y.value), true)
    else
        x.pos == y.pos && (x.pos > 0 ? isequal(x.value, y.value) : true)
    end
end

"""
    $SIGNATURES

Convert a position to an error code.
"""
@inline pos_to_error(pos::Int) = -pos

"""
    $SIGNATURES

Test if the object has been parsed (otherwise, it contains an error).
"""
@inline isparsed(x::MaybeParsed) = x.pos > 0

"""
    pos, value = @checkpos x [label]

Macro for checking a [`MaybeParsed`](@ref) object (eg returned by
[`parsenext`](@ref). If there is an error, `@goto label` is invoked, otherwise
the position and the parsed value are extracted into `pos` and `value`.
"""
macro checkpos(ex, label = :error)
    @capture ex (pos_, value_) = rhs_
    quote
        maybeparsed = $(esc(rhs))
        $(esc(pos)) = maybeparsed.pos
        if $(esc(pos)) < 0
            @goto $label
        else
            $(esc(value)) = maybeparsed.value
        end
    end
end

# integer parsing

struct PosInteger{T <: Integer, S <: Integer} <: AbstractParser{T} end

"""
    $SIGNATURES

Parse a positive integer until a delimiter as the first type. The second type is
used for the internal accumulation of digits, which is *not checked for
overflows*, but conversions to `T` are.
"""
function PosInteger(T::Type{<:Integer} = Int, S::Type{<:Integer} = Int64)
    if !(promote_type(T, S) ≡ S)
        warn("Using narrower type for parsing than return value.")
    end
    PosInteger{T, S}()
end

show(io::IO, parser::PosInteger{T, S}) where {T, S} =
     print(io, "Parse positive integer into $T (accumulating in $S)")

function parsenext(parser::PosInteger{T,S}, str::ByteVector, start::Int,
                   sep::UInt8) where {T,S}
    n = zero(S)
    z = UInt8('0')
    pos = start
    len = length(str)
    @inbounds while true
        chr = str[pos]
        if chr == sep
            if pos == start
                pos = pos_to_error(pos) # empty field
            else
                pos += 1
            end
            break
        end
        maybe_digit = chr - z
        if 0 ≤ maybe_digit ≤ 9
            n = n*10 + maybe_digit
            pos += 1
        else
            pos = pos_to_error(pos) # invalid character
            break
        end
        if pos > len            # EOL without separator
            pos = pos_to_error(pos)
            break
        end
    end
    MaybeParsed(pos, T(n))
end

struct PosFixedInteger{T <: Integer} <: AbstractParser{T}
    width::Int
    function PosFixedInteger{T}(width::Int) where {T}
        @argcheck width ≥ 1
        new{T}(width)
    end
end

show(io::IO, parser::PosFixedInteger{T}) where T =
     print(io, "Parse positive integer of $(parser.width) digits into $T")

"""
    $SIGNATURES

Parse a positive integer of *fixed width* as type `T`.

!!! NOTE
    Does not check for overflows, make sure `T` has enough digits.
"""
PosFixedInteger(width, T::Type{<: Integer} = Int) =
    PosFixedInteger{T}(width)

function parsenext(parser::PosFixedInteger{T}, str::ByteVector, start,
                   sep) where {T}
    n = zero(T)
    z = UInt8('0')
    pos = start
    stop = start + parser.width - 1
    len = length(str)
    stop > len && return MaybeParsed{Int}(pos_to_error(len+1))
    @inbounds while pos ≤ stop
        chr = str[pos]
        maybe_digit = chr - z
        if 0 ≤ maybe_digit ≤ 9
            n = n*10 + maybe_digit
            pos += 1
        else
            pos = pos_to_error(pos)
            break
        end
    end
    MaybeParsed(pos, n)
end

"""
    DateYYYYMMDD()

Parse a date in YYYYMMDD format.
"""
struct DateYYYYMMDD <: AbstractParser{Date} end

show(io::IO, ::DateYYYYMMDD) = print(io, "Parse date in YYYYMMDD format")

function parsenext(parser::DateYYYYMMDD, str::ByteVector, pos, sep)
    len = length(str)
    len ≥ pos + 8 || return MaybeParsed{Date}(pos_to_error(pos + 9)) # not enough characters
    @checkpos (pos, year) = parsenext(PosFixedInteger(4), str, pos, sep)
    @checkpos (pos, month) = parsenext(PosFixedInteger(2), str, pos, sep)
    1 ≤ month ≤ 12 || (pos -= 2; @goto error)
    @checkpos (pos, day) = parsenext(PosFixedInteger(2), str, pos, sep)
    1 ≤ day ≤ daysinmonth(year, month) || (pos -= 2; @goto error)
    @inbounds str[pos] == sep || @goto error
    pos += 1
    return MaybeParsed(pos, Date(UTD(totaldays(year, month, day))))
    @label error
    MaybeParsed{Date}(pos_to_error(pos))
end

# skip and view fields

"""
    Skip()

Skip a delimited field.
"""
struct Skip <: AbstractParser{Void} end

show(io::IO, ::Skip) = print(io, "skip field (parsed as `nothing`)")

function parsenext(parser::Skip, str::ByteVector, pos, sep::UInt8)
    len = length(str)
    @inbounds while pos ≤ len
        str[pos] == sep && return MaybeParsed(pos + 1, nothing)
        pos += 1
    end
    MaybeParsed{Void}(pos_to_error(pos))
end

"""
    ViewBytes()

Return the delimited field as a view (`SubArray`) into the parsed vector,
without the delimiter.
"""
struct ViewBytes <: AbstractParser{SubArray{UInt8,1,Array{UInt8,1},
                                            Tuple{UnitRange{Int64}},
                                            true}}
end

show(io::IO, ::ViewBytes) = print(io, "return field as bytes (SubArray)")

function parsenext(parser::ViewBytes, str::ByteVector, start, sep::UInt8)
    pos = start
    @checkpos (pos, value) = parsenext(Skip(), str, pos, sep)
    return MaybeParsed(pos, @view str[start:(pos-2)])
    @label error
    MaybeParsed{parsedtype(parser)}(pos)
end

# capture known values WIP

# struct Capture{T, S, P <: AbstractParser{S}} <: AbstractParser{Union{T,S}}
#     dict::Dict{ByteVector, S}
#     inner_parser::P
# end

# function _captured(dict::Dict{ByteVector, S}, str, start, sep) where S
#     for (key, value) in dict
#         len = length(key)
#         stop = start + len
#         if length(str) ≥ stop &&
#             str[stop] == sep &&
#             all(@view str[pos:(pos+len-1)] .== key)
#             return MaybeParsed(stop + 1, key)
#         end
#     end
#     MaybeParsed{S}(start)
# end

# function show(io::IO, parser::Capture)
#     @unpack dict, inner_parser = parser
#     print(io, "parser\n    capturing $(parser.dict)\n    inner parser ")
#     print(io, parser.inner)
# end

# function parsenext(parser::Capture, str::ByteVector, start, sep::UInt8)
#     @unpack values, inner_parser = parser
#     ifparsed
# end

# empty fields

struct FixEmpty{T, P <: AbstractParser{T}} <: AbstractParser{T}
    empty_value::T
    inner_parser::P
end

function show(io::IO, parser::FixEmpty)
    @unpack empty_value, inner_parser = parser
    print(io, "parse empty fields as $(empty_value), otherwise " * repr(inner_parser))
end

function parsenext(parser::FixEmpty, str::ByteVector, start, sep::UInt8)
    @unpack empty_value, inner_parser = parser
    if str[start] == sep
        MaybeParsed(start + 1, empty_value)
    else
        parsenext(inner_parser, str, start, sep)
    end
end

# lines

"""
    Line(parsers...)

Parse a string using `parsers`, separated by the separator given to
`parsenext`. Parsed values are returned as a tuple, wrapped in a `MaybeParsed`,
except for parsers which have `parsedtype(parser) == Void`, which are skipped.

# implementation

The return type is calculated and saved in parameter `S`, while the indices of
parsers for which the values are kept are in `K`. For example, for

```julia
Line(PosInteger(), Skip(), DateYYYYMMDD())
 ```

`S == Tuple{Int, Date}` and `K == (1, 3)`.
"""
struct Line{T <: Tuple, S, K} <: AbstractParser{S}
    parsers::T
end

function show(io::IO, parser::Line)
    print(io, "parse line as the following:")
    for p in parser.parsers
        print(io, "\n    ")
        print(io, p)
    end
end

function Line{T <: Tuple}(parsers::T)
    parsedtypes = map(parsedtype, parsers)
    keep(T) = T ≠ Void
    S = Tuple{Iterators.filter(keep, parsedtypes)...}
    K = tuple(find(keep, parsedtypes)...)
    Line{T, S, K}(parsers)
end

Line(parsers::AbstractParser...) = Line(parsers)

const LineN{N, S, K} = Line{T, S, K} where T <: NTuple{N, Any}

function _tuplefor(itr, ex)
    vars = [ Base.Cartesian.inlineanonymous(ex, i) for i in itr ]
    Expr(:escape, Expr(:tuple, vars...))
end

getindex(line::Line{T, S, K}, i) where {T,S,K} = line.parsers[K[i]]

length(line::Line{T, S, K}) where {T,S,K} = length(K)

"""
    @tuplefor itr expr

Generates a tuple using `Base.Cartesian.inlineanonymous`, using elements in
`itr` (cf `Base.Cartesian.@ntuple`, which is a special case of this for `itr =
1:N`). For example,

```julia @tuplefor (1,2,9) k ``` would generate ```julia (k_1, k_2, k_9) ```

!!! NOTE

    internal use, not exported.
"""
macro tuplefor(itr, ex)
    _tuplefor(itr, ex)
end

@generated function parsenext(line::LineN{N, S, K}, str::ByteVector,
                              pos, sep) where {N, S, K}
    quote
        R = MaybeParsed{S}
        Base.@nexprs $N j -> begin
            @checkpos (pos, value_j) = parsenext(line.parsers[j], str, pos, sep)
        end
        return R(pos, @tuplefor $K value)
        @label error
        R(pos)
    end
end

end # module


@testset "symmetric" begin
    @testset "Symmetric" begin
        rng, N = MersenneTwister(123456), 3
        rrule_test(Symmetric, randn(rng, N, N), (randn(rng, N, N), randn(rng, N, N)))
    end
end


mutable struct Backoff
    limit::Int
    @const maxdelay::Int
end

# Avoid introducing a function boundaries and move `backoff` to stack (or registers).
@inline function (backoff::Backoff)()
    limit = backoff.limit
    backoff.limit = min(backoff.maxdelay, 2limit)
    delay = rand(THREAD_LOCAL_RNG[], 1:limit)
    spinfor(delay)
    return delay
end


using LearningStrategy
using DataFrames

import LearningStrategy: hook

struct DFTracer <: LearningStrategy
  f
  b::Int
  df::DataFrame

  DFTracer(T::Vector{<:Type}, names::Vector{Symbol}, f, b = 1) =
    new(f, b, DataFrame(T, names, 0))
end

function hook(t::DFTracer, m, i)
  (mod1(i, t.b) == t.b) && push!(t.df, t.f(m, i))
  nothing
end

dftracer = DFTracer([Float64, Symbol], [:reward, :action], (x, i) -> begin
  # ...
  (42, :magic)
end)


# NOTE missing inf or nan NOT allowed
# NOTE one at a time, syntax for multiple would be confusing
@with_kw mutable struct Scatter3D{T} <: Drawing3D
    data       ::T  # data container
    linestyle  ::LineStyle   = LineStyle()   # line style (color, width, ...)
    markerstyle::MarkerStyle = MarkerStyle() # marker style (color, size, ...)
    label      ::String      = ""            # plot labels (to go in the legend)
end

mutable struct Surface <: Drawing3D

end


import Base.Dates: Day, Hour

warsaw = resolve("Europe/Warsaw", tzdata["europe"]...)

normal = DateTime(2015, 1, 1, 0)   # 24 hour day in warsaw
spring = DateTime(2015, 3, 29, 0)  # 23 hour day in warsaw
fall = DateTime(2015, 10, 25, 0)   # 25 hour day in warsaw

# Unary plus
@test +ZonedDateTime(normal, warsaw) == ZonedDateTime(normal, warsaw)

# Period arithmetic
@test ZonedDateTime(normal, warsaw) + Day(1) == ZonedDateTime(normal + Day(1), warsaw)
@test ZonedDateTime(spring, warsaw) + Day(1) == ZonedDateTime(spring + Day(1), warsaw)
@test ZonedDateTime(fall, warsaw) + Day(1) == ZonedDateTime(fall + Day(1), warsaw)

@test ZonedDateTime(normal, warsaw) + Hour(24) == ZonedDateTime(normal + Hour(24), warsaw)
@test ZonedDateTime(spring, warsaw) + Hour(24) == ZonedDateTime(spring + Hour(25), warsaw)
@test ZonedDateTime(fall, warsaw) + Hour(24) == ZonedDateTime(fall + Hour(23), warsaw)

# Do the same calculations but backwards over the transitions.
@test ZonedDateTime(normal + Day(1), warsaw) - Day(1) == ZonedDateTime(normal, warsaw)
@test ZonedDateTime(spring + Day(1), warsaw) - Day(1) == ZonedDateTime(spring, warsaw)
@test ZonedDateTime(fall + Day(1), warsaw) - Day(1) == ZonedDateTime(fall, warsaw)

@test ZonedDateTime(normal + Day(1), warsaw) - Hour(24) == ZonedDateTime(normal, warsaw)
@test ZonedDateTime(spring + Day(1), warsaw) - Hour(23) == ZonedDateTime(spring, warsaw)
@test ZonedDateTime(fall + Day(1), warsaw) - Hour(25) == ZonedDateTime(fall, warsaw)

# Ensure that arithmetic around transitions works.
@test ZonedDateTime(spring, warsaw) + Hour(1) == ZonedDateTime(spring + Hour(1), warsaw)
@test ZonedDateTime(spring, warsaw) + Hour(2) == ZonedDateTime(spring + Hour(3), warsaw)
@test ZonedDateTime(fall, warsaw) + Hour(2) == ZonedDateTime(fall + Hour(2), warsaw, 1)
@test ZonedDateTime(fall, warsaw) + Hour(3) == ZonedDateTime(fall + Hour(2), warsaw, 2)

# Non-Associativity
hour_day = (ZonedDateTime(spring, warsaw) + Hour(24)) + Day(1)
day_hour = (ZonedDateTime(spring, warsaw) + Day(1)) + Hour(24)

@test hour_day - day_hour == Hour(1)

# Arithmetic with a StepRange should always work even when the start/stop lands on
# ambiguous or non-existent DateTimes.
ambiguous = DateTime(2015, 10, 25, 2)  # Ambiguous hour in Warsaw
nonexistent = DateTime(2014, 3, 30, 2)  # Non-existent hour in Warsaw

range = ZonedDateTime(ambiguous - Day(1), warsaw):Hour(1):ZonedDateTime(ambiguous - Day(1) + Hour(1), warsaw)
@test range .+ Day(1) == ZonedDateTime(ambiguous, warsaw, 1):Hour(1):ZonedDateTime(ambiguous + Hour(1), warsaw)

range = ZonedDateTime(ambiguous - Day(1) - Hour(1), warsaw):Hour(1):ZonedDateTime(ambiguous - Day(1), warsaw)
@test range .+ Day(1) == ZonedDateTime(ambiguous - Hour(1), warsaw, 1):Hour(1):ZonedDateTime(ambiguous, warsaw, 2)

range = ZonedDateTime(nonexistent - Day(1), warsaw):Hour(1):ZonedDateTime(nonexistent - Day(1) + Hour(1), warsaw)
@test range .+ Day(1) == ZonedDateTime(nonexistent + Hour(1), warsaw):Hour(1):ZonedDateTime(nonexistent + Hour(1), warsaw)

range = ZonedDateTime(nonexistent - Day(1) - Hour(1), warsaw):Hour(1):ZonedDateTime(nonexistent - Day(1), warsaw)
@test range .+ Day(1) == ZonedDateTime(nonexistent - Hour(1), warsaw):Hour(1):ZonedDateTime(nonexistent - Hour(1), warsaw)


module SparseIR

import PyCall: pyimport, PyNULL, PyVector, PyObject

const sparse_ir = PyNULL()
const pyspr = PyNULL()
const pyaugment = PyNULL()
const pysampling = PyNULL()

function __init__()
    copy!(sparse_ir, pyimport_conda("sparse_ir", "sparse-ir", "spm-lab"))
    copy!(pyspr, pyimport("sparse_ir.spr"))
    copy!(pyaugment, pyimport("sparse_ir.augment"))
    copy!(pysampling, pyimport("sparse_ir.sampling"))
    return check_version_sparse_ir()
end

include("backend.jl")
include("types.jl")
include("kernel.jl")
include("poly.jl")
include("basis.jl")
include("augment.jl")
include("composite.jl")
include("sampling.jl")
include("spr.jl")
include("basis_set.jl")
include("exports.jl")

end


@testset "Configuration" begin
    @testset "constructors" begin
        c = Configuration("test")
        p = [StringParameter("value",  "test1"),
             IntegerParameter(1, 5, 4, "test2")]
        c = Configuration(p, "test")
        @test c["test1"] == p[1]
        @test c["test2"] == p[2]
        p = Dict{String, Parameter}()
        p["test1"] = StringParameter("value",  "test")
        p["test2"] = IntegerParameter(1, 5, 4, "test")
        c = Configuration(p, "test")
        @test c["test1"] == p["test1"]
        @test c["test2"] == p["test2"]
        p = [StringParameter("valuea",  "test1"),
             StringParameter("valueb", "test2"),
             IntegerParameter(1, 44, 3, "test3")]
        c = Configuration(p, "test")
        @test c["test1"] == p[1]
        @test c["test2"] == p[2]
    end
    @testset "perturb!" begin
        p = [StringParameter("valuea",  "test1"),
             StringParameter("valueb", "test2"),
             IntegerParameter(1, 44, 3, "test3")]
        t = Configuration(p, "test1")
        l = [IntegerParameter(1, 38, 3, "i0"),
             IntegerParameter(4, 66, 55, "i1"),
             StringParameter("value", "i2"),
             FloatParameter(2.33, 85.33, 22.2, "i3"),
             t]
        c = Configuration(l, "test")
        r = perturb!(c)
        r = perturb!(c)
        r = perturb!(c)
        r = perturb!(c)
        @test typeof(r["test1"]) <: Configuration
        @test r["test1"]["test1"].value == "valuea"
        @test r["test1"]["test2"].value == "valueb"
        @test r["i2"].value             == "value"
        @test typeof(r["test1"]["test3"]) <: NumberParameter
        @test typeof(r["i0"])             <: NumberParameter
        @test typeof(r["i1"])             <: NumberParameter
        @test typeof(r["i3"])             <: NumberParameter
        d = Dict{String, Any}()
        d["i0"]                    = 3
        d["i1"]                    = 4
        d["i3"]                    = 2.3
        d["test1"]                 = Dict{String, Any}()
        d["test1"]["test3"]        = 2
        @test (typeof(perturb!(c, d)) <: Configuration)
        r                          = perturb!(c, d)
        r                          = perturb!(c, d)
        r                          = perturb!(c, d)
        r                          = perturb!(c, d)
        @test r["i0"].value       == c["i0"].value
        @test r["i1"].value       == c["i1"].value
        @test r["i3"].value       == c["i3"].value
        @test r["test1"]["test3"] == c["test1"]["test3"]
        d["test1"]["test1"]        = 23
        @test_throws MethodError perturb!(c, d)
    end
    @testset "neighbor!" begin
        l = [IntegerParameter(1, 38, 3, "i0"),
             IntegerParameter(4, 66, 55, "i1"),
             StringParameter("value", "i2"),
             FloatParameter(2.33, 85.33, 22.2, "i3")]
        d        = Dict{String, Any}()
        c        = Configuration(l, "test")
        println(c)
        d["i0"]  = 5
        v        = c["i0"].value
        neighbor!(c, d)
        @test v != c["i0"].value
        v        = c["i0"].value
        neighbor!(c, d, distance = 9)
        d        = Dict{String, Any}()
        d["i2"]  = 5
        @test_throws MethodError neighbor!(c, d)
        @test_throws MethodError neighbor!(c, d, 2)
        l        = [IntegerParameter(1, 38, 3, "i0")]
        c        = Configuration(l, "test2")
        v        = c["i0"].value
        neighbor!(c)
        @test v != c["i0"].value
    end
    @testset "update! and convert" begin
        c                    = Configuration("test")
        c["test"]            = IntegerParameter(1, 2323, 2, "i0")
        a                    = convert(Array{Parameter}, c)
        @test a[1]          == c["test"]
        push!(a, StringParameter("name", "i2"))
        update!(c, a)
        @test c["i2"]       != a[1]
        @test c["i2"].value == a[2].value
    end
end


using Raylib

const screenWidth = 800
const screenHeight = 450

function main()
    Raylib.InitWindow(
        screenWidth, screenHeight,
        "raylib [core] example - input mouse wheel"
    )

    boxPositionY = floor(Int, screenHeight/2) - 40
    scrollSpeed = 4 # Scrolling speed in pixels

    Raylib.SetTargetFPS(60)
    while !Raylib.WindowShouldClose()    # Detect window close button or ESC key
        # Update
        boxPositionY -= Int(Raylib.GetMouseWheelMove() * scrollSpeed)

        # Draw
        Raylib.BeginDrawing()
        Raylib.ClearBackground(Raylib.RAYWHITE);
        Raylib.DrawRectangle(floor(Int, screenWidth/2) - 40, boxPositionY, 80, 80, Raylib.MAROON)
        Raylib.DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, Raylib.GRAY)
        Raylib.DrawText("Box position Y: $boxPositionY", 10, 40, 20, Raylib.LIGHTGRAY)
        Raylib.EndDrawing()
    end

    Raylib.CloseWindow()
end


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

using Random123: Philox4x, Threefry4x
using Random123: random123_r, gen_seed

"""
    bat_rng()

*Experimental feature, not part of stable public API.*

Return a new BAT-compatible random number generator, with a random seed drawn
from the system entropy pool.
"""
function bat_rng end
export bat_rng

bat_rng() = Philox4x()::Philox4x{UInt64,10}

bat_determ_rng() = Philox4x((0, 0))::Philox4x{UInt64,10}

struct RNGPartition{R<:AbstractRNG,S,C,I}
    seed::S
    partctrsbase::C
    depth::Int
    idxs::I
end

function RNGPartition(parent_rng::AbstractRNG, partidxs::AbstractUnitRange{<:Integer})
    seed = rngpart_getseed(parent_rng)
    partcounters, depth = rngpart_getpartctrs(parent_rng)

    child_rngpart_partctrsbase = _rngpart_inc_partctrs(partcounters, depth, 1)

    parent_mod_partcounters = _rngpart_inc_partctrs(child_rngpart_partctrsbase, depth, length(partidxs))
    rngpart_setpartctrs!(parent_rng, parent_mod_partcounters, depth)

    R = typeof(parent_rng)
    S = typeof(seed)
    C = typeof(child_rngpart_partctrsbase)
    I = typeof(partidxs)
    RNGPartition{R,S,C,I}(seed, child_rngpart_partctrsbase, depth, partidxs)
end

Base.eachindex(rngpart::RNGPartition) = rngpart.idxs
Base.length(rngpart::RNGPartition) = length(eachindex(rngpart))
Base.size(rngpart::RNGPartition) = (length(rngpart),)

function set_rng!(rng::R, rngpart::RNGPartition{R}, i::Integer) where R <: AbstractRNG
    idxs = eachindex(rngpart)
    Base.checkindex(Bool, idxs, i) || throw(ArgumentError("Index $i not in partition indices $idxs of $rngpart"))

    j = i - minimum(idxs)
    mod_partcounters = _rngpart_inc_partctrs(rngpart.partctrsbase, rngpart.depth, j)
    mod_depth = rngpart.depth + 1

    Random.seed!(rng, rngpart.seed)
    rngpart_setpartctrs!(rng, mod_partcounters, mod_depth)

    rng
end

Random.AbstractRNG(rngpart::RNGPartition{R}, i::Integer) where R =
    set_rng!(rngpart_createrng(R), rngpart, i)

rngpart_createrng(::Type{T}) where {T <: Philox4x} = T(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

rngpart_getseed(rng::Philox4x) = (rng.key1, rng.key2)

# function set_rng!(target::Philox4x, src::Philox4x)
#     target.ctr1 = src.ctr1
#     target.ctr2 = src.ctr2
#     target.ctr3 = src.ctr3
#     target.ctr4 = src.ctr4
#     target.key1 = src.key1
#     target.key2 = src.key2
#     target.p = src.p
#     target.x1 = src.x1
#     target.x2 = src.x2
#     target.x3 = src.x3
#     target.x4 = src.x4
#     target
# end

rngpart_createrng(::Type{T}) where {T <: Threefry4x} = T(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

rngpart_getseed(rng::Threefry4x) = (rng.key1, rng.key2, rng.key3, rng.key4)

# function set_rng!(target::Threefry4x, src::Threefry4x)
#     target.x1 = src.x1
#     target.x2 = src.x2
#     target.x3 = src.x3
#     target.x4 = src.x4
#     target.key1 = src.key1
#     target.key2 = src.key2
#     target.key3 = src.key3
#     target.key4 = src.key4
#     target.ctr1 = src.ctr1
#     target.ctr2 = src.ctr2
#     target.ctr3 = src.ctr3
#     target.ctr4 = src.ctr4
#     target.p = src.p
# end

function rngpart_getpartctrs(rng::Union{Philox4x,Threefry4x})
    partctrinfo = _rngpart_split_uints(rng.ctr4, rng.ctr3, rng.ctr2)
    depth = _rngpart_getdepth(partctrinfo)
    partctrs = map(_rngpart_getpartctr, partctrinfo)
    (partctrs = partctrs, depth = depth)
end

function rngpart_setpartctrs!(rng::Union{Philox4x,Threefry4x}, partctrs::NTuple{6,UInt32}, depth::Integer)
    tagged_partctrs = _rngpart_settopbit(partctrs, depth)
    merged_partctrs = _rngpart_merge_uints(tagged_partctrs...)::NTuple{3,UInt64}

    rng.ctr4 = merged_partctrs[1]
    rng.ctr3 = merged_partctrs[2]
    rng.ctr2 = merged_partctrs[3]
    rng.ctr1 = 0

    random123_r(rng)

    rng
end

@inline _rngpart_split_uints() = ()
@inline _rngpart_split_uints(x::UInt64, xs::UInt64...) =
    (UInt32(x >> 32), UInt32(x << 32 >> 32), _rngpart_split_uints(xs...)...)

@inline _rngpart_merge_uints() = ()
@inline _rngpart_merge_uints(x1::UInt32, x2::UInt32, xs::UInt32...) =
    (UInt64(x1) << 32 | UInt64(x2), _rngpart_merge_uints(xs...)...)

_rngpart_topbit_mask(::Type{T}) where {T <: Unsigned} = ((typemax(T) >> 1) + one(T))

_rngpart_lowbits_mask(::Type{T}) where {T <: Unsigned} = typemax(T) >> 1

_rngpart_haspartctrtag(x::T) where {T<:Unsigned} = (x & _rngpart_topbit_mask(T)) > 0

_rngpart_getpartctr(x::T) where {T<:Unsigned} = (x & _rngpart_lowbits_mask(T))

function _rngpart_getdepth(partctrinfo::NTuple{N,T}) where {N,T<:Unsigned}
    cycle::Int = 1
    for i in eachindex(partctrinfo)
        x = partctrinfo[i]
        if _rngpart_haspartctrtag(x)
            cycle = i
        else
            x == 0 || throw(ArgumentError("Inconsistent partition counter information"))
        end
    end
    return cycle
end

function _rngpart_inc_partctrs(partctrs::NTuple{N,T}, depth::Integer, x::Integer) where {N,T<:Unsigned}
    1 <= depth <= length(partctrs) || throw(ArgumentError("Partition depth out of allowed range"))
    m = ntuple(i -> i == depth ? T(x) : zero(T), Val(length(partctrs)))
    partctrs .+ m
end

function _rngpart_settopbit(partctrs::NTuple{N,T}, depth::Integer) where {N,T<:Unsigned}
    1 <= depth <= length(partctrs) || throw(ArgumentError("Partition depth out of allowed range"))
    any(_rngpart_haspartctrtag, partctrs) && throw(ArgumentError("Partition counter(s) out of allowed range"))
    m = ntuple(i -> i <= depth ? _rngpart_topbit_mask(T) : zero(T), Val(length(partctrs)))
    partctrs .| m
end


##########################################################################
# Copyright 2017 Samuel Ridler.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##########################################################################

# deployment (allocation) of ambulances to stations

# make a single random deployment
# assumes ambulances are homogeneous
function makeRandDeployment(numAmbs::Int, numStations::Int;
	stationCapacities::Union{Vector{Int},Nothing} = nothing,
	rng::AbstractRNG = GLOBAL_RNG)::Deployment

	@assert(numStations > 0)
	if stationCapacities == nothing
		return sort(rand(rng, 1:numStations, numAmbs))
	else
		@assert(numStations == length(stationCapacities))
		@assert(numAmbs <= sum(stationCapacities))
		remainingCapacity = copy(stationCapacities)
		unfilledStations = Set(findall(remainingCapacity))
		deployment = zeros(Int, numAmbs)
		for i = 1:numAmbs
			j = rand(rng, unfilledStations) # station index
			deployment[i] = j
			remainingCapacity[j] -= 1
			if remainingCapacity[j] == 0
				delete!(unfilledStations, j)
			end
		end
		return sort(deployment)
	end
end

# generate a number of unique random deployments
# ignores station capacities, assumes ambulances are homogeneous
function makeRandDeployments(numAmbs::Int, numStations::Int, numDeployments::Int;
	rng::AbstractRNG = GLOBAL_RNG)
	@assert(numAmbs >= 1)
	@assert(numStations >= 1)
	@assert(numDeployments >= 1)
	@assert(numDeployments <= binomial(numAmbs + numStations - 1, numStations - 1))

	deployments = Set{Deployment}()
	while length(deployments) < numDeployments
		push!(deployments, makeRandDeployment(numAmbs, numStations; rng = rng))
	end
	deployments = collect(deployments)

	return deployments
end
function makeRandDeployments(sim::Simulation, numDeployments::Int;
	rng::AbstractRNG = GLOBAL_RNG)
	return makeRandDeployments(sim.numAmbs, sim.numStations, numDeployments; rng = rng)
end

function deploymentToStationsNumAmbs(deployment::Deployment, numStations::Int)
	# deployment[i] gives the station index for ambulance i
	stationsNumAmbs = zeros(Int, numStations)
	for stationIndex in deployment
		stationsNumAmbs[stationIndex] += 1
	end
	return stationsNumAmbs
end

function stationsNumAmbsToDeployment(stationsNumAmbs::Vector{Int})
	# stationsNumAmbs[i] gives the number of ambulances for station i
	# will assume that all ambulances are equivalent
	deployment = Deployment() # deployment[i] gives the station index for ambulance i
	for (stationIndex, numAmbs) in enumerate(stationsNumAmbs), i = 1:numAmbs
		push!(deployment, stationIndex)
	end
	return deployment
end

function getDeployment(sim::Simulation)::Deployment
	return map(a -> a.stationIndex, sim.ambulances)
end

function getStationsNumAmbs(sim::Simulation)
	stationsNumAmbs = zeros(Int, sim.numStations)
	for ambulance in sim.ambulances
		stationsNumAmbs[ambulance.stationIndex] += 1
	end
	return stationsNumAmbs
	# return deploymentToStationsNumAmbs(getDeployment(sim), sim.numStations)
end

# set ambulance to be located at given station
# this is hacky, expects ambulance will only need changing, this may not always be true
# mutates: ambulance
function setAmbStation!(sim::Simulation, ambulance::Ambulance, station::Station)
	ambulance.stationIndex = station.index
	ambulance.route = Route()
	initRoute!(sim, ambulance.route; startLoc = station.location, startFNode = station.nearestNodeIndex, startFNodeDist = station.nearestNodeDist)
end

# apply deployment 'deployment' for sim.ambulances and sim.stations
# mutates: sim.ambulances
function applyDeployment!(sim::Simulation, deployment::Deployment)
	n = length(deployment)
	@assert(n == sim.numAmbs)
	@assert(all(d -> in(d, 1:sim.numStations), deployment))
	# @assert(all(1 .<= deployment .<= sim.numStations))
	for i = 1:n
		setAmbStation!(sim, sim.ambulances[i], sim.stations[deployment[i]])
	end
end

function applyStationsNumAmbs!(sim::Simulation, stationsNumAmbs::Vector{Int})
	applyDeployment!(sim, stationsNumAmbsToDeployment(stationsNumAmbs))
end

# runs simulation for the deployment
# mutates: sim
function simulateDeployment!(sim::Simulation, deployment::Deployment)
	reset!(sim)
	applyDeployment!(sim, deployment)
	simulateToEnd!(sim)
end

# runs simulation for each deployment
# returns vector of function applied to end of each simulation run
# mutates: sim
function simulateDeployments!(sim::Simulation, deployments::Vector{Deployment}, f::Function;
	showEta::Bool = false)
	numDeployments = length(deployments)
	results = Vector{Any}(undef, numDeployments)
	t = time() # for estimating expected time remaining
	for i = 1:numDeployments
		simulateDeployment!(sim, deployments[i])
		results[i] = f(sim)
		if showEta
			remTime = (time() - t) / i * (numDeployments - i) / 60 # minutes
			print(rpad(string(" remaining run time (minutes): ", ceil(remTime, digits = 1)), 100, " "), "\r")
		end
	end
	reset!(sim)
	return results
end


#  Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors
#  This Source Code Form is subject to the terms of the Mozilla Public
#  License, v. 2.0. If a copy of the MPL was not distributed with this
#  file, You can obtain one at https://mozilla.org/MPL/2.0/.

using LinearAlgebra

function _last_primal_solution(model::Model)
    if !has_values(model)
        error(
            "No primal solution is available. You must provide a point at " *
            "which to check feasibility.",
        )
    end
    return Dict(v => value(v) for v in all_variables(model))
end

"""
    primal_feasibility_report(
        model::Model,
        point::AbstractDict{VariableRef,Float64} = _last_primal_solution(model),
        atol::Float64 = 0.0,
        skip_missing::Bool = false,
    )::Dict{Any,Float64}

Given a dictionary `point`, which maps variables to primal values, return a
dictionary whose keys are the constraints with an infeasibility greater than the
supplied tolerance `atol`. The value corresponding to each key is the respective
infeasibilility. Infeasibility is defined as the distance between the primal
value of the constraint (see `MOI.ConstraintPrimal`) and the nearest point by
Euclidean distance in the corresponding set.

## Notes

 * If `skip_missing = true`, constraints containing variables that are not in
   `point` will be ignored.
 * If `skip_missing = false` and a partial primal solution is provided, an error
   will be thrown.
 * If no point is provided, the primal solution from the last time the model was
   solved is used.

## Examples

```jldoctest; setup=:(using JuMP)
julia> model = Model();

julia> @variable(model, 0.5 <= x <= 1);

julia> primal_feasibility_report(model, Dict(x => 0.2))
Dict{Any,Float64} with 1 entry:
  x ≥ 0.5 => 0.3
```
"""
function primal_feasibility_report(
    model::Model,
    point::AbstractDict{VariableRef,Float64} = _last_primal_solution(model);
    atol::Float64 = 0.0,
    skip_missing::Bool = false,
)
    function point_f(x::VariableRef)
        fx = get(point, x, missing)
        if ismissing(fx) && !skip_missing
            error(
                "point does not contain a value for variable $x. Provide a " *
                "value, or pass `skip_missing = true`.",
            )
        end
        return fx
    end
    violated_constraints = Dict{Any,Float64}()
    for (F, S) in list_of_constraint_types(model)
        _add_infeasible_constraints(
            model,
            F,
            S,
            violated_constraints,
            point_f,
            atol,
        )
    end
    if num_nl_constraints(model) > 0
        if skip_missing
            error(
                "`skip_missing = true` is not allowed when nonlinear " *
                "constraints are present.",
            )
        end
        _add_infeasible_nonlinear_constraints(
            model,
            violated_constraints,
            point_f,
            atol,
        )
    end
    return violated_constraints
end

function _add_infeasible_constraints(
    model::Model,
    ::Type{F},
    ::Type{S},
    violated_constraints::Dict{Any,Float64},
    point_f::Function,
    atol::Float64,
) where {F,S}
    for con in all_constraints(model, F, S)
        obj = constraint_object(con)
        d = _distance_to_set(value.(obj.func, point_f), obj.set)
        if d > atol
            violated_constraints[con] = d
        end
    end
    return
end

function _add_infeasible_nonlinear_constraints(
    model::Model,
    violated_constraints::Dict{Any,Float64},
    point_f::Function,
    atol::Float64,
)
    evaluator = NLPEvaluator(model)
    MOI.initialize(evaluator, Symbol[])
    g = zeros(num_nl_constraints(model))
    MOI.eval_constraint(evaluator, g, point_f.(all_variables(model)))
    for (i, con) in enumerate(model.nlp_data.nlconstr)
        d = max(0.0, con.lb - g[i], g[i] - con.ub)
        if d > atol
            cref =
                ConstraintRef(model, NonlinearConstraintIndex(i), ScalarShape())
            violated_constraints[cref] = d
        end
    end
    return
end

function _distance_to_set(::Any, set::MOI.AbstractSet)
    return error(
        "Feasibility checker for set type $(typeof(set)) has not been " *
        "implemented yet.",
    )
end

_distance_to_set(::Missing, ::MOI.AbstractSet) = 0.0

###
### MOI.AbstractScalarSets
###

function _distance_to_set(x::T, set::MOI.LessThan{T}) where {T<:Real}
    return max(x - set.upper, zero(T))
end

function _distance_to_set(x::T, set::MOI.GreaterThan{T}) where {T<:Real}
    return max(set.lower - x, zero(T))
end

function _distance_to_set(x::T, set::MOI.EqualTo{T}) where {T<:Number}
    return abs(set.value - x)
end

function _distance_to_set(x::T, set::MOI.Interval{T}) where {T<:Real}
    return max(x - set.upper, set.lower - x, zero(T))
end

function _distance_to_set(x::T, ::MOI.ZeroOne) where {T<:Real}
    return min(abs(x - zero(T)), abs(x - one(T)))
end

function _distance_to_set(x::T, ::MOI.Integer) where {T<:Real}
    return abs(x - round(x))
end

function _distance_to_set(x::T, set::MOI.Semicontinuous{T}) where {T<:Real}
    return min(max(x - set.upper, set.lower - x, zero(T)), abs(x))
end

function _distance_to_set(x::T, set::MOI.Semiinteger{T}) where {T<:Real}
    d = max(ceil(set.lower) - x, x - floor(set.upper), abs(x - round(x)))
    return min(d, abs(x))
end

###
### MOI.AbstractVectorSets
###

function _check_dimension(v::AbstractVector, s)
    if length(v) != MOI.dimension(s)
        throw(DimensionMismatch("Mismatch between value and set"))
    end
    return
end

function _distance_to_set(x::Vector{T}, set::MOI.Nonnegatives) where {T<:Real}
    _check_dimension(x, set)
    return LinearAlgebra.norm(max(-xi, zero(T)) for xi in x)
end

function _distance_to_set(x::Vector{T}, set::MOI.Nonpositives) where {T<:Real}
    _check_dimension(x, set)
    return LinearAlgebra.norm(max(xi, zero(T)) for xi in x)
end

function _distance_to_set(x::Vector{T}, set::MOI.Zeros) where {T<:Number}
    _check_dimension(x, set)
    return LinearAlgebra.norm(x)
end

function _distance_to_set(x::Vector{T}, set::MOI.Reals) where {T<:Real}
    _check_dimension(x, set)
    return zero(T)
end


module GLIntrinsics

using StaticArrays, Sugar, GeometryTypes

import ..Transpiler: ints, floats, numbers, Numbers, Floats, int, Ints, uchar
import ..Transpiler: fixed_array_length, is_ntuple, is_fixedsize_array, GLMethod
import ..Transpiler: AbstractGLIO, ret, vecs, Vecs, vector_lengths, functions

import Sugar: typename, vecname
using SpecialFunctions: erf

immutable GLArray{T, N} <: AbstractArray{T, N} end
immutable GLTexture{T, N} <: AbstractArray{T, N} end

const GLDeviceArray = Union{GLArray, GLTexture}
const Types = Union{vecs..., numbers..., GLArray, GLTexture}
const Functions = Union{map(typeof, functions)...}

glsl_sizeof(T) = sizeof(T) * 8
# for now we disallow Float64 and map it to Float32 -> super hack alert!!!!
glsl_sizeof(::Type{Float64}) = 32
glsl_length{T <: Number}(::Type{T}) = 1
glsl_length(T) = length(T)
prescripts = Dict(
    Float32 => "",
    Float64 => "", # ignore float64 for now
    Int => "i",
    Int32 => "i",
    UInt => "u",
    Bool => "b"
)

function typename{T, N}(io::AbstractGLIO, x::Type{GLTexture{T, N}})
    string(prescripts[eltype(T)], "sampler", N, "D")
end
function typename{T, N}(io::AbstractGLIO, x::Type{GLArray{T, N}})
    if !(N in (1, 2, 3))
        # TODO, fake ND arrays with 1D array
        error("GPUArray can't have more than 3 dimensions for now")
    end
    sz = glsl_sizeof(T)
    len = glsl_length(T)
    if true
        qualifiers = [image_format(x)]
        string("layout (", join(qualifiers, ", "), ") image$(N)D")
    else
        "image$(N)D$(len)x$(sz)_bindless"
    end
end
function typename{T <: SMatrix}(io::AbstractGLIO, ::Type{T})
    M, N = size(T)
    string(prescripts[eltype(T)], "mat", M == N ? M : string(M, "x", N))
end

function vecname{T}(io::AbstractGLIO, t::Type{T})
    N = fixed_array_length(T)
    ET = eltype(T)
    return string(prescripts[ET], "vec", N)
end

imageStore{T}(x::GLArray{T, 1}, i::int, val::NTuple{4, T}) = nothing
imageStore{T}(x::GLArray{T, 2}, i::NTuple{2, int}, val::NTuple{4, T}) = nothing

imageLoad{T}(x::GLArray{T, 1}, i::int) = ret(NTuple{4, T})
imageLoad{T}(x::GLArray{T, 2}, i::NTuple{2, int}) = ret(NTuple{4, T})

texelFetch{T}(x::GLTexture{T, 1}, i::int, lod::int) = ret(NTuple{4, T})
texelFetch{T}(x::GLTexture{T, 2}, i::NTuple{2, int}, lod::int) = ret(NTuple{4, T})

texture{T}(x::GLTexture{T, 1}, i::Float32) = ret(NTuple{4, T})
texture{T}(x::GLTexture{T, 2}, i::Vec2f0) = ret(NTuple{4, T})
texture{T}(x::GLTexture{T, 3}, i::Vec3f0) = ret(NTuple{4, T})

imageSize{T, N}(x::GLArray{T, N}) = ret(NTuple{N, int})
textureSize{T, N}(::GLTexture{T, N}) = ret(NTuple{N, int})

"""
Gradient in x direction
This is sadly a bit hard to implement for a pure CPU versions, since it's pretty much backed into the GPU hardware.
How it seems to work is, that it takes the values from neighboring registers, which work in parallel on the pixels
of the triangle, so they actually do hold the neighboring values needed to calculate the gradient.
"""
dFdx{T}(value::T) = T(0.001) # just default to a small gradient if it's called on the CPU
dFdy{T}(value::T) = T(0.001) # just default to a small gradient if it's called on the CPU

EmitVertex() = nothing
EndPrimitive() = nothing

const gl_GlobalInvocationID = Vec3f0(0, 0, 0)
const gl_FragCoord = Vec4f0(0, 0, 0, 0)

function glintrinsic{F <: Function, T <: Tuple}(f::F, types::Type{T})
    glintrinsic(f, Sugar.to_tuple(types))
end
function glintrinsic{F <: Function}(f::F, types::Tuple)
    # we rewrite Ntuples as glsl arrays, so getindex becomes inbuild
    if f == broadcast
        BF = types[1]
        if BF <: cli.Functions && all(T-> T <: Types || is_fixedsize_array(T), types[2:end])
            return true
        end
    end
    if f == getindex && length(types) == 2 && first(types) <: NTuple && last(types) <: Integer
        return true
    end
    # matmul
    if f == (*)  && length(types) == 2 && all(T-> T <: StaticArray, types)
        return true
    end
    m = methods(f)
    isempty(m) && return false
    sym = first(m).name
    (F <: cli.Functions && all(T-> T <: Types || is_fixedsize_array(T), types)) || (
        # if any intrinsic funtion stub matches
        isdefined(GLIntrinsics, sym) &&
        Base.binding_module(GLIntrinsics, sym) == GLIntrinsics &&
        length(methods(f, types)) == 1
    )
end

end # end GLIntrinsics

using .GLIntrinsics
using GeometryTypes
const gli = GLIntrinsics
import .gli: glintrinsic, GLArray, GLDeviceArray, GLTexture

import Sugar.isintrinsic

function gli.glintrinsic{T}(x::Type{T})
    T <: gli.Types ||
    is_fixedsize_array(T) ||
    T <: Tuple{Numbers} ||
    (T <: NTuple{1} && is_fixedsize_array(eltype(T))) ||
    (T <: SMatrix && all(x-> x <= 4, size(T))) ||
    T <: uchar # uchar in ints makes 0.6 segfault -.-
end
function isintrinsic(x::GLMethod)
    if isfunction(x)
        isintrinsic(Sugar.getfunction(x)) ||
        gli.glintrinsic(x.signature...)
    else
        gli.glintrinsic(x.signature)
    end
end

# copied from rewriting. TODO share implementation!

# Make constructors inbuild for now. TODO, only make default constructors inbuild
glintrinsic{T}(f::Type{T}, types::ANY) = true

# homogenous tuples, translated to glsl array
function glintrinsic{T, I}(
        f::typeof(getindex), types::Type{Tuple{T, I}}
    )
    return is_fixedsize_array(T) && I <: Union{StaticArray, Integer}
end

function glintrinsic{T <: GLDeviceArray, Val, I <: Integer}(
        f::typeof(setindex!), types::Type{Tuple{T, Val, I}}
    )
    return true
end

glintrinsic(f::typeof(tuple), types::Tuple) = true

import Base: getindex, setindex!, size

GlobalInvocationID() = gli.gl_GlobalInvocationID
FragCoord() = gli.gl_FragCoord

size{T, N}(x::gli.GLArray{T, N}) = gli.imageSize(x)
size{T, N}(x::gli.GLTexture{T, N}) = gli.textureSize(x)

getindex{T}(x::gli.GLTexture{T, 1}, i::Integer) = gli.texelFetch(x, i, 0)
getindex{T}(x::gli.GLTexture{T, 1}, i::Integer, j::Integer) = gli.texelFetch(x, (i, j), 0)

getindex{T}(x::gli.GLTexture{T, 1}, i::AbstractFloat) = gli.texture(x, i)
getindex{T}(x::gli.GLTexture{T, 2}, i::AbstractFloat, j::AbstractFloat) = gli.texture(x, Vec2f0(i, j))
getindex{T}(x::gli.GLTexture{T, 2}, idx::Vec2f0) = gli.texture(x, idx)
getindex{T}(x::gli.GLTexture{T, 3}, idx::Vec3f0) = gli.texture(x, idx)

getindex{T}(x::gli.GLArray{T, 1}, i::Integer) = gli.imageLoad(x, i)
function Base.getindex{T}(x::gli.GLArray{T, 2}, i::Integer, j::Integer)
    getindex(x, (i, j))
end
function Base.getindex{T <: Number}(x::gli.GLArray{T, 2}, idx::NTuple{2, int})
    gli.imageLoad(x, idx)[1]
end
function Base.setindex!{T}(x::gli.GLArray{T, 1}, val::T, i::Integer)
    gli.imageStore(x, i, (val, val, val, val))
end
function Base.setindex!{T}(x::gli.GLArray{T, 2}, val::T, i::Integer, j::Integer)
    setindex!(x, (val, val, val, val), (i, j))
end
function Base.setindex!{T}(x::gli.GLArray{T, 2}, val::T, idx::NTuple{2, int})
    gli.imageStore(x, idx, (val, val, val, val))
end
function Base.setindex!{T}(x::gli.GLArray{T, 2}, val::NTuple{4, T}, idx::NTuple{2, int})
    gli.imageStore(x, idx, val)
end
function Base.setindex!{T}(x::gli.GLArray{T, 1}, val::NTuple{4, T}, i::Integer)
    gli.imageStore(x, i, val)
end

function gl_erf{T <: AbstractFloat}(x::T)
    # constants
    a1 =  T(0.254829592)
    a2 = T(-0.284496736)
    a3 =  T(1.421413741)
    a4 = T(-1.453152027)
    a5 =  T(1.061405429)
    p  =  T(0.3275911)

    # Save the sign of x
    sign = 1
    if (x < 0)
        sign = -1
    end
    xabs = abs(x)
    # A&S formula 7.1.26
    t = T(1.0) / (T(1.0) + p * xabs)
    y = T(1.0) - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-xabs*xabs)

    return sign * y
end
gl_erfc{T <: AbstractFloat}(x::T) = T(1.0) - gl_erf(x)

# FMA is only supported in opengl >= 4.0 . To keep it simple, we just use this fallback for now
# until we propagate opengl versions and emit code accordingly
gl_fma{T <: AbstractFloat}(a::T, b::T, c::T) = a * b + c


type FormatHints
    width         :: Int  # column width, not the format width
    scale         :: Real
    precision     :: Int
    commas        :: Bool
    stripzeros    :: Bool
    parens        :: Bool
    rednegative   :: Bool # print in red when negative?
    hidezero      :: Bool
    alternative   :: Bool
    mixedfraction :: Bool
    suffix        :: UTF8String
    autoscale     :: Symbol
    conversion    :: ASCIIString
end

function FormatHints{T<:Integer}( ::Type{T} )
    FormatHints( 8, 1, 0, true, false, false, true, true, false, false, "", :none, "d" )
end
function FormatHints{T<:Unsigned}( ::Type{T} )
    FormatHints( 8, 1, 0, true, false, false, true, true, false, false, "", :none, "x" )
end
function FormatHints{T<:AbstractFloat}( ::Type{T} )
    FormatHints( 10, 1.0, 2, true, false, false, true, true, false, false, "", :none, "f" )
end
function FormatHints{T<:Rational}( ::Type{T} )
    FormatHints( 12, 1, 0, false, false, false, true, true, false, true, "", :none, "s" )
end
function FormatHints( ::Type{Date} )
    FormatHints( 10, 1, 0, false, false, false, false, false, false, false,"", :none,
       "yyyy-mm-dd" )
end
function FormatHints( ::Type{DateTime} )
    FormatHints( 20, 1, 0, false, false, false, false, false, false, false,"", :none,
       "yyyy-mm-dd HH:MM:SS" )
end
function FormatHints( ::Type{} )
    FormatHints( 14, 1, 0, false, false, false, true, true, false, false, "", :none, "s" )
end

function applyformat{T<:Number}( v::T, fmt::FormatHints )
    if fmt.hidezero && v == 0
        ""
    else
        format( v * fmt.scale,
            precision     = fmt.precision,
            commas        = fmt.commas,
            stripzeros    = fmt.stripzeros,
            parens        = fmt.parens,
            alternative   = fmt.alternative,
            mixedfraction = fmt.mixedfraction,
            suffix        = fmt.suffix,
            autoscale     = fmt.autoscale,
            conversion    = fmt.conversion
            )
    end
end

function applyformat( v::Union{Date,DateTime}, fmt::FormatHints )
    Dates.format( v, fmt.conversion )
end

function applyformat{T<:AbstractString}( v::T, fmt::FormatHints )
    @lintpragma( "Ignore unused fmt" )
    return v
end

function applyformat( v::AbstractArray, fmt::FormatHints )
    strs = UTF8String[]
    for s in v
        push!( strs, applyformat( s, fmt ) )
    end
    join( strs, "," )
end

function applyformat( v, fmt::FormatHints )
    @lintpragma( "Ignore unused fmt" )
    return string( v )
end


julia> julia> Inf32 == Inf64 == Inf16 == Inf
true


const MEMPOOL = [Cptr[] for i=1:30]

struct MemPoolMalloc
end

function (::MemPoolMalloc)(::Type{T}, dims::Dims{N}) where {T,N}
    bytesize = prod(dims) * sizeof(T)
    @assert bytesize > 0
    bytesize == 0 && return CuArray(Ptr{T}(C_NULL),dims,getdevice())

    c = log2ceil(bytesize) # 2^c >= bytesize
    bytesize = 1 << c
    ptrs = MEMPOOL[c]
    if isempty(ptrs)
        ptr = Ptr{T}(memalloc(bytesize))
        #status = @apicall_nocheck :cuMemAlloc (Ptr{UInt64},Csize_t) ref bytesize
        #if status != CUDA_SUCCESS
        #    gc()
        #    if isempty(buffer)
        #        dispose(mem)
        #        dptr = memalloc(bytesize)
        #    else
        #        dptr = pop!(buffer)
        #    end
        #else
        #    dptr = ref[]
        #end
    else
        ptr = Ptr{T}(pop!(ptrs))
    end
    arr = CuArray(ptr, dims, getdevice())
    function release(x::CuArray{T}) where T
        bytesize = prod(size(x)) * sizeof(T)
        c = log2ceil(bytesize)
        push!(MEMPOOL[c], Cptr(x.ptr))
    end
    finalizer(release, arr)
    arr
end

function dispose(x::MemPoolMalloc)
    gc()
    for dptrs in x.dptrs
        for dptr in dptrs
            memfree(dptr)
        end
    end
end

function log2ceil(bytesize::Int)
    @assert bytesize > 0
    bytesize -= 1
    x = 1
    while bytesize > 1
        bytesize >>= 1
        x += 1
    end
    x
end


"""
`init_subspec!(m::Model1002)`

Initializes a model subspecification by overwriting parameters from
the original model object with new parameter objects. This function is
called from within the model constructor.
"""
function init_subspec!(m::Model1002)
    if subspec(m) == "ss2"
        return
    elseif subspec(m) == "ss8"
        return ss8!(m)
    elseif subspec(m) == "ss9"
        return ss9!(m)
    elseif subspec(m) == "ss10"
        return ss10!(m)
    elseif subspec(m) == "ss11"
        # Normalize s.t. sum of variances of anticipated shocks equals the
        # variance of the contemporaneous shock
        # See measurement equation
        return ss10!(m)
    else
        error("This subspec is not defined.")
    end
end

"""
```
ss8!(m::Model1002)
```

Initializes subspec 8 of `Model1002`. In this subspecification, γ_gdi
and δ_gdi are fixed at 1 and 0 respectively.
"""
function ss8!(m::Model1002)
    m <= parameter(:γ_gdi, 1., (-10., 10.), (-10., -10.), Untransformed(), Normal(1., 2.), fixed = true,
                   tex_label = "\\gamma_{gdi}")

    m <= parameter(:δ_gdi, 0., (-10., 10.), (-10., -10.), Untransformed(), Normal(0.00, 2.), fixed = true,
                   tex_label = "\\delta_{gdi}")
end

"""
```
ss9!(m::Model1002)
```

Initializes subspec 9 of `Model1002`. This subspecification is ss8 + Iskander's
changes + the bounds for beta-distributed parameters have been changed from
(1e-5, 0.99) to (0.0, 1.0).
"""
function ss9!(m::Model1002)

    ss8!(m)

    m <= parameter(:ζ_p, 0.8940, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.1), fixed = false,
                   description = "ζ_p: The Calvo parameter. In every period, intermediate goods producers optimize prices with probability (1-ζ_p). With probability ζ_p, prices are adjusted according to a weighted average of the previous period's inflation (π_t1) and steady-state inflation (π_star).",
                   tex_label = "\\zeta_p")

    m <= parameter(:ι_p, 0.1865, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.15), fixed = false,
                   description = "ι_p: The weight attributed to last period's inflation in price indexation. (1-ι_p) is the weight attributed to steady-state inflation.",
                   tex_label = "\\iota_p")

    m <= parameter(:h, 0.5347, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.7, 0.1), fixed = false,
                   description = "h: Consumption habit persistence.",
                   tex_label = "h")

    m <= parameter(:ppsi, 0.6862, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.15), fixed = false,
                   description = "ppsi: Utilization costs.",
                   tex_label = "\\psi")

    m <= parameter(:ζ_w, 0.9291, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.1), fixed = false,
                   description = "ζ_w: (1-ζ_w) is the probability with which households can freely choose wages in each period. With probability ζ_w, wages increase at a geometrically weighted average of the steady state rate of wage increases and last period's productivity times last period's inflation.",
                   tex_label = "\\zeta_w")

    m <= parameter(:ι_w, 0.2992, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.15), fixed = false,
                   description = "ι_w: The weight attributed to last period's wage in wage indexation. (1-ι_w) is the weight attributed to steady-state wages.",
                   tex_label = "\\iota_w")

    m <= parameter(:ρ, 0.7126, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.75, 0.10), fixed = false,
                   description = "ρ: The degree of inertia in the monetary policy rule.",
                   tex_label = "\\rho_R")

    # Financial frictions parameters
    m <= parameter(:Fω, 0.0300, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.03, 0.01), fixed = true,
                   scaling = x -> 1 - (1-x)^0.25,
                   description = "F(ω): The cumulative distribution function of ω (idiosyncratic iid shock that increases or decreases entrepreneurs' capital).",
                   tex_label = "F(\\bar{\\omega})")

    m <= parameter(:ζ_spb, 0.0559, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.05, 0.005), fixed = false,
                   description = "ζ_spb: The elasticity of the expected exess return on capital (or 'spread') with respect to leverage.",
                   tex_label = "\\zeta_{sp,b}")

    m <= parameter(:γ_star, 0.9900, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.99, 0.002),
                   fixed = true,
                   description = "γ_star: Fraction of entrepreneurs who survive and continue operating for another period.",
                   tex_label = "\\gamma_*")

    m <= parameter(:ρ_g, 0.9863, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_g: AR(1) coefficient in the government spending process.",
                   tex_label = "\\rho_g")

    m <= parameter(:ρ_b, 0.9410, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_b: AR(1) coefficient in the intertemporal preference shifter process.",
                   tex_label = "\\rho_b")

    m <= parameter(:ρ_μ, 0.8735, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_μ: AR(1) coefficient in capital adjustment cost process.",
                   tex_label = "\\rho_{\\mu}")

    m <= parameter(:ρ_z, 0.9446, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_z: AR(1) coefficient in the technology process.",
                   tex_label = "\\rho_z")

    m <= parameter(:ρ_λ_f, 0.8827, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_λ_f: AR(1) coefficient in the price mark-up shock process.",
                   tex_label = "\\rho_{\\lambda_f}")

    m <= parameter(:ρ_λ_w, 0.3884, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_λ_w: AR(1) coefficient in the wage mark-up shock process.",
                   tex_label = "\\rho_{\\lambda_w}")

    m <= parameter(:ρ_rm, 0.2135, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_rm: AR(1) coefficient in the monetary policy shock process.",
                   tex_label = "\\rho_{r^m}")

    m <= parameter(:ρ_σ_w, 0.9898, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.75, 0.15), fixed = false,
                   description = "ρ_σ_w: The standard deviation of entrepreneurs' capital productivity follows an exogenous process with mean ρ_σ_w. Innovations to the process are called _spread shocks_.",
                   tex_label = "\\rho_{\\sigma_\\omega}")

    m <= parameter(:ρ_μ_e, 0.7500, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.75, 0.15), fixed = true,
                   description = "ρ_μ_e: AR(1) coefficient in the exogenous bankruptcy cost process.",
                   tex_label = "\\rho_{\\mu_e}")

    m <= parameter(:ρ_γ, 0.7500, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.75, 0.15), fixed = true,
                   description = "ρ_γ: AR(1) coefficient in the process describing the fraction of entrepreneurs surviving period t.",
                   tex_label = "\\rho_{\\gamma}")

    m <= parameter(:ρ_π_star, 0.9900, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = true,
                   description = "ρ_π_star: AR(1) coefficient in the time-varying inflation target process.",
                   tex_label = "\\rho_{\\pi_*}")

    m <= parameter(:ρ_lr, 0.6936, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   tex_label = "\\rho_{10y}")

    m <= parameter(:ρ_z_p, 0.8910, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   description = "ρ_z_p: AR(1) coefficient in the process describing the permanent component of productivity.",
                   tex_label = "\\rho_{z^p}")

    m <= parameter(:ρ_tfp, 0.1953, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   tex_label = "\\rho_{tfp}")

    m <= parameter(:ρ_gdpdef, 0.5379, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   tex_label = "\\rho_{gdpdef}")

    m <= parameter(:ρ_corepce, 0.2320, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.5, 0.2), fixed = false,
                   tex_label = "\\rho_{pce}")

    m <= parameter(:η_gz, 0.8400, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.50, 0.20), fixed = false,
                   description = "η_gz: Correlate g and z shocks.",
                   tex_label = "\\eta_{gz}")

    m <= parameter(:η_λ_f, 0.7892, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.50, 0.20), fixed = false,
                   tex_label = "\\eta_{\\lambda_f}")

    m <= parameter(:η_λ_w, 0.4226, (0.0, 1.0), (0.0, 1.0), SquareRoot(), BetaAlt(0.50, 0.20), fixed = false,
                   description = "η_λ_w: AR(2) coefficient on wage markup shock process.",
                   tex_label = "\\eta_{\\lambda_w}")

    m <= parameter(:Iendoα, 0.0000, (0.0, 1.0), (0.0, 0.0), Untransformed(), BetaAlt(0.50, 0.20), fixed = true,
                   description = "Iendoα: Indicates whether to use the model's endogenous α in the capacity utilization adjustment of total factor productivity.",
                   tex_label = "I\\{\\alpha^{model}\\}")
end

"""
```
ss10!(m::Model1002)
```

Initializes subspec 10 of `Model1002`. This subspecification is the same as ss9,
except `betabar` is defined with `m[:σ_c]` instead of `σ_ω_star`.
"""
function ss10!(m::Model1002)
    ss9!(m)
end


function construct_services!(canonical::Canonical,
                             sys::PSY.System,
                             services_template::Dict{Symbol,ServiceModel};
                             kwargs...)
    isempty(services_template) && return
    expression_list = Vector{Symbol}()
    for service_model in values(services_template)
        services = PSY.get_components(service_model.service_type, sys)
        for service in services
            add_service!(canonical, service, service_model, expression_list; kwargs...)
        end
    end
    # post constraints in expression dict
    return
end

function add_service!(canonical::Canonical, service::SR,
                      model::ServiceModel{SR, RangeUpwardReserve},
                      expression_list::Vector{Symbol};
                      kwargs...) where {SR<:PSY.Reserve}
        devices = PSY.get_contributingdevices(service)
        #Variables
        activeservice_variables!(canonical, service, devices)
        #requirement constraint Constraints
        service_requirement_constraint!(canonical, service)
        # add to
        add_to_service_expression!(canonical, model, service, expression_list)
    return
end


"""
TODO: Provide description of crossover module.

RULE: Number of inputs must be greater or equal to the number of outputs.
"""
module crossover
importall common, variation
using core, utility, representation, individual, species
export Crossover, CrossoverDefinition, num_required

"""
Base type used by all crossover operation definitions.
"""
abstract CrossoverDefinition <: VariationDefinition

"""
Base type used all by crossover operators.

All crossover operators MUST implement a +stage+ property, specifying the name
of the developmental stage that this operator acts upon.
"""
abstract Crossover <: Variation

operate!(c::Crossover, srcs::IndividualCollection, n::Int) =
  operate!(c, srcs.stages[c.stage], n)

function operate!{T}(c::Crossover, srcs::Vector{IndividualStage{T}}, n::Int)
  n_in = num_inputs(c)
  n_out = num_outputs(c)
  n_calls = ceil(n / n_out)
  n_in_from = n_in_to = n_out_from = n_out_to = 0
  for i = 1:n_calls
    # Calculate range of input chromosomes.
    n_in_from = n_in_to + 1
    n_in_to = n_in_to + n_in

    # Calculate range of output chromosomes.
    n_out_from = n_out_to + 1
    n_out_to = n_out_to + n_out

    operate!(c, srcs[n_out_from:n_out_to], srcs[n_in_from:n_in_to])
  end
end

function operate!{T}(
  c::Crossover,
  outputs::Vector{IndividualStage{T}},
  inputs::Vector{IndividualStage{T}}
)
  # Ensure that all the provided inputs are in a valid state.
  # If not, leave the output buffer unchanged.
  try; operate!(c, outputs, map(get, inputs)...); end
end

"""
By default, crossover operators accept two chromosomes as input.
"""
num_inputs(::Crossover) = 2

"""
By default, crossover operators produce two chromosomes as output.
"""
num_outputs(::Crossover) = 2

"""
Calculates the number of (internal) calls required to this operator to
produce a desired number of offspring.
"""
num_calls_required(c::Crossover, n::Int) = convert(Int, ceil(n / num_outputs(c)))

"""
Calculates the number of input chromosomes required to this operator in order
to produce a specified number of offspring.
"""
num_required(c::Crossover, n::Int) = num_calls_required(c, n) * num_inputs(c)

# Include each of the crossover operators.
include("crossover/null.jl")
include("crossover/one_point.jl")
end


fname = :ST_ARCsparse
c = Combi(hessian_sparse,PDataST,solve_modelST_ARC,preprocessST,decreaseGen,TparamsST())
include("Template.jl")


cube, angles = BetaPictoris[:cube, :pa]
cube .-= minimum(cube) # rescale so min is > 0
av = AnnulusView(cube; inner=10, outer=20)
mav = MultiAnnulusView(cube, 5)

@testset "Annulus - $(nameof(typeof(alg)))" for alg in [PCA(10), Classic(), NMF(3), GreeDS(3)]
    res = alg(av, angles)
    @test size(res) == (101, 101)
    if !(alg isa NMF)
        res_rdi = alg(av, angles; ref=av)
        @test res ≈ res_rdi
        if alg isa GreeDS
            @test_throws TypeError alg(av, angles; ref=cube)
        else
            @test_throws ErrorException alg(av, angles; ref=cube)
        end
    end

    if alg isa GreeDS
        S = ADI.fit(alg, av; angles=angles) |> reconstruct
    else
        S = ADI.fit(alg, av) |> reconstruct
    end
    X = av()
    @test size(S) == size(X) != size(flatten(cube))
end

# TODO can't test NMF because it's non-deterministic somehow????
@testset "MultiAnnulus - $(nameof(typeof(alg)))" for alg in [PCA(10), Classic()]
    res = alg(mav, angles)
    @test size(res) == (101, 101)
    res_rdi = alg(mav, angles; ref=mav)
    @test res ≈ res_rdi
    @test_throws ErrorException alg(mav, angles; ref=cube)

    des = ADI.fit(alg, mav)
    N = length(mav.indices)
    recons = reconstruct(des)
    @test length(des) == length(recons) == N
    S = reconstruct(alg, mav)
    @test S ≈ inverse(mav, recons)

    # test vector algs
    S2 = reconstruct(fill(alg, N), mav)
    @test S ≈ S2
    S3 = reconstruct(fill(alg, N), mav; ref=mav)
    @test S ≈ S3

    # make sure ref is same type
    @test_throws ErrorException reconstruct(fill(alg, N), mav; ref=cube)
end

@testset "av - framewise - $(nameof(typeof(alg)))" for alg in [PCA(10), NMF(2), Classic()]
    fr_alg = Framewise(alg)
    # gets radius automatically
    S1 = reconstruct(fr_alg, av; angles=angles, fwhm=4.7)
    S2 = reconstruct(fr_alg, av; angles=angles, r=15, fwhm=4.7)
    @test S1 ≈ S2
end

@testset "mav - framewise - $(nameof(typeof(alg)))" for alg in [PCA(10), NMF(2), Classic()]
    fr_alg = Framewise(alg)
    # gets fwhm automatically
    S1 = reconstruct(fr_alg, mav; angles=angles)
    S2 = reconstruct(fr_alg, mav; angles=angles, fwhm=5)
    @test S1 ≈ S2

    # test repeated
    k = length(mav.indices)
    algs = Framewise(fill(alg, k))
    S3 = reconstruct(algs, mav; angles=angles)
    @test S1 ≈ S3

    # test delta rot cases
    fr_alg = Framewise(alg, delta_rot=(0.1, 1))
    fr_alg2 = Framewise(alg, delta_rot=range(0.1, 1, length=k))
    S1 = reconstruct(fr_alg, mav; angles=angles)
    S2 = reconstruct(fr_alg2, mav; angles=angles)
    @test S1 ≈ S2
end


using MolecularGaussians
using GaussianMixtureAlignment
using MolecularGraph
using MolecularGraph.Graph
using LinearAlgebra
using StaticArrays
using CoordinateTransformations
using Optim
using Test

using GaussianMixtureAlignment: distance

const MG = MolecularGaussians

@testset "Partial Charge" begin
    mol = sdftomol(joinpath(@__DIR__, "..", "data", "E1050_3d.sdf"))
    # adding partial charges to attributes
    @test_throws KeyError pc = mol.attributes[:partialcharges]
    partialcharges!(mol)
    @test typeof(mol.attributes[:partialcharges]) <: Dict
    # A sulphur atom should have the most positive partial charge
    @test nodeattr(mol,findmax(partialcharges!(mol))[2]).symbol == :S
    # An oxygen atom should have the most negative partial charge
    @test nodeattr(mol,findmin(partialcharges!(mol))[2]).symbol == :O
end

@testset "Van der Waals radii" begin
    mol = sdftomol(joinpath(@__DIR__, "..", "data", "E1050_3d.sdf"))
    # adding radii to attributes
    @test_throws KeyError pc = mol.attributes[:vdwradii]
    vdwradii!(mol)
    @test typeof(mol.attributes[:vdwradii]) <: Dict
    # All atoms should have the appropriate radius
    for idx in nodeset(mol)
        @test vdwradii!(mol)[idx] == MolecularGraph.ATOM_VANDERWAALS_RADII[atomnumber(nodeattr(mol,idx).symbol)]
    end
end

@testset "Gaussian Mixture Distance" begin
    mol = sdftomol(joinpath(@__DIR__, "..", "data", "E1050_3d.sdf"))
    gonane = sdftomol(joinpath(@__DIR__, "..", "data", "gonane5α.sdf"))
    # identical mixture models have no distance
    mol_gmm = MolGMM(mol)
    gonane_gmm = MolGMM(gonane)
    @test abs(distance(mol_gmm, mol_gmm)) < 1e-12
    @test abs(distance(gonane_gmm, gonane_gmm)) < 1e-12
    # different mixture models have some distance
    @test distance(mol_gmm, gonane_gmm) > 0.1
    # different transforms of a molecule have some distance
    gmm= MolGMM(mol)
    tform = AffineMap(10*rand(6)...)
    @test distance(tform(gmm), gmm) > 0.1
    # subgraphs of a molecule have some distance
    submol = nodesubgraph(mol, collect(nodeset(mol))[1:Int(floor(end/2))])
    sub_gmm = MolGMM(submol)
    @test distance(sub_gmm, gmm) > 0.1
end

@testset "MolGMM and PharmacophoreGMM alignment" begin
    ## MolGMM alignment
    mol1= sdftomol(joinpath(@__DIR__, "..", "data", "E1050_3d.sdf"))
    mol2 = sdftomol(joinpath(@__DIR__, "..", "data", "E1103_3d.sdf"))
    molgmm1 = MolGMM(mol1)
    molgmm2 = MolGMM(mol2)
    gonane = sdftomol(joinpath(@__DIR__, "..", "data", "gonane5α.sdf"))
    # No rotation/translation when aligning a mol to itself
    self_res = tiv_gogma_align(molgmm1, molgmm1, 0.5, 0.5; maxstagnant=1000)
    self_tform = AffineMap(self_res.tform_params...)
    @test norm(self_tform.translation) ≈ 0 atol=1e-6
    @test self_tform.linear ≈ I
    # Do you get similar distances when performing the alignment in both directions?
    f_ovrlp = tiv_gogma_align(molgmm1, molgmm2, 0.5, 0.5; maxstagnant=1000).upperbound
    b_ovrlp = tiv_gogma_align(molgmm2, molgmm1, 0.5, 0.5; maxstagnant=1000).upperbound
    @test abs(2*(f_ovrlp-b_ovrlp)/(f_ovrlp+b_ovrlp)) < 0.05     # 5% difference too forgiving?
    # Do you get the inverse transformation when swapping molfixed and molmoving (requires more evals)?
    # This may not be important for generating a distance matrix for many molecules
    # f_res= tiv_gogma_align(PharmacophoreGMM(mol1), PharmacophoreGMM(mol2); maxstagnant=1000)
    # f_tform = AffineMap(f_res.tform_params...)
    # b_res = tiv_gogma_align(PharmacophoreGMM(mol2), PharmacophoreGMM(mol1); maxstagnant=1000)
    # b_tform = AffineMap(b_res.tform_params...)
    # @test (b_tform ∘ f_tform).linear ≈ I atol=0.1
    # @test (b_tform ∘ f_tform).tranlsation ≈ zeros(3) atol=0.1
end


# License for this file: MIT (expat)
# Copyright 2017-2018, DLR Institute of System Dynamics and Control
#
# This file is part of module
#   ModiaMath.DAE (ModiaMath/DAE/_module.jl)
#

# Provide functions to handle time and state events in user models.
@eval using Printf

mutable struct EventHandler
   # Input values for the event functions
    time::Float64              # Current simulation time
    initial::Bool              # = true, if model is called at initialization
                              #         (if initial, event=true)
    terminal::Bool             # = true, if model is called for termination (close files, streams, visualization, ...)
    event::Bool                # = true, if model is called at an event
    afterSimulationStart::Bool # = true, if model is called after simulation start
    crossing::Bool             # = true, if model is called to compute crossing function

    # Computed by the event functions
    # For time events:
    maxTime::Float64        # Integrate at most up to maxTime
                            # (if integrateToEvent == true, maxTime = nextEventTime,
                            #  otherwise maxTime > nextEventTime)
    nextEventTime::Float64  # Next time event instant (= realmax(Time) if no event)
    integrateToEvent::Bool  # = true, if integrator shall integrate to nextEventTime
                            # = false, if integrator can integrate beyond nextEventTime
    restart::EventRestart   # Type of integrator restart at current event
    newEventIteration::Bool   # = true, if another event iteration; = false, if no event iteration anymore
    firstEventIteration::Bool # = true, if first iteration at an event.

   # For state events:
    nz::Int                 # Number of event indicators
    z::Vector{Float64}      # Vector of event indicators (zero crossings). If one of z[i] passes
                            # zero, that is beforeEvent(z[i])*z[i] < 0, an event is triggered
                            # (allocated during instanciation according to nz).
    zPositive::Vector{Bool} # = true if z > 0 at the last event instant, otherwise false
    zDir::Vector{Cint}      # zDir[i] =  0: Root is reported for both crossing directions
                            #         =  1: Root is reported when crossing from negative to positive direction
                            #         = -1: Root is reported when crossing from positive to negative direction

    # Logger
    logger::ModiaMath.Logger

    function EventHandler(;nz::Int=0)
        @assert(nz >= 0)
        new(0.0, false, false, false, false, false, floatmax(Float64), floatmax(Float64),
            true, NoRestart, false, false, nz, ones(nz), fill(false, nz), fill(0, nz))
    end
end

positiveCrossingAsString(positive::Bool) = positive ? " (became > 0)" : " (became <= 0)"
negativeCrossingAsString(negative::Bool) = negative ? " (became < 0)" : " (became >= 0)"

function initEventIteration!(h::EventHandler, t::Float64)
    h.time          = t
    h.restart       = NoRestart
    h.maxTime       = floatmax(Float64)
    h.nextEventTime = floatmax(Float64)
    h.newEventIteration   = false
    h.firstEventIteration = true
end

function terminateEventIteration!(h::EventHandler)
    result                = h.initial ? false : !h.newEventIteration
    h.firstEventIteration = false
    h.newEventIteration   = false
    return result
end

isInitial(h::EventHandler)              = h.initial
isTerminal(h::EventHandler)             = h.terminal
isEvent(h::EventHandler)                = h.event
isAfterSimulationStart(h::EventHandler) = h.afterSimulationStart
isEventIndicator(h::EventHandler) = h.crossing
const zEps = 1.e-14

function setNextEvent!(h::EventHandler, nextEventTime::Float64;
                       integrateToEvent::Bool=true,
                       restart::EventRestart=Restart)
    if (h.event && nextEventTime > h.time) || (h.initial && nextEventTime >= h.time)
        if integrateToEvent
            h.maxTime = min(h.maxTime, nextEventTime)
        end
        if nextEventTime < h.nextEventTime
            h.nextEventTime    = nextEventTime
            h.integrateToEvent = integrateToEvent
            if ModiaMath.isLogEvents(h.logger)
                @printf("        nextEventTime = %.6g s, integrateToEvent = %s\n",
                   nextEventTime, integrateToEvent ? "true" : "false")
            end
        end
        h.restart = max(h.restart, restart)
    end
    return nothing
end

function positive!(h::EventHandler, nr::Int, crossing::Float64, crossingAsString::String;
                   restart::EventRestart=Restart)::Bool
    if h.initial
        h.zPositive[nr] = crossing > 0.0
        if ModiaMath.isLogEvents(h.logger)
            println("        ", crossingAsString, " = ", crossing, positiveCrossingAsString(h.zPositive[nr]))
        end

    elseif h.event
        # println("... nr = ", nr, ", crossing = ", crossing)
        new_zPositive = crossing > 0.0
        change = (h.zPositive[nr] && !new_zPositive) || (!h.zPositive[nr] && new_zPositive)
        h.zPositive[nr] = new_zPositive

        if change
            h.restart = max(h.restart, restart)
            if ModiaMath.isLogEvents(h.logger)
                println("        ", crossingAsString, " = ", crossing, positiveCrossingAsString(h.zPositive[nr]))
            end
            h.newEventIteration = true
        end
    end
    h.z[nr] = crossing + (h.zPositive[nr] ? zEps : -zEps)

    return h.zPositive[nr]
end

function negative!(h::EventHandler, nr::Int, crossing::Float64, crossingAsString::String;
                   restart::EventRestart=Restart)::Bool
    if h.initial
        h.zPositive[nr] = crossing >= 0.0
        if ModiaMath.isLogEvents(h.logger)
            println("        ", crossingAsString, " = ", crossing, negativeCrossingAsString(!h.zPositive[nr]))
        end

    elseif h.event
        new_zPositive = crossing >= 0.0
        change = (h.zPositive[nr] && !new_zPositive) || (!h.zPositive[nr] && new_zPositive)
        h.zPositive[nr] = new_zPositive

        if change
            h.restart = max(h.restart, restart)
            if ModiaMath.isLogEvents(h.logger)
                println("        ", crossingAsString, " = ", crossing, negativeCrossingAsString(!h.zPositive[nr]))
            end
            h.newEventIteration = true
        end
    end
    h.z[nr] = crossing + (h.zPositive[nr] ? zEps : -zEps)

    return !h.zPositive[nr]
end

function change!(h::EventHandler, nr::Int, crossing::Float64, crossingAsString::String;
                 restart::EventRestart=Restart)::Bool
    h.z[nr] = crossing
    if h.initial
        h.zPositive[nr] = crossing > 0.0
        if ModiaMath.isLogEvents(h.logger)
            println("        ", crossingAsString, " = ", crossing, positiveCrossingAsString(h.zPositive[nr]))
        end
        return false

    elseif h.event
        new_zPositive = crossing > 0.0
        change = (h.zPositive[nr] && !new_zPositive) || (!h.zPositive[nr] && new_zPositive)
        h.zPositive[nr] = new_zPositive

        if change
            h.restart = max(h.restart, restart)
            if ModiaMath.isLogEvents(h.logger)
                println("        ", crossingAsString, " = ", crossing, positiveCrossingAsString(h.zPositive[nr]))
            end
            h.newEventIteration = true
            return true
        end
    end

    return false
end

function edge!(h::EventHandler, nr::Int, crossing::Float64, crossingAsString::String;
               restart::EventRestart=Restart)::Bool
    h.z[nr] = crossing

    if h.initial
        h.zPositive[nr] = crossing > 0.0
        h.zDir[nr] = 1

        if ModiaMath.isLogEvents(h.logger)
            println("        ", crossingAsString, " = ", crossing, positiveCrossingAsString(h.zPositive[nr]))
        end
        return false

    elseif h.event
        new_zPositive = crossing > 0.0
        edge = !h.zPositive[nr] && new_zPositive
        h.zPositive[nr] = new_zPositive

        if edge
            h.restart = max(h.restart, restart)
            if ModiaMath.isLogEvents(h.logger)
                println("        ", crossingAsString, " = ", crossing, " (became > 0)")
            end
            h.newEventIteration = true
            return true
        end

    elseif h.crossing && h.zPositive[nr] && crossing < 0.0
        # Eventually need to improve later
        # (need to check, whether h.get_z is good enough to switch h.zPositive)
        h.zPositive[nr] = false
    end
    return false
end


"""
    Options

Options configurable by the user with recommended default values.
"""
@with_kw struct Options
    num_start_samples::Int = 5
    trace::Bool = true
    sampling_plan_opt_gens::Int = 5000
    rippa::Bool = true
    kerns = [ScatteredInterpolation.Gaussian, ScatteredInterpolation.InverseQuadratic,
             ScatteredInterpolation.InverseMultiquadratic]
    rbf_opt_gens::Int = 50_000
    rbf_opt_pop::Int = 50
    rbf_opt_method::Symbol = :de_rand_1_bin_radiuslimited
    rbf_dist_metric = Distances.Euclidean()
    variable_kernel_width::Bool = true
    variable_dim_scaling::Bool = true
    cond_max::Float64 = 1e4
    max_rbf_width::Float64 = 1000.0
    min_rbf_width::Float64 = 1e-4
    max_scale::Float64 = 10.0
    min_scale::Float64 = 1e-4
    num_interpolants::Int = 20
    smooth = :single
    max_smooth::Float64 = 1.0
    smooth_user::Float64 = 0.0
    iterations::Int64 = 10
    num_infill_points::Int64 = 1
    parallel_surrogate::Bool = true
    infill_funcs::Array{Symbol,1} = [:median,:std]
    infill_iterations::Int64 = 25_000

    @assert ((smooth == false) || (smooth == :variable) ||
    (smooth == :single) || (smooth == :single_user)) "Not supported option for smooth"
end

"""
    RBFHypers(width::S,kernelFunc,scaling::U,fitness::Float64)

Data structure to store results from the optimization of an RBF interpolation kernel
"""
struct RBFHypers{T,U}
    kernelFunc
    scaling::T
    smooth::U
end

"""
    SurrogateResult

Data structure to store the results of an optimization task.
"""
struct SurrogateResult
    lhc_samples::Array{Float64,2}
    lhc_plan::Array{Float64,2}
    sm_interpolant
    sm_interpolant_settings::Array{SurrogateModelOptim.RBFHypers{T,U},1} where U where T
    infill_samples::Array{Float64,2}
    infill_type::Array{Symbol,1}
    infill_plan::Array{Float64,2}
    infill_prediction::Array{Float64,1}
    options::Options
end