text:
# Copyright (c) 2015 Dahua Lin, Miles Lubin, Joey Huchette, Iain Dunning, and
#   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.

# ==============================================================================
#    Generic Callbacks in Gurobi
# ==============================================================================

mutable struct CallbackData
    model::Optimizer
    ptr::Ptr{Cvoid}
    cb_where::Cint
end
Base.cconvert(::Type{Ptr{Cvoid}}, x::CallbackData) = x
Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::CallbackData) = x.ptr::Ptr{Cvoid}
Base.broadcastable(x::CallbackData) = Ref(x)

mutable struct _CallbackUserData
    model::Optimizer
    callback::Function
end
Base.cconvert(::Type{Ptr{Cvoid}}, x::_CallbackUserData) = x
function Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::_CallbackUserData)
    return pointer_from_objref(x)::Ptr{Cvoid}
end

function _gurobi_callback_wrapper(
    p_model::Ptr{Cvoid},
    cb_data::Ptr{Cvoid},
    cb_where::Cint,
    p_user_data::Ptr{Cvoid},
)
    user_data = unsafe_pointer_to_objref(p_user_data)::_CallbackUserData
    try
        user_data.callback(
            CallbackData(user_data.model, cb_data, cb_where),
            cb_where,
        )
    catch ex
        GRBterminate(p_model)
        if !(ex isa InterruptException)
            rethrow(ex)
        end
    end
    return Cint(0)
end

"""
    CallbackFunction()

Set a generic Gurobi callback function.

Callback function should be of the form

    callback(cb_data::CallbackData, cb_where::Cint)

Note: before accessing `MOI.CallbackVariablePrimal`, you must call
`Gurobi.load_callback_variable_primal(cb_data, cb_where)`.
"""
struct CallbackFunction <: MOI.AbstractCallback end

function MOI.set(model::Optimizer, ::CallbackFunction, f::Function)
    grb_callback = @cfunction(
        _gurobi_callback_wrapper,
        Cint,
        (Ptr{Cvoid}, Ptr{Cvoid}, Cint, Ptr{Cvoid})
    )
    user_data = _CallbackUserData(
        model,
        (cb_data, cb_where) -> begin
            model.callback_state = _CB_GENERIC
            f(cb_data, cb_where)
            model.callback_state = _CB_NONE
            return
        end,
    )
    ret = GRBsetcallbackfunc(model, grb_callback, user_data)
    _check_ret(model, ret)
    # We need to keep a reference to the callback function so that it isn't
    # garbage collected.
    model.generic_callback = user_data
    model.has_generic_callback = true
    # Mark the update as necessary and immediately call for the update.
    _require_update(model)
    _update_if_necessary(model)
    return
end
MOI.supports(::Optimizer, ::CallbackFunction) = true

"""
    load_callback_variable_primal(cb_data, cb_where)

Load the solution during a callback so that it can be accessed using
`MOI.CallbackVariablePrimal`.
"""
function load_callback_variable_primal(cb_data::CallbackData, cb_where::Cint)
    resize!(
        cb_data.model.callback_variable_primal,
        length(cb_data.model.variable_info),
    )
    if cb_where == GRB_CB_MIPNODE
        ret = GRBcbget(
            cb_data,
            cb_where,
            GRB_CB_MIPNODE_REL,
            cb_data.model.callback_variable_primal,
        )
        _check_ret(cb_data.model, ret)
    elseif cb_where == GRB_CB_MIPSOL
        ret = GRBcbget(
            cb_data,
            cb_where,
            GRB_CB_MIPSOL_SOL,
            cb_data.model.callback_variable_primal,
        )
        _check_ret(cb_data.model, ret)
    else
        error(
            "`load_callback_variable_primal` can only be called at " *
            "GRB_CB_MIPNODE or GRB_CB_MIPSOL.",
        )
    end
    return
end

# ==============================================================================
#    MOI callbacks
# ==============================================================================

function _default_moi_callback(model::Optimizer)
    return (cb_data, cb_where) -> begin
        if cb_where == GRB_CB_MIPSOL
            load_callback_variable_primal(cb_data, cb_where)
            if model.lazy_callback !== nothing
                model.callback_state = _CB_LAZY
                model.lazy_callback(cb_data)
            end
        elseif cb_where == GRB_CB_MIPNODE
            resultP = Ref{Cint}()
            GRBcbget(cb_data, cb_where, GRB_CB_MIPNODE_STATUS, resultP)
            if resultP[] != GRB_OPTIMAL
                return  # Solution is something other than optimal.
            end
            load_callback_variable_primal(cb_data, cb_where)
            if model.user_cut_callback !== nothing
                model.callback_state = _CB_USER_CUT
                model.user_cut_callback(cb_data)
            end
            if model.heuristic_callback !== nothing
                model.callback_state = _CB_HEURISTIC
                model.heuristic_callback(cb_data)
            end
            if model.lazy_callback !== nothing
                model.callback_state = _CB_LAZY
                model.lazy_callback(cb_data)
            end
        end
        model.callback_state = _CB_NONE
    end
end

function MOI.get(
    model::Optimizer,
    ::MOI.CallbackVariablePrimal{CallbackData},
    x::MOI.VariableIndex,
)
    return model.callback_variable_primal[_info(model, x).column]
end

function MOI.get(::Optimizer, attr::MOI.CallbackNodeStatus{CallbackData})
    if attr.callback_data.cb_where == GRB_CB_MIPSOL
        return MOI.CALLBACK_NODE_STATUS_INTEGER
    elseif attr.callback_data.cb_where == GRB_CB_MIPNODE
        return MOI.CALLBACK_NODE_STATUS_FRACTIONAL
    end
    return MOI.CALLBACK_NODE_STATUS_UNKNOWN
end

# ==============================================================================
#    MOI.LazyConstraint
# ==============================================================================

function MOI.set(model::Optimizer, ::MOI.LazyConstraintCallback, cb::Function)
    MOI.set(model, MOI.RawOptimizerAttribute("LazyConstraints"), 1)
    model.lazy_callback = cb
    return
end
MOI.supports(::Optimizer, ::MOI.LazyConstraintCallback) = true

function MOI.submit(
    model::Optimizer,
    cb::MOI.LazyConstraint{CallbackData},
    f::MOI.ScalarAffineFunction{Float64},
    s::Union{
        MOI.LessThan{Float64},
        MOI.GreaterThan{Float64},
        MOI.EqualTo{Float64},
    },
)
    if model.callback_state == _CB_USER_CUT
        throw(MOI.InvalidCallbackUsage(MOI.UserCutCallback(), cb))
    elseif model.callback_state == _CB_HEURISTIC
        throw(MOI.InvalidCallbackUsage(MOI.HeuristicCallback(), cb))
    elseif !iszero(f.constant)
        throw(
            MOI.ScalarFunctionConstantNotZero{Float64,typeof(f),typeof(s)}(
                f.constant,
            ),
        )
    end
    indices, coefficients = _indices_and_coefficients(model, f)
    sense, rhs = _sense_and_rhs(s)
    ret = GRBcblazy(
        cb.callback_data,
        length(indices),
        indices,
        coefficients,
        sense,
        rhs,
    )
    _check_ret(model, ret)
    return
end
MOI.supports(::Optimizer, ::MOI.LazyConstraint{CallbackData}) = true

# ==============================================================================
#    MOI.UserCutCallback
# ==============================================================================

function MOI.set(model::Optimizer, ::MOI.UserCutCallback, cb::Function)
    model.user_cut_callback = cb
    return
end
MOI.supports(::Optimizer, ::MOI.UserCutCallback) = true

function MOI.submit(
    model::Optimizer,
    cb::MOI.UserCut{CallbackData},
    f::MOI.ScalarAffineFunction{Float64},
    s::Union{
        MOI.LessThan{Float64},
        MOI.GreaterThan{Float64},
        MOI.EqualTo{Float64},
    },
)
    if model.callback_state == _CB_LAZY
        throw(MOI.InvalidCallbackUsage(MOI.LazyConstraintCallback(), cb))
    elseif model.callback_state == _CB_HEURISTIC
        throw(MOI.InvalidCallbackUsage(MOI.HeuristicCallback(), cb))
    elseif !iszero(f.constant)
        throw(
            MOI.ScalarFunctionConstantNotZero{Float64,typeof(f),typeof(s)}(
                f.constant,
            ),
        )
    end
    indices, coefficients = _indices_and_coefficients(model, f)
    sense, rhs = _sense_and_rhs(s)
    ret = GRBcbcut(
        cb.callback_data,
        length(indices),
        indices,
        coefficients,
        sense,
        rhs,
    )
    _check_ret(model, ret)
    return
end
MOI.supports(::Optimizer, ::MOI.UserCut{CallbackData}) = true

# ==============================================================================
#    MOI.HeuristicCallback
# ==============================================================================

function MOI.set(model::Optimizer, ::MOI.HeuristicCallback, cb::Function)
    model.heuristic_callback = cb
    return
end
MOI.supports(::Optimizer, ::MOI.HeuristicCallback) = true

function MOI.submit(
    model::Optimizer,
    cb::MOI.HeuristicSolution{CallbackData},
    variables::Vector{MOI.VariableIndex},
    values::MOI.Vector{Float64},
)
    if model.callback_state == _CB_LAZY
        throw(MOI.InvalidCallbackUsage(MOI.LazyConstraintCallback(), cb))
    elseif model.callback_state == _CB_USER_CUT
        throw(MOI.InvalidCallbackUsage(MOI.UserCutCallback(), cb))
    end
    solution = fill(GRB_UNDEFINED, MOI.get(model, MOI.NumberOfVariables()))
    for (var, value) in zip(variables, values)
        solution[_info(model, var).column] = value
    end
    objP = Ref{Cdouble}()
    ret = GRBcbsolution(cb.callback_data, solution, objP)
    _check_ret(model, ret)
    return objP[] < GRB_INFINITY ? MOI.HEURISTIC_SOLUTION_ACCEPTED :
           MOI.HEURISTIC_SOLUTION_REJECTED
end
MOI.supports(::Optimizer, ::MOI.HeuristicSolution{CallbackData}) = true


text:
VERSION >= v"0.4.0-dev+6521" && __precompile__()

module Compat

using Base.Meta

if VERSION < v"0.4.0-dev+2340"
    const base64encode = base64
    export base64encode
end

if VERSION >= v"0.4.0-dev+3184"
    include("ngenerate.jl")
    using .CompatCartesian
    export @ngenerate, @nsplat
end

if VERSION < v"0.4.0-dev+1624"
    eachindex(A::AbstractArray) = 1:length(A)
    eachindex(a::Associative) = Base.KeyIterator(a)
    export eachindex
end

if VERSION < v"0.4.0-dev+1419"
    export UInt, UInt8, UInt16, UInt32, UInt64, UInt128
    const UInt = Uint
    const UInt8 = Uint8
    const UInt16 = Uint16
    const UInt32 = Uint32
    const UInt64 = Uint64
    const UInt128 = Uint128
end

if VERSION < v"0.4.0-dev+1387"
    typealias AbstractString String
    export AbstractString
end

if VERSION < v"0.4.0-dev+3324"
    typealias AssertionError ErrorException
    export AssertionError
end

if VERSION < v"0.4.0-dev+412"
    eval(Base, :(const IPAddr = IpAddr))
end

if VERSION < v"0.4.0-dev+2197"
    Base.IPv4(ipstr::AbstractString) = Base.parseipv4(ipstr)
    Base.IPv6(ipstr::AbstractString) = Base.parseipv6(ipstr)
end

if VERSION < v"0.4.0-dev+2200"
    function Base.isless{T<:Base.IPAddr}(a::T, b::T)
        return isless(a.host, b.host)
    end
end

if VERSION < v"0.4.0-dev+980"
    macro Dict(pairs...)
        esc(Expr(:dict, pairs...))
    end
    macro AnyDict(pairs...)
        esc(Expr(:typed_dict, :(Any=>Any), pairs...))
    end

    Base.Dict(kv) = dict_with_eltype(kv, eltype(kv))
    dict_with_eltype{K,V}(kv, ::Type{(K,V)}) = Dict{K,V}(kv)
    dict_with_eltype(kv, t) = Dict{Any,Any}(kv)
else
    macro Dict(pairs...)
        esc(Expr(:call, :Dict, pairs...))
    end
    macro AnyDict(pairs...)
        esc(Expr(:call, :(Base.AnyDict), pairs...))
    end
end

import Base: round, ceil, floor, trunc
if VERSION < v"0.4.0-dev+1827"
    for (fnew,fold) in ((:round, :iround), (:ceil, :iceil), (:floor, :ifloor), (:trunc, :itrunc))
        @eval begin
            ($fnew){T<:Integer}(::Type{T}, x::Integer) = convert(T, x)  # ambiguity resolution with digits/base version, not all old methods defined
            ($fnew){T<:Integer}(::Type{T}, x::Real) = ($fold)(T, x)
            ($fnew){T<:Integer}(::Type{T}, x::Rational) = convert(T, ($fold)(x)) # no e.g. iround(::Type{T}, x::Rational) is defined in 0.3
            function ($fnew){T,R}(::Type{T}, x::AbstractArray{R,1})
                [ ($fnew)(T, x[i])::T for i = 1:length(x) ]
            end
            function ($fnew){T,R}(::Type{T}, x::AbstractArray{R,2})
                [ ($fnew)(T, x[i,j])::T for i = 1:size(x,1), j = 1:size(x,2) ]
            end
            function ($fnew){T}(::Type{T}, x::AbstractArray)
                reshape([ ($fnew)(T, x[i])::T for i = 1:length(x) ], size(x))
            end
        end
    end
end

if VERSION < v"0.4.0-dev+838"
    Base.rem{T<:Integer}(n::Integer, ::Type{T}) = convert(T, n)
elseif VERSION < v"0.4.0-dev+1037"
    Base.rem{T<:Integer}(n::Integer, ::Type{T}) = itrunc(T, n)
elseif VERSION < v"0.4.0-dev+1039"
    Base.rem{T<:Integer}(n::Integer, ::Type{T}) = mod(n, T)
end

import Base: srand, rand, rand!
if VERSION < v"0.4.0-dev+1373" # PR #8854
    function make_seed()
        try
            @unix_only seed = open("/dev/urandom") do io
                a = Array(UInt32, 4)
                read!(io, a)
                a
            end
            @windows_only seed = let a = zeros(UInt32, 2)
                Base.dSFMT.win32_SystemFunction036!(a)
                a
            end
            return seed
        catch
            println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.")
            seed = reinterpret(UInt64, time())
            seed = hash(seed, convert(UInt64, getpid()))
            try
                seed = hash(seed, parse(UInt64, readall(`ifconfig` |> `sha1sum`)[1:40], 16))
            end
            return seed
        end
    end
    if VERSION < v"0.4.0-dev+1352" # PR #8832
        function srand(r::MersenneTwister, seed::Vector{UInt32})
            r.seed = seed
            Base.dSFMT.dsfmt_init_by_array(r.state, seed)
            return r
        end
    else
        function srand(r::MersenneTwister, seed::Vector{UInt32})
            r.seed = seed
            Base.dSFMT.dsfmt_init_by_array(r.state, seed)
            r.idx = Base.dSFMT.dsfmt_get_min_array_size()
            return r
        end
        rand(r::MersenneTwister, ::Type{UInt32})  = reinterpret(UInt64, Base.Random.rand_close1_open2(r)) % UInt32
    end
    srand(r::MersenneTwister, seed::Union(UInt32,Int32)) = srand(r, UInt32[seed])
    srand(r::MersenneTwister, seed::Union(UInt64,Int64)) =
        srand(r, UInt32[seed & 0xffffffff, seed >> 32])
    srand(r::MersenneTwister) = srand(r, make_seed())

    rand(r::MersenneTwister, ::Type{UInt8})   = rand(r, UInt32) % UInt8
    rand(r::MersenneTwister, ::Type{UInt16})  = rand(r, UInt32) % UInt16
    rand(r::MersenneTwister, ::Type{UInt32})  = reinterpret(UInt64, rand(r)) % UInt32
    rand(r::MersenneTwister, ::Type{UInt64})  = convert(UInt64,rand(r, UInt32)) <<32 | rand(r, UInt32)
    rand(r::MersenneTwister, ::Type{UInt128}) = convert(UInt128,rand(r, UInt64))<<64 | rand(r, UInt64)

    for (T,Tu) in ((Int8,UInt8),(Int16,UInt16),(Int32,UInt32),(Int64,UInt64),(Int128,UInt128))
        @eval rand(r::MersenneTwister, ::Type{$T}) = reinterpret($T, rand(r, $Tu))
    end
    rand(r::MersenneTwister, ::Type{Float64}) = rand(r)
    rand(r::MersenneTwister, ::Type{Float64}) = rand(r)
    rand{T<:Union(Float16,Float32)}(r::MersenneTwister, ::Type{T}) = convert(T, rand(r))
    rand{T<:Real}(r::AbstractRNG, ::Type{Complex{T}}) = complex(rand(r, T), rand(r, T))
    if VERSION < v"0.4.0-dev+1296" # commit 89befafa7deded0119d0186ef116e03ec91b9680
        function rand!{T<:Number}(r::AbstractRNG, A::AbstractArray{T})
            for i=1:length(A)
                @inbounds A[i] = rand(r, T)
            end
            A
        end
    end
    rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array(T, dims))
    rand(r::AbstractRNG, T::Type, d1::Int, dims::Int...) = rand!(r, Array(T, d1, dims...))
    rand(r::MersenneTwister, ::Type{Bool}) = ((rand(r, UInt32) & 1) == 1)
end

if VERSION < v"0.4.0-dev+551" # PR #8320
    srand() = srand(make_seed())
end

if VERSION < v"0.4.0-dev+1884"
    randexp(rng::MersenneTwister) = Base.Random.randmtzig_exprnd(rng)
    randexp() = Base.Random.randmtzig_exprnd()
    export randexp
end

if VERSION < v"0.4.0-dev+2014"
    sizehint! = Base.sizehint
    export sizehint!
end

if VERSION < v"0.4.0-dev+3413"
    # based on pipe in base/process.jl:
    import Base: AbstractCmd, Redirectable
    function pipe(cmd::AbstractCmd; stdin=nothing, stdout=nothing, stderr=nothing, append::Bool=false)
        if append && stdout === nothing && stderr === nothing
            error("append set to true, but no output redirections specified")
        end
        if stdin !== nothing
            cmd = stdin |> cmd
        end
        if stdout !== nothing
            cmd = append ? cmd >> stdout : cmd |> stdout
        end
        if stderr !== nothing
            cmd = append ? cmd .>> stderr : cmd .> stderr
        end
        return cmd
    end
    pipe(cmd::AbstractCmd, dest) = pipe(cmd, stdout=dest)
    pipe(src::Union(Redirectable,AbstractString), cmd::AbstractCmd) = pipe(cmd, stdin=src)
    pipe(a, b, c, d...) = pipe(pipe(a,b), c, d...)
    export pipe
end

function rewrite_dict(ex)
    length(ex.args) == 1 && return ex

    f = ex.args[1]
    if isexpr(f, :curly)
        newex = Expr(:typed_dict, :($(f.args[2])=>$(f.args[3])))
    else
        newex = Expr(:dict)
    end

    for i = 2:length(ex.args)
        pair = ex.args[i]
        !isexpr(pair, :(=>)) && return ex
        push!(newex.args, pair)
    end
    newex
end

function rewrite_ordereddict(ex)
    length(ex.args) == 1 && return ex

    f = ex.args[1]
    newex = Expr(:call, f, :[])

    for i = 2:length(ex.args)
        pair = ex.args[i]
        !isexpr(pair, :(=>)) && return ex
        push!(newex.args[2].args, Expr(:tuple, pair.args...))
    end

    newex
end

# rewrite Julia 0.4-style split or rsplit (str, splitter; kws...)
# into 0.2/0.3-style positional arguments
function rewrite_split(ex, f)
    limit = nothing
    keep = nothing
    for i in 4:length(ex.args)
        if isexpr(ex.args[i], :kw)
            kw = ex.args[i].args
            if kw[1] == :limit
                limit = kw[2]
            elseif kw[1] == :keep
                keep = kw[2]
            end
        end
    end
    if limit == nothing
        if keep == nothing
            return Expr(:call, f, ex.args[2], ex.args[3])
        else
            return Expr(:call, f, ex.args[2], ex.args[3], keep)
        end
    else
        if keep == nothing
            return Expr(:call, f, ex.args[2], ex.args[3], limit)
        else
            return Expr(:call, f, ex.args[2], ex.args[3], limit, keep)
        end
    end
end

if VERSION < v"0.4.0-dev+707"
    macro inline(ex)
        esc(ex)
    end
end

if VERSION < v"0.4.0-dev+2056"
    macro noinline(ex)
        esc(ex)
    end
end

if VERSION < v"0.4.0-dev+2440"
    bitrand(r::AbstractRNG, dims::Dims)   = rand!(r, BitArray(dims))
    bitrand(r::AbstractRNG, dims::Int...) = rand!(r, BitArray(dims))
    bitrand(dims::Dims)   = rand!(BitArray(dims))
    bitrand(dims::Int...) = rand!(BitArray(dims))
    export bitrand
    Base.rand(::Type{Bool}) = randbool()
end

if VERSION < v"0.4.0-dev+2485"
    startswith = Base.beginswith
    export startswith
end

if VERSION < v"0.4.0-dev+3609"
    fieldnames(x::DataType) = collect(x.names)
    function fieldnames(v)
        t = typeof(v)
        if !isa(t, DataType)
            throw(ArgumentError("cannot call fieldnames() on a non-composite type"))
        end
        return fieldnames(t)
    end
    export fieldnames
end

if VERSION < v"0.4.0-dev+3874"
    Base.parse{T<:Integer}(::Type{T}, c::Char) = parseint(T, c)
    Base.parse{T<:Integer}(::Type{T}, c::Char, base::Integer) = parseint(T, c, base)
    Base.parse{T<:Integer}(::Type{T}, s::AbstractString) = parseint(T, s)
    Base.parse{T<:Integer}(::Type{T}, s::AbstractString, base::Integer) = parseint(T, s, base)
    Base.parse{T<:Union(Float32,Float64)}(::Type{T}, s::AbstractString) = parsefloat(T, s)
end

if VERSION < v"0.4.0-dev+4539"
    Base.parse(::Type{BigFloat}, s::AbstractString) = BigFloat(s)
end

if VERSION < v"0.4.0-dev+3710"
    const unsafe_convert = Base.convert
else
    import Base.unsafe_convert
end

if VERSION < v"0.4.0-dev+3732"
    const calltypes = Dict{Symbol,Symbol}()
    for (k,v) in [(:Integer,:integer),
                   (:Signed,:signed),
                   (:Unsigned,:unsigned),
                   (:Int,:int),
                   (:Int8,:int8),
                   (:Int16,:int16),
                   (:Int32,:int32),
                   (:Int64,:int64),
                   (:Int128,:int128),
                   (:UInt,:uint),
                   (:UInt8,:uint8),
                   (:UInt16,:uint16),
                   (:UInt32,:uint32),
                   (:UInt64,:uint64),
                   (:UInt128,:uint128),
                   (:Float16,:float16),
                   (:Float32,:float32),
                   (:Float64,:float64),
                   (:Complex32,:complex32),
                   (:Complex64,:complex64),
                   (:Complex128,:complex128),
                   (:Char,:char),
                   (:Bool,:bool)]
        calltypes[k] = v
    end
end

function _compat(ex::Expr)
    if ex.head == :call
        f = ex.args[1]
        if VERSION < v"0.4.0-dev+980" && (f == :Dict || (isexpr(f, :curly) && length(f.args) == 3 && f.args[1] == :Dict))
            ex = rewrite_dict(ex)
        elseif VERSION < v"0.4.0-dev+980" && (f == :OrderedDict || (isexpr(f, :curly) && length(f.args) == 3 && f.args[1] == :OrderedDict))
            ex = rewrite_ordereddict(ex)
        elseif VERSION < v"0.4.0-dev+129" && (f == :split || f == :rsplit) && length(ex.args) >= 4 && isexpr(ex.args[4], :kw)
            ex = rewrite_split(ex, f)
        elseif VERSION < v"0.4.0-dev+3732" && haskey(calltypes, f) && length(ex.args) > 1
            T = ex.args[1]
            if T in (:Complex32, :Complex64, :Complex128)
                ex = Expr(:call, calltypes[T], ex.args[2:end]...)
            else
                ex = Expr(:(::), Expr(:call, :convert, T, ex.args[2:end]...), T)
            end
        elseif VERSION < v"0.4.0-dev+3732" && (f == :map && haskey(calltypes, ex.args[2]) && length(ex.args) > 2)
            ex = Expr(:call, calltypes[ex.args[2]], ex.args[3:end]...)
        elseif VERSION < v"0.4.0-dev+1419" && isexpr(f, :curly) && f.args[1] == :Ptr && length(ex.args) == 2 && ex.args[2] == 0
            ex = Expr(:call, :zero, f)
        elseif VERSION < v"0.4.0-dev+4356" && f == :chol
            s = ex.args[3]
            if isexpr(s, :curly) && s.args[1] == :Val
                ex = Expr(:call, :chol, ex.args[2], s.args[2])
            end
        elseif VERSION < v"0.4.0-dev+4739" && isexpr(f, :curly) && (f.args[1] == :Vector || f.args[1] == :Array)
            if f.args[1] == :Vector && length(ex.args) == 1
                ex = Expr(:call, :Array, f.args[2], 0)
            else
                ex = Expr(:call, :Array, f.args[2], ex.args[2:end]...)
            end
        end
    elseif ex.head == :curly
        f = ex.args[1]
        if VERSION < v"0.4.0-dev+4319" && f == :Tuple
            args = ex.args[2:end]
            has_ellipsis = any(args) do arg
                isa(arg, Expr) && (arg.head == :...)
            end
            ex = if has_ellipsis
                Expr(:call, TopNode(:tuple), args...)
            else
                Expr(:tuple, args...)
            end
        end
    elseif ex.head == :macrocall
        f = ex.args[1]
        if f == symbol("@generated") && VERSION < v"0.4.0-dev+4387"
            f = ex.args[2]
            if isexpr(f, :function)
                ex = Expr(:stagedfunction, f.args...)
            end
        end
    elseif VERSION < v"0.4.0-dev+5322" && ex.head == :(::) && isa(ex.args[end], Symbol)
        # Replace Base.Timer with Compat.Timer2 in type declarations
        if ex.args[end] == :Timer || ex.args[end] == :(Base.Timer)
            ex.args[end] = :(Compat.Timer2)
        end
    end
    return Expr(ex.head, map(_compat, ex.args)...)
end
_compat(ex) = ex

macro compat(ex)
    esc(_compat(ex))
end

export @compat, @inline, @noinline

if VERSION < v"0.4.0-dev+656"
    include("nullable.jl")
end

if VERSION < v"0.4.0-dev+3864"
    function tryparse(T::Type{Float32}, s)
        r = Array(T,1)
        float32_isvalid(s, r) ? Nullable(r[1]) : Nullable{Float32}()
    end

    function tryparse(T::Type{Float64}, s)
        r = Array(T,1)
        float64_isvalid(s, r) ? Nullable(r[1]) : Nullable{Float64}()
    end

    function tryparse{T<:Integer}(::Type{T}, s)
        local ret
        try
            val = parse(T, s)
            ret = Nullable{T}(val)
        catch
            ret = Nullable{T}()
        end
        return ret
    end
    export tryparse
end

if VERSION < v"0.4.0-dev+3844"
    @eval module Libc
        const FILE = Base.CFILE
        const calloc = Base.c_calloc
        const free = Base.c_free
        const malloc = Base.c_malloc
        const realloc = Base.c_realloc
        using Base: munmap, systemsleep
        export FILE, TmStruct, calloc, errno, flush_cstdio, free,
               gethostname, getpid, malloc, msync, realloc, strerror,
               strftime, strptime, systemsleep, time
    end
    @eval module Libdl
        using Base.Sys: dlext, dllist, dlpath
        export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY,
               RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlext,
               dllist, dlopen, dlopen_e, dlpath, dlsym, dlsym_e, find_library
    end
    export Libc, Libdl
else
    import Base: Libc, Libdl
end

if VERSION < v"0.4.0-dev+2418"
    function findprev(A, start)
        for i = start:-1:1
            A[i] != 0 && return i
        end
        0
    end
    findlast(A) = findprev(A, length(A))
    function findprev(A, v, start)
        for i = start:-1:1
            A[i] == v && return i
        end
        0
    end
    findlast(A, v) = findprev(A, v, length(A))
    function findprev(testf::Function, A, start)
        for i = start:-1:1
            testf(A[i]) && return i
        end
        0
    end
    findlast(testf::Function, A) = findprev(testf, A, length(A))
    export findprev, findlast
end

if VERSION < v"0.4.0-dev+4524"
    isdiag(A::AbstractMatrix) = istril(A) && istriu(A)
    isdiag(x::Number) = true
    export isdiag
end

if VERSION < v"0.4.0-dev+4502"
    keytype{K,V}(::Associative{K,V}) = K
    valtype{K,V}(::Associative{K,V}) = V
    export keytype, valtype
end

if VERSION < v"0.4.0-dev+2254"
    immutable Val{T} end
    export Val
end

if VERSION < v"0.4.0-dev+4603"
    # used for C string arguments to ccall
    # (in Julia 0.4, these types also check for embedded NUL chars)
    const Cstring = Ptr{UInt8}
    const Cwstring = Ptr{Cwchar_t}
    export Cstring, Cwstring
end

if VERSION < v"0.4.0-dev+2823"
    fma(x::Number,y::Number,z::Number) = x*y + z
    export fma
end

if VERSION < v"0.4.0-dev+2861"
    muladd(x::Number,y::Number,z::Number) = x*y + z
    export muladd
end

if VERSION < v"0.4.0-dev+4734"
    function is_valid_utf32(str::Union(Vector{Char}, Vector{UInt32}))
        for i=1:length(str)
            @inbounds if !is_valid_char(reinterpret(UInt32, str[i])) ; return false ; end
        end
        return true
    end
    is_valid_utf32(str::UTF32String) = is_valid_utf32(str.data)
    export is_valid_utf32
end

if VERSION < v"0.4.0-dev+4939"
    import Base.isvalid
    isvalid(ch::Char)         = is_valid_char(ch)
    isvalid(str::ASCIIString) = is_valid_ascii(str)
    isvalid(str::UTF8String)  = is_valid_utf8(str)
    isvalid(str::UTF16String) = is_valid_utf16(str)
    isvalid(str::UTF32String) = is_valid_utf32(str)
    isvalid(::Type{Char}, ch)         = is_valid_char(ch)
    isvalid(::Type{ASCIIString}, str) = is_valid_ascii(str)
    isvalid(::Type{UTF8String},  str) = is_valid_utf8(str)
    isvalid(::Type{UTF16String}, str) = is_valid_utf16(str)
    isvalid(::Type{UTF32String}, str) = is_valid_utf32(str)
    export isvalid
end

if VERSION < v"0.4.0-dev+5322"
    include("timer.jl")
end

if VERSION < v"0.4.0-dev+5688"
    typealias Irrational MathConst
    @eval const $(symbol("@irrational")) = getfield(Base, symbol("@math_const"))
    export Irrational
else
    import Base.@irrational
end

if VERSION < v"0.4.0-dev+5305"
    function Base.gc_enable(on::Bool)
        state = ccall(:jl_gc_is_enabled, Cint, ()) != 0
        on ? gc_enable() : gc_disable()
        state
    end
end

if VERSION < v"0.4.0-dev+5697"
    @eval module Mmap
        function mmap{T,N}(io::IO,
            ::Type{Array{T,N}}=Vector{UInt8},
            dims::NTuple{N,Integer}=(div(filesize(io)-position(io),sizeof(T)),),
            offset::Integer=position(io); grow::Bool=true, shared::Bool=true)
            mmap_array(T,dims,io::IO,convert(FileOffset,offset))
        end

        mmap{T<:Array,N}(file::String,
                         ::Type{T}=Vector{UInt8},
                         dims::NTuple{N,Integer}=(div(filesize(file),sizeof(eltype(T))),),
                         offset::Integer=0; grow::Bool=true, shared::Bool=true) =
            open(io->mmap(io, T, dims, offset; grow=grow, shared=shared), file, isfile(file) ? "r+" : "w+")::Array{eltype(T),N}

        # using a length argument instead of dims
        mmap{T<:Array}(io::IO, ::Type{T}, len::Integer, offset::Integer=position(io); grow::Bool=true, shared::Bool=true) =
            mmap(io, T, (len,), offset; grow=grow, shared=shared)
        mmap{T<:Array}(file::String, ::Type{T}, len::Integer, offset::Integer=0; grow::Bool=true, shared::Bool=true) =
            open(io->mmap(io, T, (len,), offset; grow=grow, shared=shared), file, isfile(file) ? "r+" : "w+")::Vector{eltype(T)}

        # constructors for non-file-backed (anonymous) mmaps
        mmap{T<:Array,N}(::Type{T}, dims::NTuple{N,Integer}; shared::Bool=true) = mmap(Anonymous(), T, dims, 0; shared=shared)
        mmap{T<:Array}(::Type{T}, i::Integer...; shared::Bool=true) = mmap(Anonymous(), T, convert(Tuple{Vararg{Int}},i), 0; shared=shared)
    end
    export Mmap
end

if VERSION < v"0.4.0-dev+6521"
    __precompile__(::Bool) = nothing
    export __precompile__
end

if VERSION < v"0.4.0-dev+6506"
    include_dependency(::AbstractString) = nothing
    export include_dependency
end

if VERSION < v"0.4.0-dev+6425"
    typealias AbstractFloat FloatingPoint
    export AbstractFloat
end

if VERSION < v"0.4.0-dev+6068"
    Base.real{T<:Real}(::Type{T}) = T
    Base.real{T<:Real}(::Type{Complex{T}}) = T
end

if VERSION < v"0.4.0-dev+6578"
    rtoldefault{T<:AbstractFloat}(::Type{T}) = sqrt(eps(T))
    rtoldefault{T<:Real}(::Type{T}) = 0
    rtoldefault{T<:Number,S<:Number}(x::Union(T,Type{T}), y::Union(S,Type{S})) = rtoldefault(promote_type(real(T),real(S)))
    function Base.isapprox{T<:Number,S<:Number}(x::AbstractArray{T}, y::AbstractArray{S}; rtol::Real=rtoldefault(T,S), atol::Real=0, norm::Function=vecnorm)
        d = norm(x - y)
        return isfinite(d) ? d <= atol + rtol*max(norm(x), norm(y)) : x == y
    end
    const ≈ = isapprox
    ≉(x,y) = !(x ≈ y)
    export ≈, ≉
end

end # module

ext: jl


text:
using Plots

function ackley(x, a=20, b=0.2, c=2π)
    d = length(x);
    return -a * exp(-b * sqrt(sum(x.^2)/d)) -
                exp(sum(cos(c*xi) for xi in x)/d) + a + exp(1);
end

# plot

contour(-30:0.1:30, -30:0.1:30, (x, y)->ackley([x,y]))
heatmap(-30:0.1:30, -30:0.1:30, (x, y)->ackley([x,y]))


text:
import Rotations

function helloexternal()
    println("Hi")
end

function rotateZ(θ::Float64,v::Array{Float64})
    r = Rotations.RotZ(θ)
    return convert(Array{Float64},r*v)
end

xxx = 3

@show rotateZ(pi / 6,Float64[1,0,0])

max_stars_repo_name: terasakisatoshi/juliaExer

max_issues_repo_name: terasakisatoshi/juliaExer

max_forks_repo_name: terasakisatoshi/juliaExer


text:
# Methods / functions in common for several contour integral methods
using Distributed, LinearAlgebra, Random

export MatrixIntegrator, MatrixTrapezoidal;

"""
    abstract type MatrixIntegrator

This type is used for integration of (matrix valued)
functions with a particular structure. It
is used in `contour_beyn` and `contour_block_SS`.

In order to specify your own way to
do numerical quadrature you should inherit from this
type
```
julia> abstract type MyIntegrator <: MatrixIntegrator; end
```
and implement the method `integrate_interval`:
```
julia> import NonlinearEigenproblems.NEPSolver.integrate_interval
julia> function integrate_interval(ST::Type{MyType},::Type{T},f,gv,a,b,N,logger) where {T<:Number}
```
The function `integrate_interval` should integrate functions on the interval `[a,b]` with `N` quadrature points. Further parameters:

* The type `T` (typically `ComplexF64`) specifies what the target number type is.
* The `logger::Logger` specifies if / how things should written to the log.
* `f::Function`, takes a scalar input (in the interval `[a,b]`) and returns a matrix or a vector.
* `gv::Vector{Function}` which takes scalar input (in the interval `[a,b]`)  and returns scalars.

The function `integrate_interval` should return a tensor `I`
where the last dimension is `m=length(gv)` and should contain the
integrals. More precisely,
 `I[:,:,1]`, ... `I[:,:,m]` should contain approximations
of the product of `f(x)gv[1](x)`,...`f(x)gv[m](x)` over
`[a,b]`, e.g., `I[:,:,j]` should contain an approximation of
```math
\\int_a^b f(x)g_j(x)\\,dx
```
Usually, `f(x)` is considerably more expensive to
evaluate than `g_1(x),..,g_m(x)`.

See also: `contour_beyn`, `contour_block_SS`, `MatrixTrapezoidal`
"""
abstract type MatrixIntegrator ; end

"""
    abstract type MatrixTrapezoidal <: MatrixIntegrator; end

Implements the trapezoidal rule to be used
with quadrature methods.

See also: `contour_beyn`, `contour_block_SS`, `MatrixIntegrator`
"""
abstract type MatrixTrapezoidal <: MatrixIntegrator; end

function integrate_interval(ST::Type{MatrixTrapezoidal},::Type{T},f,gv,a,b,N,logger) where {T<:Number}
    h = (b-a)/N
    t = range(a, stop = b-h, length = N)
    f1=f(t[1]);
    m=size(gv,1);
    S = zeros(T,size(f1)...,m)

    push_info!(logger,string(ST)*": computing G",continues=true)
    # Matrix of gv evaluations
    G = zeros(T,N,m);
    for i=1:m
        push_info!(logger,".",continues=true);
        push_info!(logger,2,"t=$t",continues=true);
        gg=gv[i].(Vector(t));
        G[:,i] = gg
    end
    push_info!(logger,".",continues=false);

    # Do the sum
    push_info!(logger,string(ST)*": summing terms",continues=true);
    for i = 1:N
        push_info!(logger,".",continues=true);
        if (i==1) # Already computed
            temp = f1;
        else
            temp = f(t[i])
        end
        for j=1:m
            S[:,:,j] += temp*G[i,j];
        end
    end
    push_info!(logger,"")
    return S * h
end


text:
module TensorProductQuadraturesTests

using Gridap.Integration

using Test

degrees = (2,2)
quad = TensorProductQuadrature(degrees)
test_quadrature(quad)
@test sum(get_weights(quad)) ≈ 1

D = 2
degree = 3
quad = TensorProductQuadrature{D}(degree)
test_quadrature(quad)
@test sum(get_weights(quad)) ≈ 1

end # module


text:
using Test
using CUDA
using ClimaCore.DataLayouts

function knl_copy!(dst, src)
    i = threadIdx().x
    j = threadIdx().y

    h = blockIdx().x

    p_dst = slab(dst, h)
    p_src = slab(src, h)

    @inbounds p_dst[i, j] = p_src[i, j]
    return nothing
end

function test_copy!(dst, src)
    @cuda threads = (4, 4) blocks = (10,) knl_copy!(dst, src)
end

@testset "data in GPU kernels" begin

    S = Tuple{Complex{Float64}, Float64}
    src = IJFH{S, 4}(CuArray(rand(4, 4, 3, 10)))
    dst = IJFH{S, 4}(CuArray(zeros(4, 4, 3, 10)))

    test_copy!(dst, src)

    @test getfield(dst, :array) == getfield(src, :array)
end

@testset "broadcasting" begin
    FT = Float64
    S1 = NamedTuple{(:a, :b), Tuple{Complex{Float64}, Float64}}
    data1 = CuArray(ones(FT, 2, 2, 3, 2))
    S2 = Float64
    data2 = CuArray(ones(FT, 2, 2, 1, 2))
    data1 = IJFH{S1, 2}(data1)
    data2 = IJFH{S2, 2}(data2)

    f(a1, a2) = a1.a.re * a2 + a1.b
    res = f.(data1, data2)
    @test res isa IJFH{Float64}
    @test Array(parent(res)) == FT[2 for i in 1:2, j in 1:2, f in 1:1, h in 1:2]
end

@testset "broadcasting assignment from scalar" begin
    FT = Float64
    S = Complex{FT}
    data = IJFH{S, 2}(CuArray{FT}, 3)
    data .= Complex(1.0, 2.0)
    @test Array(parent(data)) ==
          FT[f == 1 ? 1 : 2 for i in 1:2, j in 1:2, f in 1:2, h in 1:3]
end


text:
import Base.==
struct Weight <: EdgeModifier end
==(x::Weight, y::Weight) = true

struct Start <: EdgeModifier
    s::Number
end
==(x::Start, y::Start) = x.s == y.s

struct Observed <: NodeModifier end

@testset "Modifier" begin
    @test [Start(1) Weight()] == [Start(1) Weight()]
    @test [Start(1) Weight()] == [Weight() Start(1)]
end

@testset "Node" begin
    @test Node(:a) == Node(:a)
    @test Node(:a) != Node(:b)
    a = ModifyingNode(Node(:a), [Start(1) Weight()])
    @test a == deepcopy(a)
    @test ModifyingNode(Node(:a), [Start(1) Weight()]) == ModifyingNode(Node(:a), [Start(1) Weight()])
    b = ModifiedNode(Node(:a), Observed())
    @test b == deepcopy(b)
end

@testset "UndirectedEdge" begin
    @test UndirectedEdge(:a, :b) == UndirectedEdge(:b, :a)
end

@testset "DirectedEdge" begin
    me1 = ModifiedEdge(Edge(:a, :b), Start(1))
    @test me1 == me1
    me2 = deepcopy(me1)
    @test me1 == me2
    me2.modifiers[:Start] = Start(2)
    @test me1 != me2
    me3 = ModifiedEdge(Edge(:a, :b), [Weight() Start(1)])
    me4 = deepcopy(me3)
    @test me3 == me4
    me5 = ModifiedEdge(Edge(:a, :b), [Start(1) Weight()])
    @test me5 == me3
end

@testset "Arrow" begin
    a1 = :a → :b
    a2 = deepcopy(a1)
    a3 = :b ← :a
    @test a1 == a2
    @test a1 != a3
    @test unmeta(a1) == unmeta(a3)
end

@testset "Hash of UndirectedEdge" begin
    e1 = UndirectedEdge(:a, :b)
    e2 = UndirectedEdge(:b, :a)
    me1 = ModifiedEdge(e1, Weight())
    me2 = ModifiedEdge(e2, Weight())
    @test hash(e1) == hash(e2)
    @test hash(me1) == hash(me2)
    @test length(unique([e1, e2])) == 1
    @test length(unique([me1, me2])) == 1
end


text:
"""
    mutable struct ClusterTree{T,S,D}

Tree structure used to cluster elements of type `V = eltype(T)` into containers of type `S`.
The method `center(::V)::SVector` is required for the clustering algorithms. An
additional `data` field of type `D` can be associated with each node to store
node-specific information (it defaults to `D=Nothing`).

# Fields:
- `_elements::T` : vector containing the sorted elements.
- `container::S` : container for the elements in the current node.
- `index_range::UnitRange{Int}` : indices of elements contained in the current node.
- `loc2glob::Vector{Int}` : permutation from the local indexing system to the
original (global) indexing system used as input in the construction of the tree.
- `children::Vector{ClusterTree{N,T,D}}`
- `parent::ClusterTree{N,T,D}`
- `data::D` : generic data field of type `D`.
"""
mutable struct ClusterTree{T,S,D} <: AbstractTree
    _elements::T
    container::S
    index_range::UnitRange{Int}
    loc2glob::Vector{Int}
    children::Vector{ClusterTree{T,S,D}}
    parent::ClusterTree{T,S,D}
    data::D
    # inner constructors handling missing fields.
    function ClusterTree{D}(els::T,container::S,loc_idxs,loc2glob,children,parent,data=nothing) where {T,S,D}
        clt = new{T,S,D}(els,container,loc_idxs,loc2glob)
        clt.children = isnothing(children) ? Vector{typeof(clt)}() : children
        clt.parent   = isnothing(parent)   ? clt : parent
        clt.data     = isnothing(data)     ? D() : data
        return clt
    end
end

# convenience functions and getters
"""
    root_elements(clt::ClusterTree)

The elements contained in the root of the tree to which `clt` belongs.
"""
root_elements(clt::ClusterTree) = clt._elements

"""
    index_range(clt::ClusterTree)

Indices of elements in `root_elements(clt)` which lie inside `clt`.
"""
index_range(clt::ClusterTree)   = clt.index_range

children(clt::ClusterTree)      = clt.children

Base.parent(clt::ClusterTree)        = clt.parent

"""
    container(clt::ClusterTree)

Return the object enclosing all the elements of the `clt`.
"""
container(clt::ClusterTree)     = clt.container

"""
    elements(clt::ClusterTree)

Iterable list of the elements inside `clt`.
"""
elements(clt::ClusterTree)      = view(root_elements(clt),index_range(clt))

"""
    loc2glob(clt::ClusterTree)

The permutation from the (local) indexing system of the elements of the `clt` to
the (global) indexes used upon the construction of the tree.
"""
loc2glob(clt::ClusterTree)      = clt.loc2glob

"""
    container_type(clt::ClusterTree)

Type used to enclose the elements of `clt`.
"""
container_type(::ClusterTree{T,S}) where {T,S} = S

"""
    element_type(clt::ClusterTree)

Type of elements sorted in `clt`.
"""
element_type(::ClusterTree{T}) where {T} = eltype(T)

isleaf(clt::ClusterTree)  = isempty(clt.children)
isroot(clt::ClusterTree)  = clt.parent == clt
hasdata(clt::ClusterTree) = isdefined(clt,:data)

diameter(node::ClusterTree)                      = node |> container |> diameter
radius(node::ClusterTree)                        = node |> container |> radius

"""
    distance(X::ClusterTree, Y::ClusterTree)

Distance between the containers of `X` and `Y`.
"""
distance(node1::ClusterTree,node2::ClusterTree)  = distance(container(node1), container(node2))

Base.length(node::ClusterTree) = length(index_range(node))

ambient_dimension(clt::ClusterTree) = ambient_dimension(container(clt))

"""
    ClusterTree(elements,splitter;[copy_elements=true])
    ClusterTree{D}(points,splitter;[copy_elements=true])

Construct a `ClusterTree` from the  given `elements` using the splitting
strategy encoded in `splitter`. If `copy_elements` is set to false, the
`elements` argument are directly stored in the `ClusterTree` and are permuted
during the tree construction.
"""
function ClusterTree{D}(elements,splitter=CardinalitySplitter();copy_elements=true) where {D}
    copy_elements && (elements = deepcopy(elements))
    if splitter isa DyadicSplitter
        # make a cube for bounding box for quad/oct trees
        bbox         = HyperRectangle(elements,true)
    else
        bbox         = HyperRectangle(elements)
    end
    n            = length(elements)
    irange     = 1:n
    loc2glob     = collect(irange)
    children     = nothing
    parent       = nothing
    #build the root, then recurse
    root         = ClusterTree{D}(elements,bbox,irange,loc2glob,children,parent)
    _build_cluster_tree!(root,splitter)
    return root
end
ClusterTree(args...;kwargs...) = ClusterTree{Nothing}(args...;kwargs...)

function _build_cluster_tree!(current_node,splitter)
    if should_split(current_node,splitter)
        children = split!(current_node,splitter)
        current_node.children = children
        for child in children
            _build_cluster_tree!(child,splitter)
        end
    end
    return current_node
end

"""
    _binary_split!(cluster::ClusterTree,dir,pos;parentcluster=cluster)
    _binary_split!(cluster::ClusterTree,f;parentcluster=cluster)

Split a `ClusterTree` into two, sorting all elements in the process.
For each resulting child assign `child.parent=parentcluster`.

Passing a `dir` and `pos` arguments splits the `container` of `node`
along direction `dir` at position `pos`, then sorts all points into the
resulting  left/right nodes.

If passed a predicate `f`, each point is sorted
according to whether `f(x)` returns `true` (point sorted on
the left node) or `false` (point sorted on the right node). At the end a minimal
`HyperRectangle` containing all left/right points is created.
"""
function _binary_split!(cluster::ClusterTree,dir,pos;parentcluster=cluster)
    return _common_binary_split!(cluster,(dir,pos);parentcluster)
end
function _binary_split!(cluster::ClusterTree,f::Function;parentcluster=cluster)
    return _common_binary_split!(cluster,f;parentcluster)
end
function _common_binary_split!(cluster::ClusterTree{T,S,D},conditions;
                               parentcluster) where {T,S,D}
    els    = root_elements(cluster)
    l2g    = loc2glob(cluster)
    irange = index_range(cluster)
    # get split data
    npts_left,npts_right,left_rec,right_rec,buff = binary_split_data(cluster,conditions)
    @assert npts_left + npts_right == length(irange) "elements lost during split"
    l2g[irange] = l2g[buff]
    els[irange] = els[buff] # reorders the global index set
    # new ranges for cluster
    left_indices  = irange.start:(irange.start)+npts_left-1
    right_indices = (irange.start+npts_left):irange.stop
    # create children
    clt1 = ClusterTree{D}(els,left_rec,left_indices,l2g,nothing,parentcluster)
    clt2 = ClusterTree{D}(els,right_rec,right_indices,l2g,nothing,parentcluster)
    return clt1, clt2
end
function binary_split_data(cluster::ClusterTree{T,S},conditions::Function) where {T,S}
    f          = conditions
    rec        = container(cluster)
    els        = root_elements(cluster)
    irange     = index_range(cluster)
    n          = length(irange)
    npts_left  = 0
    npts_right = 0
    buff = Vector{Int}(undef,length(cluster))
    xl_left = xl_right = high_corner(rec)
    xu_left = xu_right = low_corner(rec)
    #sort the points into left and right rectangle
    for i in irange
        pt = els[i] |> center
        if f(pt)
            xl_left = min.(xl_left,pt)
            xu_left = max.(xu_left,pt)
            npts_left += 1
            buff[npts_left] = i
        else
            xl_right = min.(xl_right,pt)
            xu_right = max.(xu_right,pt)
            buff[n-npts_right] = i
            npts_right += 1
        end
    end
    # bounding boxes
    left_rec  = S(xl_left,xu_left)
    right_rec = S(xl_right,xu_right)
    return npts_left,npts_right,left_rec,right_rec,buff
end
function binary_split_data(cluster::ClusterTree,conditions::Tuple{Integer,Real})
    dir,pos    = conditions
    rec        = container(cluster)
    els        = root_elements(cluster)
    irange     = index_range(cluster)
    n          = length(irange)
    npts_left  = 0
    npts_right = 0
    buff = Vector{Int}(undef,length(cluster))
    # bounding boxes
    left_rec, right_rec = split(rec,dir,pos)
    #sort the points into left and right rectangle
    for i in irange
        pt = els[i] |> center
        if pt in left_rec
            npts_left += 1
            buff[npts_left] = i
        else # pt in right_rec
            buff[n-npts_right] = i
            npts_right += 1
        end
    end
    return npts_left,npts_right,left_rec,right_rec,buff
end

function Base.show(io::IO,tree::ClusterTree{T,S,D}) where {T,S,D}
    print(io,"ClusterTree with $(length(tree.index_range)) elements of type $T")
end

function Base.summary(clt::ClusterTree)
    @printf "Cluster tree with %i elements" length(clt)
    nodes = collect(AbstractTrees.PreOrderDFS(clt))
    @printf "\n\t number of nodes: %i" length(nodes)
    leaves = collect(AbstractTrees.Leaves(clt))
    @printf "\n\t number of leaves: %i" length(leaves)
    points_per_leaf = map(length,leaves)
    @printf "\n\t min number of elements per leaf: %i" minimum(points_per_leaf)
    @printf "\n\t max number of elements per leaf: %i" maximum(points_per_leaf)
    depth_per_leaf = map(depth,leaves)
    @printf "\n\t min depth of leaves: %i" minimum(depth_per_leaf)
    @printf "\n\t max depth of leaves: %i" maximum(depth_per_leaf)
end

max_stars_repo_name: WaveProp/WavePropBase

max_issues_repo_name: WaveProp/WavePropBase

max_forks_repo_name: WaveProp/WavePropBase


text:
"""
    verify_eigen(A[, λ, X0]; w=0.1, ϵ=1e-16, maxiter=10)

Finds a rigorous bound for the eigenvalues and eigenvectors of `A`. Eigenvalues are treated
as simple.

### Input

- `A`       -- matrix
- `λ`       -- (optional) approximate value for an eigenvalue of `A`
- `X0`      -- (optional) eigenvector associated to `λ`
- `w`       -- relative inflation parameter
- `ϵ`       -- absolute inflation parameter
- `maxiter` -- maximum number of iterations

### Output

- Interval bounds on eigenvalues and eigenvectors.
- A boolean certificate (or a vector of booleans if all eigenvalues are computed) `cert`.
  If `cert[i]==true`, then the bounds for the ith eigenvalue and eigenvectore are rigorous,
  otherwise not.

### Algorithm

The algorithm for this function is described in [[RUM01]](@ref).

### Example

```julia
julia> A = Symmetric([1 2;2 3])
2×2 Symmetric{Int64, Matrix{Int64}}:
 1  2
 2  3

julia> evals, evecs, cert = verify_eigen(A);

julia> evals
2-element Vector{Interval{Float64}}:
 [-0.236068, -0.236067]
  [4.23606, 4.23607]

julia> evecs
2×2 Matrix{Interval{Float64}}:
 [-0.850651, -0.85065]  [0.525731, 0.525732]
  [0.525731, 0.525732]  [0.85065, 0.850651]

julia> cert
2-element Vector{Bool}:
 1
 1
```
"""
function verify_eigen(A; kwargs...)
    evals, evecs = eigen(mid.(A))

    T = interval_eigtype(A, evals[1])
    evalues = similar(evals, T)
    evectors = similar(evecs, T)

    cert = Vector{Bool}(undef, length(evals))
    @inbounds for (i, λ₀) in enumerate(evals)
        λ, v, flag = verify_eigen(A, λ₀, view(evecs, :,i); kwargs...)
        evalues[i] = λ
        evectors[:, i] .= v
        cert[i] = flag

    end
    return evalues, evectors, cert
end

function verify_eigen(A, λ, X0; kwargs...)
    ρ, X, cert = _verify_eigen(A, λ, X0; kwargs...)
    return (real(λ) ± ρ) + (imag(λ) ± ρ) * im, X0 + X, cert
end

function verify_eigen(A::Symmetric, λ, X0; kwargs...)
    ρ, X, cert = _verify_eigen(A, λ, X0; kwargs...)
    return λ ± ρ, X0 + real.(X), cert
end

function _verify_eigen(A, λ::Number, X0::AbstractVector;
                      w=0.1, ϵ=floatmin(), maxiter=10)

    _, v = findmax(abs.(X0))

    R = mid.(A) - λ * I
    R[:, v] .= -X0
    R = inv(R)
    C = IA.Interval.(A) - λ * I
    Z = -R * (C * X0)
    C[:, v] .= -X0
    C = I - R * C
    Zinfl = w * IA.Interval.(-mag.(Z), mag.(Z)) .+ IA.Interval(-ϵ, ϵ)

    X = Complex.(Z)
    cert = false
    @inbounds for _ in 1:maxiter
        Y = (real.(X) + Zinfl) + (imag.(X) + Zinfl) * im

        Ytmp = Y * Y[v]
        Ytmp[v] = 0

        X = Z + C * Y + R * Ytmp
        cert = all(X .⊂ Y)
        cert && break
    end

    ρ = mag(X[v])
    X[v] = 0

    return ρ, X, cert
end

"""
    bound_perron_frobenius_eigenvalue(A, max_iter=10)

Finds an upper bound for the Perron-Frobenius eigenvalue of the **non-negative** matrix `A`.

### Input

- `A` -- square real non-negative matrix
- `max_iter` -- maximum number of iterations of the power method used internally to compute
    an initial approximation of the Perron-Frobenius eigenvector

### Example

```julia-repl
julia> A = [1 2;3 4]
2×2 Matrix{Int64}:
 1  2
 3  4

julia> bound_perron_frobenius_eigenvalue(A)
5.372281323275249
```
"""
function bound_perron_frobenius_eigenvalue(A::AbstractMatrix{T}, max_iter=10) where {T<:Real}
    any(A .< 0) && throw(ArgumentError("Matrix contains negative entries"))
    return _bound_perron_frobenius_eigenvalue(A, max_iter)
end

function _bound_perron_frobenius_eigenvalue(M, max_iter=10)

    size(M, 1) == 1 && return M[1]
    xpf = IA.Interval.(_power_iteration(M, max_iter))
    Mxpf = M * xpf
    ρ = zero(eltype(M))
    @inbounds for (i, xi) in enumerate(xpf)
        iszero(xi) && continue
        tmp = Mxpf[i] / xi
        ρ = max(ρ, tmp.hi)
    end
    return ρ
end

function _power_iteration(A, max_iter)
    n = size(A,1)
    xp = rand(n)
    @inbounds for _ in 1:max_iter
    xp .= A*xp
    xp ./= norm(xp)
    end
    return xp
end

interval_eigtype(::Symmetric, ::T) where {T<:Real} = Interval{T}
interval_eigtype(::AbstractMatrix, ::T) where {T<:Real} = Complex{Interval{T}}
interval_eigtype(::AbstractMatrix, ::Complex{T}) where {T<:Real} = Complex{Interval{T}}


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: networkmanager
using AWS.Compat
using AWS.UUIDs

"""
    AssociateCustomerGateway()

Associates a customer gateway with a device and optionally, with a link. If you specify a link, it must be associated with the specified device.  You can only associate customer gateways that are connected to a VPN attachment on a transit gateway. The transit gateway must be registered in your global network. When you register a transit gateway, customer gateways that are connected to the transit gateway are automatically included in the global network. To list customer gateways that are connected to a transit gateway, use the DescribeVpnConnections EC2 API and filter by transit-gateway-id. You cannot associate a customer gateway with more than one device and link.

# Required Parameters
- `CustomerGatewayArn`: The Amazon Resource Name (ARN) of the customer gateway. For more information, see Resources Defined by Amazon EC2.
- `DeviceId`: The ID of the device.
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `LinkId`: The ID of the link.
"""
associate_customer_gateway(CustomerGatewayArn, DeviceId, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/customer-gateway-associations", Dict{String, Any}("CustomerGatewayArn"=>CustomerGatewayArn, "DeviceId"=>DeviceId); aws_config=aws_config)
associate_customer_gateway(CustomerGatewayArn, DeviceId, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/customer-gateway-associations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CustomerGatewayArn"=>CustomerGatewayArn, "DeviceId"=>DeviceId), args)); aws_config=aws_config)

"""
    AssociateLink()

Associates a link to a device. A device can be associated to multiple links and a link can be associated to multiple devices. The device and link must be in the same global network and the same site.

# Required Parameters
- `DeviceId`: The ID of the device.
- `LinkId`: The ID of the link.
- `globalNetworkId`: The ID of the global network.

"""
associate_link(DeviceId, LinkId, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/link-associations", Dict{String, Any}("DeviceId"=>DeviceId, "LinkId"=>LinkId); aws_config=aws_config)
associate_link(DeviceId, LinkId, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/link-associations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DeviceId"=>DeviceId, "LinkId"=>LinkId), args)); aws_config=aws_config)

"""
    CreateDevice()

Creates a new device in a global network. If you specify both a site ID and a location, the location of the site is used for visualization in the Network Manager console.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `Description`: A description of the device. Length Constraints: Maximum length of 256 characters.
- `Location`: The location of the device.
- `Model`: The model of the device. Length Constraints: Maximum length of 128 characters.
- `SerialNumber`: The serial number of the device. Length Constraints: Maximum length of 128 characters.
- `SiteId`: The ID of the site.
- `Tags`: The tags to apply to the resource during creation.
- `Type`: The type of the device.
- `Vendor`: The vendor of the device. Length Constraints: Maximum length of 128 characters.
"""
create_device(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/devices"; aws_config=aws_config)
create_device(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/devices", args; aws_config=aws_config)

"""
    CreateGlobalNetwork()

Creates a new, empty global network.

# Optional Parameters
- `Description`: A description of the global network. Length Constraints: Maximum length of 256 characters.
- `Tags`: The tags to apply to the resource during creation.
"""
create_global_network(; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks"; aws_config=aws_config)
create_global_network(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks", args; aws_config=aws_config)

"""
    CreateLink()

Creates a new link for a specified site.

# Required Parameters
- `Bandwidth`:  The upload speed and download speed in Mbps.
- `SiteId`: The ID of the site.
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `Description`: A description of the link. Length Constraints: Maximum length of 256 characters.
- `Provider`: The provider of the link. Constraints: Cannot include the following characters: |  ^ Length Constraints: Maximum length of 128 characters.
- `Tags`: The tags to apply to the resource during creation.
- `Type`: The type of the link. Constraints: Cannot include the following characters: |  ^ Length Constraints: Maximum length of 128 characters.
"""
create_link(Bandwidth, SiteId, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/links", Dict{String, Any}("Bandwidth"=>Bandwidth, "SiteId"=>SiteId); aws_config=aws_config)
create_link(Bandwidth, SiteId, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/links", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Bandwidth"=>Bandwidth, "SiteId"=>SiteId), args)); aws_config=aws_config)

"""
    CreateSite()

Creates a new site in a global network.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `Description`: A description of your site. Length Constraints: Maximum length of 256 characters.
- `Location`: The site location. This information is used for visualization in the Network Manager console. If you specify the address, the latitude and longitude are automatically calculated.    Address: The physical address of the site.    Latitude: The latitude of the site.     Longitude: The longitude of the site.
- `Tags`: The tags to apply to the resource during creation.
"""
create_site(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/sites"; aws_config=aws_config)
create_site(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/sites", args; aws_config=aws_config)

"""
    DeleteDevice()

Deletes an existing device. You must first disassociate the device from any links and customer gateways.

# Required Parameters
- `deviceId`: The ID of the device.
- `globalNetworkId`: The ID of the global network.

"""
delete_device(deviceId, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/devices/$(deviceId)"; aws_config=aws_config)
delete_device(deviceId, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/devices/$(deviceId)", args; aws_config=aws_config)

"""
    DeleteGlobalNetwork()

Deletes an existing global network. You must first delete all global network objects (devices, links, and sites) and deregister all transit gateways.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

"""
delete_global_network(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)"; aws_config=aws_config)
delete_global_network(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)", args; aws_config=aws_config)

"""
    DeleteLink()

Deletes an existing link. You must first disassociate the link from any devices and customer gateways.

# Required Parameters
- `globalNetworkId`: The ID of the global network.
- `linkId`: The ID of the link.

"""
delete_link(globalNetworkId, linkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/links/$(linkId)"; aws_config=aws_config)
delete_link(globalNetworkId, linkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/links/$(linkId)", args; aws_config=aws_config)

"""
    DeleteSite()

Deletes an existing site. The site cannot be associated with any device or link.

# Required Parameters
- `globalNetworkId`: The ID of the global network.
- `siteId`: The ID of the site.

"""
delete_site(globalNetworkId, siteId; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/sites/$(siteId)"; aws_config=aws_config)
delete_site(globalNetworkId, siteId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/sites/$(siteId)", args; aws_config=aws_config)

"""
    DeregisterTransitGateway()

Deregisters a transit gateway from your global network. This action does not delete your transit gateway, or modify any of its attachments. This action removes any customer gateway associations.

# Required Parameters
- `globalNetworkId`: The ID of the global network.
- `transitGatewayArn`: The Amazon Resource Name (ARN) of the transit gateway.

"""
deregister_transit_gateway(globalNetworkId, transitGatewayArn; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/transit-gateway-registrations/$(transitGatewayArn)"; aws_config=aws_config)
deregister_transit_gateway(globalNetworkId, transitGatewayArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/transit-gateway-registrations/$(transitGatewayArn)", args; aws_config=aws_config)

"""
    DescribeGlobalNetworks()

Describes one or more global networks. By default, all global networks are described. To describe the objects in your global network, you must use the appropriate Get* action. For example, to list the transit gateways in your global network, use GetTransitGatewayRegistrations.

# Optional Parameters
- `globalNetworkIds`: The IDs of one or more global networks. The maximum is 10.
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
"""
describe_global_networks(; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks"; aws_config=aws_config)
describe_global_networks(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks", args; aws_config=aws_config)

"""
    DisassociateCustomerGateway()

Disassociates a customer gateway from a device and a link.

# Required Parameters
- `customerGatewayArn`: The Amazon Resource Name (ARN) of the customer gateway. For more information, see Resources Defined by Amazon EC2.
- `globalNetworkId`: The ID of the global network.

"""
disassociate_customer_gateway(customerGatewayArn, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/customer-gateway-associations/$(customerGatewayArn)"; aws_config=aws_config)
disassociate_customer_gateway(customerGatewayArn, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/customer-gateway-associations/$(customerGatewayArn)", args; aws_config=aws_config)

"""
    DisassociateLink()

Disassociates an existing device from a link. You must first disassociate any customer gateways that are associated with the link.

# Required Parameters
- `deviceId`: The ID of the device.
- `globalNetworkId`: The ID of the global network.
- `linkId`: The ID of the link.

"""
disassociate_link(deviceId, globalNetworkId, linkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/link-associations", Dict{String, Any}("deviceId"=>deviceId, "linkId"=>linkId); aws_config=aws_config)
disassociate_link(deviceId, globalNetworkId, linkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/global-networks/$(globalNetworkId)/link-associations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("deviceId"=>deviceId, "linkId"=>linkId), args)); aws_config=aws_config)

"""
    GetCustomerGatewayAssociations()

Gets the association information for customer gateways that are associated with devices and links in your global network.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `customerGatewayArns`: One or more customer gateway Amazon Resource Names (ARNs). For more information, see Resources Defined by Amazon EC2. The maximum is 10.
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
"""
get_customer_gateway_associations(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/customer-gateway-associations"; aws_config=aws_config)
get_customer_gateway_associations(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/customer-gateway-associations", args; aws_config=aws_config)

"""
    GetDevices()

Gets information about one or more of your devices in a global network.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `deviceIds`: One or more device IDs. The maximum is 10.
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
- `siteId`: The ID of the site.
"""
get_devices(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/devices"; aws_config=aws_config)
get_devices(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/devices", args; aws_config=aws_config)

"""
    GetLinkAssociations()

Gets the link associations for a device or a link. Either the device ID or the link ID must be specified.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `deviceId`: The ID of the device.
- `linkId`: The ID of the link.
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
"""
get_link_associations(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/link-associations"; aws_config=aws_config)
get_link_associations(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/link-associations", args; aws_config=aws_config)

"""
    GetLinks()

Gets information about one or more links in a specified global network. If you specify the site ID, you cannot specify the type or provider in the same request. You can specify the type and provider in the same request.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `linkIds`: One or more link IDs. The maximum is 10.
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
- `provider`: The link provider.
- `siteId`: The ID of the site.
- `type`: The link type.
"""
get_links(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/links"; aws_config=aws_config)
get_links(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/links", args; aws_config=aws_config)

"""
    GetSites()

Gets information about one or more of your sites in a global network.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
- `siteIds`: One or more site IDs. The maximum is 10.
"""
get_sites(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/sites"; aws_config=aws_config)
get_sites(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/sites", args; aws_config=aws_config)

"""
    GetTransitGatewayRegistrations()

Gets information about the transit gateway registrations in a specified global network.

# Required Parameters
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `maxResults`: The maximum number of results to return.
- `nextToken`: The token for the next page of results.
- `transitGatewayArns`: The Amazon Resource Names (ARNs) of one or more transit gateways. The maximum is 10.
"""
get_transit_gateway_registrations(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/transit-gateway-registrations"; aws_config=aws_config)
get_transit_gateway_registrations(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/global-networks/$(globalNetworkId)/transit-gateway-registrations", args; aws_config=aws_config)

"""
    ListTagsForResource()

Lists the tags for a specified resource.

# Required Parameters
- `resourceArn`: The Amazon Resource Name (ARN) of the resource.

"""
list_tags_for_resource(resourceArn; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/tags/$(resourceArn)"; aws_config=aws_config)
list_tags_for_resource(resourceArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("GET", "/tags/$(resourceArn)", args; aws_config=aws_config)

"""
    RegisterTransitGateway()

Registers a transit gateway in your global network. The transit gateway can be in any AWS Region, but it must be owned by the same AWS account that owns the global network. You cannot register a transit gateway in more than one global network.

# Required Parameters
- `TransitGatewayArn`: The Amazon Resource Name (ARN) of the transit gateway. For more information, see Resources Defined by Amazon EC2.
- `globalNetworkId`: The ID of the global network.

"""
register_transit_gateway(TransitGatewayArn, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/transit-gateway-registrations", Dict{String, Any}("TransitGatewayArn"=>TransitGatewayArn); aws_config=aws_config)
register_transit_gateway(TransitGatewayArn, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/global-networks/$(globalNetworkId)/transit-gateway-registrations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("TransitGatewayArn"=>TransitGatewayArn), args)); aws_config=aws_config)

"""
    TagResource()

Tags a specified resource.

# Required Parameters
- `Tags`: The tags to apply to the specified resource.
- `resourceArn`: The Amazon Resource Name (ARN) of the resource.

"""
tag_resource(Tags, resourceArn; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/tags/$(resourceArn)", Dict{String, Any}("Tags"=>Tags); aws_config=aws_config)
tag_resource(Tags, resourceArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("POST", "/tags/$(resourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Tags"=>Tags), args)); aws_config=aws_config)

"""
    UntagResource()

Removes tags from a specified resource.

# Required Parameters
- `resourceArn`: The Amazon Resource Name (ARN) of the resource.
- `tagKeys`: The tag keys to remove from the specified resource.

"""
untag_resource(resourceArn, tagKeys; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/tags/$(resourceArn)", Dict{String, Any}("tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(resourceArn, tagKeys, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("DELETE", "/tags/$(resourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tagKeys"=>tagKeys), args)); aws_config=aws_config)

"""
    UpdateDevice()

Updates the details for an existing device. To remove information for any of the parameters, specify an empty string.

# Required Parameters
- `deviceId`: The ID of the device.
- `globalNetworkId`: The ID of the global network.

# Optional Parameters
- `Description`: A description of the device. Length Constraints: Maximum length of 256 characters.
- `Location`:
- `Model`: The model of the device. Length Constraints: Maximum length of 128 characters.
- `SerialNumber`: The serial number of the device. Length Constraints: Maximum length of 128 characters.
- `SiteId`: The ID of the site.
- `Type`: The type of the device.
- `Vendor`: The vendor of the device. Length Constraints: Maximum length of 128 characters.
"""
update_device(deviceId, globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)/devices/$(deviceId)"; aws_config=aws_config)
update_device(deviceId, globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)/devices/$(deviceId)", args; aws_config=aws_config)

"""
    UpdateGlobalNetwork()

Updates an existing global network. To remove information for any of the parameters, specify an empty string.

# Required Parameters
- `globalNetworkId`: The ID of your global network.

# Optional Parameters
- `Description`: A description of the global network. Length Constraints: Maximum length of 256 characters.
"""
update_global_network(globalNetworkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)"; aws_config=aws_config)
update_global_network(globalNetworkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)", args; aws_config=aws_config)

"""
    UpdateLink()

Updates the details for an existing link. To remove information for any of the parameters, specify an empty string.

# Required Parameters
- `globalNetworkId`: The ID of the global network.
- `linkId`: The ID of the link.

# Optional Parameters
- `Bandwidth`: The upload and download speed in Mbps.
- `Description`: A description of the link. Length Constraints: Maximum length of 256 characters.
- `Provider`: The provider of the link. Length Constraints: Maximum length of 128 characters.
- `Type`: The type of the link. Length Constraints: Maximum length of 128 characters.
"""
update_link(globalNetworkId, linkId; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)/links/$(linkId)"; aws_config=aws_config)
update_link(globalNetworkId, linkId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)/links/$(linkId)", args; aws_config=aws_config)

"""
    UpdateSite()

Updates the information for an existing site. To remove information for any of the parameters, specify an empty string.

# Required Parameters
- `globalNetworkId`: The ID of the global network.
- `siteId`: The ID of your site.

# Optional Parameters
- `Description`: A description of your site. Length Constraints: Maximum length of 256 characters.
- `Location`: The site location:    Address: The physical address of the site.    Latitude: The latitude of the site.     Longitude: The longitude of the site.
"""
update_site(globalNetworkId, siteId; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)/sites/$(siteId)"; aws_config=aws_config)
update_site(globalNetworkId, siteId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = networkmanager("PATCH", "/global-networks/$(globalNetworkId)/sites/$(siteId)", args; aws_config=aws_config)


text:
"""
    Decryptor

A `Decryptor` can be used to decrypt a `Ciphertext` instance back into a `Plaintext` instance.

See also: [`Plaintext`](@ref), [`Ciphertext`](@ref)
"""
mutable struct Decryptor <: SEALObject
  handle::Ptr{Cvoid}

  function Decryptor(context::SEALContext, secret_key::SecretKey)
    handleref = Ref{Ptr{Cvoid}}(C_NULL)
    retval = ccall((:Decryptor_Create, libsealc), Clong,
                   (Ptr{Cvoid}, Ptr{Cvoid}, Ref{Ptr{Cvoid}}),
                   context, secret_key, handleref)
    @check_return_value retval
    return Decryptor(handleref[])
  end

  function Decryptor(handle::Ptr{Cvoid})
    object = new(handle)
    finalizer(destroy!, object)
    return object
  end
end

function destroy!(object::Decryptor)
  if isallocated(object)
    @check_return_value ccall((:Decryptor_Destroy, libsealc), Clong, (Ptr{Cvoid},), object)
    sethandle!(object, C_NULL)
  end

  return nothing
end

function decrypt!(destination::Plaintext, encrypted::Ciphertext, decryptor::Decryptor)
  retval = ccall((:Decryptor_Decrypt, libsealc), Clong,
                 (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
                 decryptor, encrypted, destination)
  @check_return_value retval
  return destination
end

function invariant_noise_budget(encrypted::Ciphertext, decryptor::Decryptor)
  budgetref = Ref{Cint}(0)
  retval = ccall((:Decryptor_InvariantNoiseBudget, libsealc), Clong,
                 (Ptr{Cvoid}, Ptr{Cvoid}, Ref{Cint}),
                 decryptor, encrypted, budgetref)
  @check_return_value retval
  return Int(budgetref[])
end


text:
VERSION >= v"0.4.0-dev+6521" && __precompile__(true)
module ModernGL

using Compat

function glXGetProcAddress(glFuncName)
    ccall((:glXGetProcAddress, "libGL.so.1"), Ptr{Void}, (Ptr{UInt8},), glFuncName)
end
function NSGetProcAddress(glFuncName)
    tmp = "_"*glFuncName
    if ccall(:NSIsSymbolNameDefined, Cint, (Ptr{UInt8},), tmp) == 0
        return convert(Ptr{Void}, 0)
    else
        symbol = ccall(:NSLookupAndBindSymbol, Ptr{Void}, (Ptr{UInt8},), tmp)
        return ccall(:NSAddressOfSymbol, Ptr{Void}, (Ptr{Void},), symbol)
    end
end

function wglGetProcAddress(glFuncName)
    ccall((:wglGetProcAddress, "opengl32"), Ptr{Void}, (Ptr{UInt8},), glFuncName)
end

function getprocaddress(glFuncName)
    @static if is_linux()
        glXGetProcAddress(glFuncName)
    elseif is_windows()
        wglGetProcAddress(glFuncName)
    elseif is_apple()
        NSGetProcAddress(glFuncName)
    else
        error("platform not supported")
    end
end

immutable ContextNotAvailable <: Exception
    message::Compat.UTF8String
end
export ContextNotAvailable
function getprocaddress_e(glFuncName)
    p = getprocaddress(glFuncName)
    if !isavailable(p)
        throw(ContextNotAvailable(
            "$glFuncName, not available for your driver, or no valid OpenGL context available"
        ))
    end
    p
end

# Test, if an opengl function is available.
# Sadly, this doesn't work for Linux, as glxGetProcAddress
# always returns a non null function pointer, as the function pointers are not depending on an active context.
isavailable(name::Symbol) =
    isavailable(ModernGL.getprocaddress(ascii(string(name))))

isavailable(ptr::Ptr{Void}) = !(
    ptr == C_NULL ||
    ptr == convert(Ptr{Void},  1) ||
    ptr == convert(Ptr{Void},  2) ||
    ptr == convert(Ptr{Void},  3)
)

abstract Enum
macro GenEnums(list)
    tmp = list.args
    enumName = tmp[2]
    splice!(tmp, 1:2)
    enumType    = typeof(eval(tmp[4].args[1].args[2]))
    enumdict1   = Dict{enumType, Symbol}()
    for elem in tmp
        if elem.head == :const
            enumdict1[eval(elem.args[1].args[2])] = elem.args[1].args[1]
        end
    end
    dictname = gensym()
    enumtype =  quote
        immutable $(enumName){Sym, T} <: Enum
            number::T
            name::Symbol
        end
        $(dictname) = $enumdict1
        function $(enumName){T}(number::T)
            if !haskey($(dictname), number)
                error("$number is not a GLenum")
            end
            $(enumName){$(dictname)[number], T}(number, $(dictname)[number])
        end

    end
    esc(Expr(:block, enumtype, tmp..., Expr(:export, :($(enumName)))))
end

include("glTypes.jl")
include("functionloading.jl")
include("glConstants.jl")

end # module


text:
const COMPRESSOR_TO_ID = Dict(
    :ZlibCompressor => UInt16(1),
    :Bzip2Compressor => UInt16(307),
    #:BloscCompressor => UInt16(32001),
    :LZ4FrameCompressor => UInt16(32004),
    )

# For loading need filter_ids as keys
const ID_TO_DECOMPRESSOR = Dict(
    UInt16(1) => (:CodecZlib, :ZlibCompressor, :ZlibDecompressor, ""),
    UInt16(307) => (:CodecBzip2, :Bzip2Compressor, :Bzip2Decompressor, "BZIP2"),
    #UInt16(32001) => (:Blosc, :BloscCompressor, :BloscDecompressor, "BLOSC"),
    UInt16(32004) => (:CodecLz4, :LZ4FrameCompressor, :LZ4FrameDecompressor, "LZ4"),
)

issupported_filter(filter_id) = filter_id ∈ keys(ID_TO_DECOMPRESSOR)

function verify_compressor(compressor)
    (compressor isa Bool || haskey(COMPRESSOR_TO_ID, nameof(typeof(compressor)))) && return

    crs = map(values(ID_TO_DECOMPRESSOR)) do val
        "\n\t"*string(val[1])*"."*string(val[2])
    end

    throw(ArgumentError("""Unsupported Compressor
    Supported Compressors are $(crs...)"""))
end
#############################################################################################################
# Dynamic Package Loading Logic copied from FileIO
const load_locker = Base.ReentrantLock()

is_installed(pkg::Symbol) = get(Pkg.installed(), string(pkg), nothing) != nothing

function _findmod(f::Symbol)
    for (u,v) in Base.loaded_modules
        (Symbol(v) == f) && return u
    end
    nothing
end

function topimport(modname)
    @info "Attempting to dynamically load $modname"
    @eval Base.__toplevel__  import $modname
    u = _findmod(modname)
    @eval $modname = Base.loaded_modules[$u]
end

function checked_import(pkg::Symbol)
    lock(load_locker) do
        # kludge for test suite
        if isdefined(Main, pkg)
            m1 = getfield(Main, pkg)
            isa(m1, Module) && return false, m1
        end
        if isdefined(JLD2, pkg)
            m1 = getfield(JLD2, pkg)
            isa(m1, Module) && return false, m1
        end
        m = _findmod(pkg)
        (m === nothing) || return false, Base.loaded_modules[m]
        topimport(pkg)
        return true, Base.loaded_modules[_findmod(pkg)]
    end
end

#############################################################################################################

# jld2.jl 341
Base.write(f::JLDFile, name::AbstractString, obj, wsession::JLDWriteSession=JLDWriteSession(); compress=nothing) =
    write(f.root_group, name, obj, wsession; compress=compress)

# groups.jl 112
function Base.write(g::Group, name::AbstractString, obj, wsession::JLDWriteSession=JLDWriteSession(); compress=nothing)
    f = g.f
    prewrite(f)
    (g, name) = pathize(g, name, true)
    if !(compress === nothing)
        verify_compressor(compress)
        if obj isa Array
            g[name] = write_dataset(f, obj, wsession, compress)
            return nothing
        end
        @warn "Only arrays can be compressed."
    end
    g[name] = write_dataset(f, obj, wsession)
    nothing
end

get_compressor(compressor) = false, COMPRESSOR_TO_ID[nameof(typeof(compressor))], compressor

function get_compressor(::Bool)
    # This method is only called when the argument is true
    # No specific compression lib was given. Return the default
    if !isdefined(JLD2, :CodecZlib)
        m = checked_import(:CodecZlib)
        return true, Base.invokelatest(get_compressor, true)[2:3]...
    end
    false, COMPRESSOR_TO_ID[:ZlibCompressor], CodecZlib.ZlibCompressor()
end

function get_decompressor(filter_id::UInt16)
    modname, compressorname, decompressorname, = ID_TO_DECOMPRESSOR[filter_id]
    invoke_again, m = checked_import(modname)
    return invoke_again, @eval $m.$decompressorname()
end

pipeline_message_size(filter_id) = 4 + 12 + (filter_id > 255)*(2 + length(ID_TO_DECOMPRESSOR[filter_id][4]))

function write_filter_pipeline_message(io, filter_id::UInt16)
    hmsize = 12
    if filter_id > 255
        filter_name = ID_TO_DECOMPRESSOR[filter_id][4]
        hmsize += 2 + length(filter_name)
    end
    jlwrite(io, HeaderMessage(HM_FILTER_PIPELINE, hmsize, 0))
    jlwrite(io, UInt8(2))                 # Version
    jlwrite(io, UInt8(1))                 # Number of Filters
    jlwrite(io, filter_id)                # Filter Identification Value
    filter_id > 255 && jlwrite(io, UInt16(length(filter_name)))
                                        # Length of Filter Name
    jlwrite(io, UInt16(0))                # Flags
    jlwrite(io, UInt16(1))                # Number of Client Data Values
    filter_id > 255 && jlwrite(io, filter_name) # Filter Name
    jlwrite(io, UInt32(5))                # Client Data (Compression Level)
    nothing
end

function deflate_data(f::JLDFile, data::Array{T}, odr::S, wsession::JLDWriteSession,
                      compressor) where {T,S}
    buf = Vector{UInt8}(undef, odr_sizeof(odr) * length(data))
    cp = Ptr{Cvoid}(pointer(buf))
    @simd for i = 1:length(data)
        @inbounds h5convert!(cp, odr, f, data[i], wsession)
        cp += odr_sizeof(odr)
    end
    TranscodingStreams.initialize(compressor)
    res = transcode(compressor, buf)
    TranscodingStreams.finalize(compressor)
    res
end

@inline chunked_storage_message_size(ndims::Int) =
    jlsizeof(HeaderMessage) + 5 + (ndims+1)*jlsizeof(Length) + 1 + jlsizeof(Length) + 4 + jlsizeof(RelOffset)

function write_chunked_storage_message( io::IO,
                                        elsize::Int,
                                        dims::NTuple{N,Int},
                                        filtered_size::Int,
                                        offset::RelOffset) where N
    jlwrite(io, HeaderMessage(HM_DATA_LAYOUT, chunked_storage_message_size(N) - jlsizeof(HeaderMessage), 0))
    jlwrite(io, UInt8(4))                     # Version
    jlwrite(io, UInt8(LC_CHUNKED_STORAGE))    # Layout Class
    jlwrite(io, UInt8(2))                     # Flags (= SINGLE_INDEX_WITH_FILTER)
    jlwrite(io, UInt8(N+1))                   # Dimensionality
    jlwrite(io, UInt8(jlsizeof(Length)))        # Dimensionality Size
    for i = N:-1:1
        jlwrite(io, Length(dims[i]))          # Dimensions 1...N
    end
    jlwrite(io, Length(elsize))               # Element size (last dimension)
    jlwrite(io, UInt8(1))                     # Chunk Indexing Type (= Single Chunk)
    jlwrite(io, Length(filtered_size))        # Size of filtered chunk
    jlwrite(io, UInt32(0))                    # Filters for chunk
    jlwrite(io, offset)                       # Address
end

function write_compressed_data(cio, f, data, odr, wsession, filter_id, compressor)
    write_filter_pipeline_message(cio, filter_id)

    # deflate first
    deflated = deflate_data(f, data, odr, wsession, compressor)

    write_chunked_storage_message(cio, odr_sizeof(odr), size(data), length(deflated), h5offset(f, f.end_of_data))
    jlwrite(f.io, end_checksum(cio))

    f.end_of_data += length(deflated)
    jlwrite(f.io, deflated)
end

function read_compressed_array!(v::Array{T}, f::JLDFile{MmapIO},
                                rr::ReadRepresentation{T,RR},
                                data_length::Int,
                                filter_id
                                ) where {T,RR}

    invoke_again, decompressor = get_decompressor(filter_id)
    if invoke_again
        return Base.invokelatest(read_compressed_array!, v, f, rr, data_length, filter_id)
    end
    io = f.io
    inptr = io.curptr
    TranscodingStreams.initialize(decompressor)
    data = transcode(decompressor, unsafe_wrap(Array, Ptr{UInt8}(inptr), data_length))::Array{UInt8, 1}
    TranscodingStreams.finalize(decompressor)
    @simd for i = 1:length(v)
        dataptr = Ptr{Cvoid}(pointer(data, odr_sizeof(RR)*(i-1)+1))
        if !jlconvert_canbeuninitialized(rr) || jlconvert_isinitialized(rr, dataptr)
            @inbounds v[i] = jlconvert(rr, f, dataptr, NULL_REFERENCE)
        end
    end
    io.curptr = inptr + data_length
    v
end

function read_compressed_array!(v::Array{T}, f::JLDFile{IOStream},
                                rr::ReadRepresentation{T,RR},
                                data_length::Int,
                                filter_id,
                                ) where {T,RR}
    invoke_again, decompressor = get_decompressor(filter_id)
    if invoke_again
        return Base.invokelatest(read_compressed_array!, v, f, rr, data_length, filter_id)
    end
    io = f.io
    data_offset = position(io)
    n = length(v)
    data = read!(TranscodingStream(decompressor, io), Vector{UInt8}(undef, odr_sizeof(RR)*n))
    @simd for i = 1:n
        dataptr = Ptr{Cvoid}(pointer(data, odr_sizeof(RR)*(i-1)+1))
        if !jlconvert_canbeuninitialized(rr) || jlconvert_isinitialized(rr, dataptr)
            @inbounds v[i] = jlconvert(rr, f, dataptr, NULL_REFERENCE)
        end
    end
    seek(io, data_offset + data_length)
    v
end


text:
fname = :ST_ARCOp
c = Combi(hessian_operator,PDataST,solve_modelST_ARC,preprocessST,decreaseGen,TparamsST())
include("Template.jl")


text:
export @manipulate, widget

function make_widget(binding)
    if binding.head != :(=)
        error("@manipulate syntax error.")
    end
    sym, expr = binding.args
    Expr(:call, :(=>), Expr(:quote, Symbol(sym)), Expr(:(=), esc(sym),
         Expr(:call, widget, esc(expr), Expr(:kw, :label, string(sym)))))
end

function map_block(block, symbols, throttle = nothing)
    lambda = Expr(:(->), Expr(:tuple, symbols...),
                  block)
    f = gensym()

    get_obs(wdg, throttle::Nothing = nothing) = :(observe($wdg))
    get_obs(wdg, throttle) = :(InteractBase.throttle($throttle, $(get_obs(wdg))))
    quote
        $f = $lambda
        map($f, $(get_obs.(symbols, throttle)...))
    end
end

function symbols(bindings)
    map(x->x.args[1], bindings)
end

"""
`@manipulate expr`

The @manipulate macro lets you play with any expression using widgets. `expr` needs to be a `for` loop. The `for` loop variable
are converted to widgets using the [`widget`](@ref) function (ranges become `slider`, lists of options become `togglebuttons`, etc...).
The `for` loop body is displayed beneath the widgets and automatically updated as soon as the widgets change value.

Use `throttle = df` to only update the output after a small time interval `dt` (useful if the update is costly as it prevents
multiple updates when moving for example a slider).

## Examples

```julia
using Colors

@manipulate for r = 0:.05:1, g = 0:.05:1, b = 0:.05:1
    HTML(string("<div style='color:#", hex(RGB(r,g,b)), "'>Color me</div>"))
end

@manipulate throttle = 0.1 for r = 0:.05:1, g = 0:.05:1, b = 0:.05:1
    HTML(string("<div style='color:#", hex(RGB(r,g,b)), "'>Color me</div>"))
end
```

[`@layout!`](@ref) can be used to adjust the layout of a manipulate block:

```julia
using Interact

ui = @manipulate throttle = 0.1 for r = 0:.05:1, g = 0:.05:1, b = 0:.05:1
    HTML(string("<div style='color:#", hex(RGB(r,g,b)), "'>Color me</div>"))
end
@layout! ui dom"div"(observe(_), vskip(2em), :r, :g, :b)
ui
```
"""
macro manipulate(args...)
    n = length(args)
    @assert 1 <= n <= 2
    expr = args[n]
    throttle = n == 2 ? args[1].args[2] : nothing

    if expr.head != :for
        error("@manipulate syntax is @manipulate for ",
              " [<variable>=<domain>,]... <expression> end")
    end
    block = expr.args[2]
    if expr.args[1].head == :block
        bindings = expr.args[1].args
    else
        bindings = [expr.args[1]]
    end
    syms = symbols(bindings)

    widgets = map(make_widget, bindings)

    dict = Expr(:call, :OrderedDict, widgets...)
    quote
        local children = $dict
        local output = $(esc(map_block(block, syms, throttle)))
        local layout = t -> node(:div, map(center, values(components(t)))..., map(center, output))
        Widget{:manipulate}(children, output=output, layout=layout)
    end
end

"""
`widget(args...; kwargs...)`

Automatically convert Julia types into appropriate widgets. `kwargs` are passed to the
more specific widget function.

## Examples

```julia
map(display, [
    widget(1:10),                 # Slider
    widget(false),                # Checkbox
    widget("text"),               # Textbox
    widget(1.1),                  # Spinbox
    widget([:on, :off]),          # Toggle Buttons
    widget(Dict("π" => float(π), "τ" => 2π)),
    widget(colorant"red"),        # Color picker
    widget(Dates.today()),        # Date picker
    widget(Dates.Time()),         # Time picker
    ]);
```
"""
function widget end

@deprecate widget(x, label) widget(x, label = label)

widget(x; kwargs...) = x
widget(x::Observable; label = nothing) =
    label === nothing ? x : Widget{:observable}(["label" => label], output = x, layout = t -> flex_row(t["label"], t.output))
widget(x::AbstractRange; kwargs...) = slider(x; kwargs...)
widget(x::AbstractVector; kwargs...) = togglebuttons(x; kwargs...)
widget(x::AbstractVector{<:Real}; kwargs...) = slider(x; kwargs...)
widget(x::AbstractVector{Bool}; kwargs...) = togglebuttons(x; kwargs...)
widget(x::AbstractDict; kwargs...) = togglebuttons(x; kwargs...)
widget(x::Bool; kwargs...) = toggle(x; kwargs...)
widget(x::AbstractString; kwargs...) = textbox(; value=x, kwargs...)
widget(x::Real; kwargs...) = spinbox(; value=Float64(x), kwargs...)
widget(x::Color; kwargs...) = colorpicker(x; kwargs...)
widget(x::Date; kwargs...) = datepicker(x; kwargs...)
widget(x::Dates.Time; kwargs...) = timepicker(x; kwargs...)

center(w) = flex_row(w)
center(w::Widget) = w
center(w::Widget{:toggle}) = flex_row(w)


text:
#=

StridedView allows permutations, and gap dimensions, but not Diagonal-like objects.
Instead of storing a permutation in the type, it stores the strides in a field.

Making @strided transmute(A, perm) return another StridedView will make TensorCast work,
but is unlike the other _transmute methods, which aim to unwrap.

=#

using Strided
using Strided: StridedView, UnsafeStridedView # @unsafe_strided

@inline function _transmute(A::StridedView{T,M}, P) where {T,M}
    Q = invperm_zero(P, size(A))  # also checks size of dropped dimensions
    N = length(P)
    if M == N && P == ntuple(identity, N)  # trivial case
        data
    elseif unique_or_zero(P)
        sz = map(d -> d==0 ? 1 : size(A,d), P)
        st = map(d -> d==0 ? 0 : stride(A,d), P)
        StridedView(A.parent, sz, st, A.offset, A.op)
    elseif P == (1,1)
        Diagonal(A.parent)
    else
        TransmutedDimsArray{T,N,P,Q,typeof(A)}(A)
    end
end

@inline function Strided.StridedView(A::TransmutedDimsArray{T,N,P,Q}) where {T,N,P,Q}
    _transmute(StridedView(parent(A)), P)
end

#=

The eager transmutedims(A, perm) should also use this?

Restricted here to T,T specificaly so that _float_transmutedims(A, perm) can,
with integer-valued A, test the alternative _densecopy_permuted!() path.

=#

@inline function _densecopy_permuted!(dst::Array{T}, A::StridedArray{T}, ::Val{P}) where {T,P}
    sz = map(d -> d==0 ? 1 : size(A,d), P)
    st = map(d -> d==0 ? 0 : stride(A,d), P)
    if isbitstype(T)
        _A = UnsafeStridedView(pointer(A), sz, st, 0, identity)
        _B = UnsafeStridedView(pointer(dst), size(dst), strides(dst), 0, identity)
        _densecopy_strided!(_B, _A)
    else
        # This path may be slower than the default one. Not really tested, either.
        _densecopy_strided!(dst, StridedView(A, sz, st, 0, identity))
    end
    nothing
end

@inline function _densecopy_strided!(dst, src)
    T = eltype(dst)
    LinearAlgebra.axpby!(one(T), src, zero(T), dst)
end