text:
# """
# $(SIGNATURES)
# BFGS multiplier update
# """
# function BFGS(Hinv,ρ,y,s)
#     p = size(y)
#     ρ = inv(y'*s)
#     Hinv = (1.0*Matrix(I,p,p) - ρ*y*s')*Hinv*(1.0*Matrix(I,p,p) - ρ*s*y') + ρ*s*s'
#     return Hinv
# end

"""
$(SIGNATURES)
    Lagrange multiplier updates
        -see Bertsekas 'Constrained Optimization' chapter 2 (p.135)
        -see Toussaint 'A Novel Augmented Lagrangian Approach for Inequalities and Convergent Any-Time Non-Central Updates'
"""
function λ_update_default!(results::ConstrainedIterResults,solver::Solver)
    n,m,N = get_sizes(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    for k = 1:N
        k != N ? idx_pI = pI : idx_pI = pI_N

        results.λ[k] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, results.λ[k] + results.μ[k].*results.C[k]))
        results.λ[k][1:idx_pI] = max.(0.0,results.λ[k][1:idx_pI])
    end
end

function λ_update_default!(results::ConstrainedIterResults,solver::Solver,i::Int,k::Int)
    n,m,N = get_sizes(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    k != N ? idx_pI = pI : idx_pI = pI_N

    results.λ[k][i] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, results.λ[k][i] + results.μ[k][i].*results.C[k][i]))
    i <= idx_pI ? results.λ[k][i] = max.(0.0,results.λ[k][i]) : nothing
end

# function λ_update_accel!(results::ConstrainedIterResults,solver::Solver)
#     n,m,N = get_sizes(solver)
#     p,pI,pE = get_num_constraints(solver)
#     p_N,pI_N,pE_N = get_num_terminal_constraints(solver)
#
#     λ,μ,C = results.λ, results.μ, results.C
#     t,λ_tilde_prev = results.t_prev, results.λ_prev
#     for k = 1:N-1
#         t_prime = @. (1+sqrt(1+4t[k]^2))/2
#         λ_tilde = λ[k] + μ[k].*C[k]
#         λ_prime = @. λ_tilde + ((t[k]-1)/t_prime)*(λ_tilde - λ_tilde_prev[k])
#
#         results.λ[k] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ_prime))
#         results.λ[k][1:pI] = max.(0.0,results.λ[k][1:pI])
#
#         λ_tilde_prev[k] = λ_tilde
#         t[k] = t_prime
#     end
#     t_prime = @. (1+sqrt(1+4t[N]^2))
#     λ_tilde = λ[N] + μ[N].*C[N]
#     λ_prime = @. λ_tilde + ((t[N]-1)/t_prime)*(λ_tilde - λ_tilde_prev[N])
#
#     results.λ[N] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ_prime))
#     results.λ[N][1:pI_N] = max.(0.0,results.λ[N][1:pI_N])
#
#     λ_tilde_prev[N] = λ_tilde
#     t[N] = t_prime
# end

function λ_update_momentum!(results::ConstrainedIterResults,solver::Solver)
    n,m,N = get_sizes(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    λ,μ,C = results.λ, results.μ, results.C
    λ_prev = results.λ_prev

    for k = 1:N-1
        λ_next = λ[k] + 1.9*μ[k].*C[k] + 0.99*λ_prev[k]
        λ_next = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ_next))
        λ_next[1:pI] = max.(0.0,λ_next[1:pI])
        λ_prev[k] = λ[k]
        λ[k] = λ_next
    end
    λ_next = λ[N] + 1.9μ[N].*C[N] + 0.99*λ_prev[N]
    λ_next = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ_next))
    λ_next[1:pI_N] = max.(0.0,λ_next[1:pI_N])
    λ_prev[N] = λ[N]
    λ[N] = λ_next
end

function λ_update_nesterov!(results::ConstrainedIterResults,solver::Solver)
    n,m,N = get_sizes(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    λ,μ,C = results.λ, results.μ, results.C
    y = results.λ_prev
    α_prev = results.nesterov[1]
    α = results.nesterov[2]
    α_next = (1+sqrt(1+4α^2))/2
    γ_k = -(1-α)/α_next

    for k = 1:N-1
        y_next = λ[k] + μ[k].*C[k]
        y_next = max.(solver.opts.dual_min, min.(solver.opts.dual_max, y_next))
        y_next[1:pI] = max.(0.0,y_next[1:pI])
        λ[k] = (1-γ_k)*y_next + γ_k*y[k]
        y[k] = y_next

        λ[k] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ[k]))
        λ[k][1:pI] = max.(0.0,results.λ[k][1:pI])
    end
    y_next = λ[N] + μ[N].*C[N]
    y_next = max.(solver.opts.dual_min, min.(solver.opts.dual_max, y_next))
    y_next[1:pI_N] = max.(0.0,y_next[1:pI_N])
    λ[N] = (1-γ_k)*y_next + γ_k*y[N]
    y[N] = y_next

    λ[N] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ[N]))
    λ[N][1:pI_N] = max.(0.0,results.λ[N][1:pI_N])

    results.nesterov[1] = α
    results.nesterov[2] = α_next
end

# """
# $(SIGNATURES)
#     Second order dual update - Buys Update
#     -UNDER DEVELOPMENT -
# """
# function Buys_λ_second_order_update!(results::SolverIterResults,solver::Solver,update::Bool=true)
#     bp = results.bp
#     n,m,N = get_sizes(solver)
#     n̄,nn = get_num_states(solver)
#     m̄,mm = get_num_controls(solver)
#     p,pI,pE = get_num_constraints(solver)
#     p_N,pI_N,pE_N = get_num_terminal_constraints(solver)
#
#     nm = nn + mm
#     Nz = nn*N + mm*(N-1)
#     Np = p*(N-1) + p_N
#     ∇²L = zeros(Nz,Nz)
#     ∇c = zeros(Np,Nz)
#
#     for k = 1:N
#         if k < N
#             idx = ((k-1)*nm + 1):k*nm
#             solver.opts.square_root ? Q = [bp.Qxx[k]'*bp.Qxx[k] bp.Qux[k]'; bp.Qux[k] bp.Quu[k]'*bp.Quu[k]] : Q = [bp.Qxx[k] bp.Qux[k]'; bp.Qux[k] bp.Quu[k]]
#             ∇²L[idx,idx] = Q
#
#             idx2 = ((k-1)*p + 1):k*p
#             ∇c[idx2,idx] = [results.Cx[k] results.Cu[k]]
#         else
#             idx = ((k-1)*nm + 1):Nz
#             solver.opts.square_root ? Q = results.S[N]'*results.S[N] : Q = results.S[N]
#             ∇²L[idx,idx] = Q
#
#             idx2 = ((k-1)*p + 1):Np
#             ∇c[idx2,idx] = results.Cx[N]
#         end
#     end
#
#     C = vcat(results.C...)
#     λ = vcat(results.λ...)
#     active_set = vcat(results.active_set...)
#
#     ∇c̄ = ∇c[active_set,:]
#
#     tmp = (∇c̄*(∇²L\∇c̄'))
#     λ[active_set] += tmp\C[active_set]
#
#     if update
#         # update the results
#         for k = 1:N
#             if k != N
#                 idx_pI = pI
#                 idx = (k-1)*p+1:k*p
#             else
#                 idx_pI = pI_N
#                 idx = (k-1)*p+1:Np
#             end
#             results.λ[k] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ[idx]))
#             results.λ[k][1:idx_pI] = max.(0.0,results.λ[k][1:idx_pI])
#         end
#         return nothing
#     else
#         return λ
#     end
# end
using Test

function Buys_λ_second_order_update!(results::SolverIterResults,solver::Solver,update::Bool=true)
    n,m,N = get_sizes(solver)
    n̄,nn = get_num_states(solver)
    m̄,mm = get_num_controls(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)
    update_constraints!(results,solver)

    X = results.X
    U = results.U
    Iμ = results.Iμ
    Cx = results.Cx
    Cu = results.Cu

    x0 = solver.obj.x0
    nm = nn + mm
    Nz = nn*N + mm*(N-1)
    Np = p*(N-1) + p_N
    Nu = mm*(N-1) # number of control decision variables u
    Ā = zeros(Nz,nn)
    B̄ = zeros(Nz,Nu)
    Q̄ = zeros(Nz,Nz)
    C̄ = zeros(Np,Nz)

    for k = 1:N
        # Calculate Ā
        k == 1 ? Ā[((k-1)*nm + 1):((k-1)*nm + nn),1:nn] = 1.0*Matrix(I,nn,nn) : Ā[((k-1)*nm + 1):((k-1)*nm + nn),1:nn] = prod(results.fdx[1:k-1])

        if k < N
            x = X[k][1:n]
            u = U[k][1:m]
            solver.state.minimum_time ? dt = U[k][m̄]^2 : dt = solver.dt
            expansion = taylor_expansion(solver.obj.cost,x,u)
            Q,R,H,q,r = expansion .* dt

            idx = ((k-1)*nm + 1):k*nm
            Q̄[idx,idx] = [Q H'; H R] + [Cx[k]'*Iμ[k]*Cx[k] Cx[k]'*Iμ[k]*Cu[k]; Cu[k]'*Iμ[k]*Cx[k] Cu[k]'*Iμ[k]*Cu[k]]

            idx2 = ((k-1)*p + 1):k*p
            C̄[idx2,idx] = [results.Cx[k] results.Cu[k]]
        else
            x = X[N][1:n]
            expansion = taylor_expansion(solver.obj.cost,x)
            Qf,qf = expansion

            idx = ((k-1)*nm + 1):Nz
            Q̄[idx,idx] = Qf + Cx[N]'*Iμ[N]*Cx[N]

            idx2 = ((k-1)*p + 1):Np
            C̄[idx2,idx] = results.Cx[N]
        end
    end

    for k = 1:N
        # Indices
        idx3 = ((k-1)*nm + 1):((k-1)*nm + n)
        idx4 = ((k-1)*nm + n + 1):k*nm
        idx5 = ((k-1)*m + 1):k*m

        # Calculate B̄
        if k > 1
            for j = 1:k-1
                idx7 = ((j-1)*m + 1):j*m
                j == k-1 ? B̄[idx3,idx7] = results.fdu[j][1:n,1:m] : B̄[idx3,idx7] = prod(results.fdx[j+1:(k-1)])*results.fdu[j][1:n,1:m]
            end
        end

        if k != N
            B̄[idx4,idx5] = 1.0*Matrix(I,m,m)
        end
    end

    # ū = vcat(results.U...)
    # z = B̄*ū + Ā*x0
    #
    # x̄ = [zeros(n) for i = 1:solver.N]
    # x̄[1] = x0
    # for k = 1:N-1
    #     x̄[k+1] = results.fdx[k]*x̄[k] + results.fdu[k]*results.U[k]
    # end
    # x̂ = [zeros(n) for i = 1:solver.N]
    # for k = 1:N
    #     k != N ? idx = ((k-1)*nm+1:(k-1)*nm+n) : idx = ((k-1)*nm+1:Nz)
    #     x̂[k] = z[idx]
    # end
    #
    # @test isapprox(to_array(x̄),to_array(x̂))
    # @test B̄[1:n,1:n] == zeros(n,n)
    # @test B̄[n+1:nm,1:m] == 1.0*Matrix(I,m,m)
    # @test B̄[nm+1:nm+n,1:m] == results.fdu[1][1:n,1:m]
    # @test B̄[nm+n+1:2*nm,m+1:2*m] == 1.0*Matrix(I,m,m)
    # @test B̄[(N-1)*nm+1:Nz,1:m] == prod(results.fdx[2:N-1])*results.fdu[1]
    # @test B̄[(N-1)*nm+1:Nz,m+1:2*m] == prod(results.fdx[3:N-1])*results.fdu[2]
    # @test B̄[(N-1)*nm+1:Nz,(N-2)*m+1:(N-1)*m] == results.fdu[N-1]

    ∇²L = B̄'*Q̄*B̄
    println("cond(∇²L): $(cond(∇²L))")
    ∇g = C̄*B̄

    C = vcat(results.C...)
    λ = vcat(results.λ...)
    active_set = vcat(results.active_set...)

    ∇ḡ = ∇g[active_set,:]
    #
    tmp = (∇ḡ*(∇²L\∇ḡ'))
    println("cond tmp: $(cond(tmp))")
    λ[active_set] += tmp\C[active_set]
    # λ += tmp\C

    if update
        # update the results
        for k = 1:N
            if k != N
                idx_pI = pI
                idx = (k-1)*p+1:k*p
            else
                idx_pI = pI_N
                idx = (k-1)*p+1:Np
            end
            results.λ[k] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ[idx]))
            results.λ[k][1:idx_pI] = max.(0.0,results.λ[k][1:idx_pI])
        end
        return nothing
    else
        return λ
    end
end

"""
$(SIGNATURES)
    Second order dual update - Batch Dual QP solve
    -UNDER DEVELOPMENT - currently not improving convergence rate
"""
function qp_λ_second_order_update!(results::SolverIterResults,solver::Solver)
    n = solver.model.n
    m = solver.model.m
    nm = n+m
    N = solver.N
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    Nz = n*N + m*(N-1) # number of decision variables x, u
    Nu = m*(N-1) # number of control decision variables u

    Np = p*(N-1) + p_N # number of constraints

    Mz = N + N-1 # number of decision vectors
    Mu = N-1 # number of control decision vectors

    B̄ = zeros(Nz,Nu)
    Ā = zeros(Nz,n)

    Q̄ = zeros(Nz,Nz)
    q̄ = zeros(Nz)

    C̄ = zeros(Np,Nz)
    c̄ = zeros(Np)

    λ_tmp = zeros(Np)
    idx_inequality = zeros(Bool,Np)
    active_set = zeros(Bool,Np)

    x0 = solver.obj.x0
    costfun = solver.obj.cost

    for k = 1:N
        x = results.X[k]

        if k != N
            u = results.U[k]
            Q,R,H,q,r = taylor_expansion(costfun,x,u) .* solver.dt
        else
            Qf,qf = taylor_expansion(costfun,x) .* solver.dt
        end

        # Indices
        idx = ((k-1)*nm + 1):k*nm
        idx2 = ((k-1)*p + 1):k*p
        idx3 = ((k-1)*nm + 1):((k-1)*nm + n)
        idx4 = ((k-1)*nm + n + 1):k*nm
        idx5 = ((k-1)*m + 1):k*m
        idx6 = ((k-1)*p + 1):((k-1)*p + pI)

        # Calculate Ā
        k == 1 ? Ā[idx3,1:n] = 1.0*Matrix(I,n,n) : Ā[idx3,1:n] = prod(results.fdx[1:k-1])

        # Calculate B̄
        if k > 1
            for j = 1:k-1
                idx7 = ((j-1)*m + 1):j*m
                j == k-1 ? B̄[idx3,idx7] = results.fdu[j][1:n,1:m] : B̄[idx3,idx7] = prod(results.fdx[j+1:(k-1)])*results.fdu[j][1:n,1:m]
            end
        end

        # Calculate Q̄, q̄, C̄, c̄
        if k != N
            Q̄[idx,idx] = [Q H';H R]
            q̄[idx] = [q;r]

            G = results.Cx[k]
            H = results.Cu[k]
            C̄[idx2,idx] = [G H]
            c̄[idx2] = results.C[k]

            λ_tmp[idx2] = results.λ[k]
            idx_inequality[idx6] .= true
            active_set[idx2] = results.active_set[k]

            B̄[idx4,idx5] = 1.0*Matrix(I,m,m)
        else
            idx = ((k-1)*nm + 1):Nz
            Q̄[idx,idx] = Qf
            q̄[idx] = qf

            idx2 = ((k-1)*p + 1):Np
            C̄[idx2,idx] = results.Cx[N]
            c̄[idx2] = results.C[N]

            idx6 = ((k-1)*p + 1):((k-1)*p + pI_N)

            λ_tmp[idx2] = results.λ[N]
            idx_inequality[idx6] .= true
            active_set[idx2] .= results.active_set[N]
        end

    end
    N_active_set = sum(active_set)

    # if verbose
    #     # Tests
    #     @test Ā[1:n,:] == Matrix(I,n,n)
    #     k = 3
    #     @test Ā[(k-1)*nm+1:(k-1)*nm+n,:] == prod(results.fdx[1:2])
    #     @test results.fdx[1]*results.fdx[2] == prod(results.fdx[1:2])
    #
    #     k = 7
    #     @test Ā[(k-1)*nm+1:(k-1)*nm+n,:] == prod(results.fdx[1:k-1])
    #     @test Ā[(N-1)*nm+1:Nz,:] == prod(results.fdx[1:N-1])
    #
    #     @test B̄[1:n,1:n] == zeros(n,n)
    #     @test B̄[n+1:nm,1:m] == 1.0*Matrix(I,m,m)
    #     @test B̄[nm+1:nm+n,1:m] == results.fdu[1][1:n,1:m]
    #     @test B̄[nm+n+1:2*nm,m+1:2*m] == 1.0*Matrix(I,m,m)
    #     @test B̄[(N-1)*nm+1:Nz,1:m] == prod(results.fdx[2:N-1])*results.fdu[1]
    #     @test B̄[(N-1)*nm+1:Nz,m+1:2*m] == prod(results.fdx[3:N-1])*results.fdu[2]
    #     @test B̄[(N-1)*nm+1:Nz,(N-2)*m+1:(N-1)*m] == results.fdu[N-1]
    #
    #     k = 1
    #     Q,R,H,q,r = taylor_expansion(costfun,results.X[k],results.U[k])
    #     @test Q̄[1:nm,1:nm] == [Q H'; H R]
    #     @test q̄[1:nm] == [q;r]
    #
    #     k = 13
    #     Q,R,H,q,r = taylor_expansion(costfun,results.X[k],results.U[k])
    #     @test Q̄[(k-1)*nm+1:k*nm,(k-1)*nm+1:k*nm] == [Q H'; H R]
    #     @test q̄[(k-1)*nm+1:k*nm] == [q;r]
    #
    #     k = N
    #     Qf,qf = taylor_expansion(costfun,results.X[k])
    #     @test Q̄[(k-1)*nm+1:Nz,(k-1)*nm+1:Nz] == Qf
    #     @test q̄[(k-1)*nm+1:Nz] == qf
    #
    #     @test C̄[1:p,1:nm] == [results.Cx[1] results.Cu[1]]
    #
    #     k = 9
    #     @test C̄[(k-1)*p+1:k*p,(k-1)*nm+1:k*nm] == [results.Cx[k] results.Cu[k]]
    #     @test C̄[(N-1)*p+1:Np,(N-1)*nm+1:Nz] == results.Cx[N]
    #
    #     @test c̄[1:p] == results.C[1]
    #
    #     k = 17
    #     @test c̄[(k-1)*p+1:k*p] == results.C[k]
    #     @test c̄[(N-1)*p+1:Np] == results.C[N]
    #
    #     @test all(idx_inequality[1:pI] .== true)
    #     @test all(idx_inequality[pI+1:p] .== false)
    #     k = 12
    #
    #     @test all(idx_inequality[(k-1)*p+1:(k-1)*p+pI] .== true)
    #     idx_inequality[(k-1)*p+1:(k-1)*p+pI]
    #     @test all(idx_inequality[(N-1)*p+1:(N-1)*pI_N] .== true)
    # end
    ū = -(B̄'*Q̄*B̄)\(B̄'*(q̄ + Q̄*Ā*x0) + B̄'*C̄'*λ_tmp)

    # println(any(isnan.(q̄)))
    # println(any(isnan.(Ā)))
    # println(any(isnan.(λ_tmp)))
    # println(any(isnan.(ū)))
    # println(any(isnan.(Ā)))
    # println(any(isnan.(B̄)))
    # println(any(isnan.(C̄)))
    # println(any(isnan.(c̄)))
    P = B̄'*Q̄*B̄
    M = q̄ + Q̄*Ā*x0
    # println(rank(P))
    # println(size(P))
    # println(λ_tmp)
    L = 0.5*ū'*P*ū + M'*B̄*ū + λ_tmp'*(C̄*B̄*ū + C̄*Ā*x0 + c̄)

    D = 0.5*M'*B̄*inv(P')*B̄'*M + 0.5*M'*B̄*inv(P')*B̄'*C̄'*λ_tmp + 0.5*λ_tmp'*C̄*B̄*inv(P')*B̄'*M + 0.5*λ_tmp'*C̄*B̄*inv(P')*B̄'*C̄'*λ_tmp - M'*B̄*inv(P)*B̄'*M - M'*B̄*inv(P)*B̄'*C̄'*λ_tmp - λ_tmp'*C̄*B̄*inv(P)*B̄'*M - λ_tmp'*C̄*B̄*inv(P)*B̄'*C̄'*λ_tmp + λ_tmp'*C̄*Ā*x0 + λ_tmp'*c̄
    Q_dual = C̄*B̄*inv(P')*B̄'*C̄' - 2*C̄*B̄*inv(P)*B̄'*C̄'
    q_dual = M'*B̄*inv(P')*B̄'*C̄' - 2*M'*B̄*inv(P)*B̄'*C̄' + x0'*Ā'*C̄' + c̄'
    qq_dual = 0.5*M'*B̄*inv(P')*B̄'*M - M'*B̄*inv(P)*B̄'*M
    DD = 0.5*λ_tmp'*Q_dual*λ_tmp + q_dual*λ_tmp + qq_dual

    # @test isapprox(D,L)
    # @test isapprox(DD,L)

    # solve QP
    m = JuMP.Model(solver=IpoptSolver(print_level=0))
    # m = JuMP.Model(solver=ClpSolver())

    # @variable(m, u[1:Nu])
    #
    # @objective(m, Min, 0.5*u'*P*u + M'*B̄*u)
    # @constraint(m, con, C̄*B̄*u + C̄*Ā*x0 + c̄ .== 0.)

    # @variable(m, λ[1:Np])
    N_active_set = sum(active_set)

    @variable(m, λ[1:N_active_set])

    # @objective(m, Min, λ'*λ)
    # @constraint(m, con, Q_dual[active_set,active_set]*λ + q_dual[active_set] .== 0.)
    #
    @objective(m, Max, 0.5*λ'*Q_dual[active_set,active_set]*λ + q_dual[active_set]'*λ)
    @constraint(m, con2, λ[idx_inequality[active_set]] .>= 0)
    #
    # # print(m)
    #
    status = JuMP.solve(m)
    #
    if status != :Optimal
        error("QP failed")
    end

    # Solution
    # println("Objective value: ", JuMP.getobjectivevalue(m))
    # println("λ = ", JuMP.getvalue(λ))

    λ_tmp[active_set] = JuMP.getvalue(λ)

    for k = 1:N
        if k != N
            idx = ((k-1)*p + 1):k*p
            results.λ[k] = λ_tmp[idx]#JuMP.getvalue(λ[idx])
            # results.λ[k][1:pI] = max.(0.0,results.λ[k][1:pI])
        else
            idx = ((N-1)*p + 1):Np
            results.λ[k] = λ_tmp[idx]#JuMP.getvalue(λ[idx])
            # results.λ[k][1:pI_N] = max.(0.0,results.λ[k][1:pI_N])
        end

        # tolerance check
        results.λ[k][abs.(results.λ[k]) .< 1e-8] .= 0.
    end
end

""" $(SIGNATURES) Penalty update """
function μ_update!(results::ConstrainedIterResults,solver::Solver)
    if solver.opts.outer_loop_update_type ∈ [:default,:accelerated, :momentum]
        μ_update_default!(results,solver)
    elseif solver.opts.outer_loop_update_type == :individual
        μ_update_individual!(results,solver)
    end
    return nothing
end

""" $(SIGNATURES) Penalty update scheme ('default') - all penalty terms are updated"""
function μ_update_default!(results::ConstrainedIterResults,solver::Solver)
    n,m,N = get_sizes(solver)
    for k = 1:N
        results.μ[k] = min.(solver.opts.penalty_max, solver.opts.penalty_scaling*results.μ[k])
    end
    return nothing
end

""" @(SIGNATURES) Penalty update scheme ('individual')- all penalty terms are updated uniquely according to indiviual improvement compared to previous iteration"""
function μ_update_individual!(results::ConstrainedIterResults,solver::Solver)
    n,m,N = get_sizes(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    constraint_decrease_ratio = solver.opts.constraint_decrease_ratio
    penalty_max = solver.opts.penalty_max
    penalty_scaling_no  = solver.opts.penalty_scaling_no
    penalty_scaling = solver.opts.penalty_scaling

    # Stage constraints
    for k = 1:N
        if k != N
            _p = p
            _pI = pI
        else
            _p = p_N
            _pI = pI_N
        end
        for i = 1:_p
            if i <= _pI
                if max(0.0,results.C[k][i]) <= constraint_decrease_ratio*max(0.0,results.C_prev[k][i])
                    results.μ[k][i] = min(penalty_max, penalty_scaling_no*results.μ[k][i])
                else
                    results.μ[k][i] = min(penalty_max, penalty_scaling*results.μ[k][i])
                end
            else
                if abs(results.C[k][i]) <= constraint_decrease_ratio*abs(results.C_prev[k][i])
                    results.μ[k][i] = min(penalty_max, penalty_scaling_no*results.μ[k][i])
                else
                    results.μ[k][i] = min(penalty_max, penalty_scaling*results.μ[k][i])
                end
            end
        end
    end
    return nothing
end

function feedback_outer_loop_update!(results::ConstrainedIterResults,solver::Solver)
    n,m,N = get_sizes(solver)
    p,pI,pE = get_num_constraints(solver)
    p_N,pI_N,pE_N = get_num_terminal_constraints(solver)

    constraint_decrease_ratio = solver.opts.constraint_decrease_ratio
    penalty_max = solver.opts.penalty_max
    penalty_scaling_no  = solver.opts.penalty_scaling_no
    penalty_scaling = solver.opts.penalty_scaling
    use_nesterov = solver.opts.use_nesterov

    if use_nesterov
        λ,μ,C = results.λ, results.μ, results.C
        y = results.λ_prev
        α_prev = results.nesterov[1]
        α = results.nesterov[2]
        α_next = (1+sqrt(1+4α^2))/2
        γ_k = -(1-α)/α_next
    end

    if solver.state.second_order_dual_update
        λ = Buys_λ_second_order_update!(results,solver,false)
    end

    # Stage constraints
    for k = 1:N
        if k != N
            _p = p
            _pI = pI
        else
            _p = p_N
            _pI = pI_N
        end
        for i = 1:_p
            if i <= _pI
                if max(0.0,results.C[k][i]) <= constraint_decrease_ratio*max(0.0,results.C_prev[k][i])
                    if use_nesterov
                        y_next = λ[k][i] + μ[k][i].*C[k][i]
                        y_next = max.(solver.opts.dual_min, min.(solver.opts.dual_max, y_next))
                        y_next = max.(0.0,y_next)

                        λ[k][i] = (1-γ_k)*y_next + γ_k*y[k][i]
                        y[k][i] = y_next
                    # else
                    #     results.λ[k][i] += results.μ[k][i]*results.C[k][i]
                    end
                    if solver.state.second_order_dual_update
                        # λ_second_order_update!(results,solver,i,k)

                        idx = (k-1)*p+i

                        results.λ[k][i] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ[idx]))
                        results.λ[k][i] = max.(0.0,results.λ[k][i])
                    else
                        λ_update_default!(results,solver,i,k)
                    end

                    # results.λ[k][i] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, results.λ[k][i]))
                    # results.λ[k][i] = max.(0.0,results.λ[k][i])
                    !solver.state.second_order_dual_update ? results.μ[k][i] = min(penalty_max, penalty_scaling_no*results.μ[k][i]) : nothing
                else
                    !solver.state.second_order_dual_update ? results.μ[k][i] = min(penalty_max, penalty_scaling*results.μ[k][i]) : nothing
                end
            else
                if abs(results.C[k][i]) <= constraint_decrease_ratio*abs(results.C_prev[k][i])
                    if use_nesterov
                        y_next = λ[k][i] + μ[k][i].*C[k][i]
                        y_next = max.(solver.opts.dual_min, min.(solver.opts.dual_max, y_next))

                        λ[k][i] = (1-γ_k)*y_next + γ_k*y[k][i]
                        y[k][i] = y_next
                    # else
                    #     results.λ[k][i] += results.μ[k][i]*results.C[k][i]
                    end

                    # results.λ[k][i] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, results.λ[k][i]))

                    if solver.state.second_order_dual_update
                        idx = (k-1)*p+i
                        results.λ[k][i] = max.(solver.opts.dual_min, min.(solver.opts.dual_max, λ[idx]))
                    else
                        λ_update_default!(results,solver,i,k)
                    end

                    !solver.state.second_order_dual_update ? results.μ[k][i] = min(penalty_max, penalty_scaling_no*results.μ[k][i]) : nothing
                else
                    !solver.state.second_order_dual_update ? results.μ[k][i] = min(penalty_max, penalty_scaling*results.μ[k][i]) : nothing
                end
            end
        end
    end
    if use_nesterov
        results.nesterov[1] = α
        results.nesterov[2] = α_next
    end
    return nothing
end

λ_second_order_update! = qp_λ_second_order_update!#Buys_λ_second_order_update!

"""
$(SIGNATURES)
    Updates penalty (μ) and Lagrange multiplier (λ) parameters for Augmented Lagrangian method
"""
function outer_loop_update(results::ConstrainedIterResults,solver::Solver,k::Int=0)::Nothing

    if solver.opts.outer_loop_update_type == :default
        if solver.opts.use_nesterov
            λ_update_nesterov!(results,solver)
        else
            solver.state.second_order_dual_update ? λ_second_order_update!(results,solver) : λ_update_default!(results,solver)
        end
        if !solver.state.second_order_dual_update
            k % solver.opts.penalty_update_frequency == 0 ? μ_update_default!(results,solver) : nothing
        end
    elseif solver.opts.outer_loop_update_type == :individual
        λ_update_default!(results,solver)
        k % solver.opts.penalty_update_frequency == 0 ? μ_update_individual!(results,solver) : nothing

    # elseif solver.opts.outer_loop_update_type == :accelerated
    #     λ_update_accel!(results,solver)
    #     k % solver.opts.penalty_update_frequency == 0 ? μ_update_default!(results,solver) : nothing

    elseif solver.opts.outer_loop_update_type == :momentum
        λ_update_momentum!(results,solver)
        k % solver.opts.penalty_update_frequency == 0 ? μ_update_default!(results,solver) : nothing

    elseif solver.opts.outer_loop_update_type == :feedback
        if solver.state.penalty_only
            μ_update_default!(results,solver)
        else
            feedback_outer_loop_update!(results,solver)
        end
    end

    ## Store current constraints evaluations for next outer loop update
    results.C_prev .= deepcopy(results.C)

    # reset regularization
    results.ρ[1] = 0.
    return nothing
end

function outer_loop_update(results::UnconstrainedIterResults,solver::Solver, k::Int=0)::Nothing
    return nothing
end


text:
"""
    theoretical_distribution(;input::Input, interf::Interferometer, i=nothing)

Compute the probability distribution of all possible output configurations of
fully/partially-indistinguishable photons through a lossless interferometer.

!!! note
    - The probabilities within the distribution are indexed following the same order as `output_mode_occupation(n,m)`
    - If `i` (with default value `nothing`) is set to an integer
      `theoretical_distribution` returns the probability to find the photons in
      the i'th configuration given by `output_mode_occupation`
"""
function theoretical_distribution(;input::Input, interf::Interferometer, i=nothing)

    input_modes = input.r.state
    number_photons = input.n
    number_modes = input.m

    if get_parametric_type(input)[1] == OneParameterInterpolation
        distinguishability = input.distinguishability_param
    else
        get_parametric_type(input)[1] == Bosonic ? distinguishability = 1.0 : distinguishability = 0.0
    end

    U = interf.U
    S = input.G.S

    input_event = fill_arrangement(input_modes)
    output_events = output_mode_occupation(number_photons, number_modes)

    function compute_pi(event)

        M = U[input_event, event]
        output_modes = fill_arrangement(event)

        if distinguishability == 1 ||distinguishability == 0
            return abs(ryser(M)).^2
        else
            W = Array{eltype(S)}(undef, (number_photons, number_photons, number_photons))
            for rr in 1:number_photons
                for ss in 1:number_photons
                    for j in 1:number_photons
                        W[rr,ss,j] = real(M[ss,j] * conj(M[rr,j]) * S[rr,ss])
                    end
                end
            end
            return ryser_tensor(W)
        end

    end

    complete_distribution() = map(e->compute_pi(e)/factorial(number_photons), output_events)

    if i == nothing
        return complete_distribution()
    else
        return compute_pi(output_events[i])
    end

end

ext: jl


text:
#==============================================================================#
# MigrationHub.jl
#
# This file is generated from:
# https://github.com/aws/aws-sdk-js/blob/master/apis/AWSMigrationHub-2017-05-31.normal.json
#==============================================================================#

__precompile__()

module MigrationHub

using AWSCore

"""
    using AWSSDK.MigrationHub.associate_created_artifact
    associate_created_artifact([::AWSConfig], arguments::Dict)
    associate_created_artifact([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, CreatedArtifact=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "AssociateCreatedArtifact", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "AssociateCreatedArtifact", ProgressUpdateStream=, MigrationTaskName=, CreatedArtifact=, <keyword arguments>)

# AssociateCreatedArtifact Operation

Associates a created artifact of an AWS cloud resource, the target receiving the migration, with the migration task performed by a migration tool. This API has the following traits:

*   Migration tools can call the `AssociateCreatedArtifact` operation to indicate which AWS artifact is associated with a migration task.

*   The created artifact name must be provided in ARN (Amazon Resource Name) format which will contain information about type and region; for example: `arn:aws:ec2:us-east-1:488216288981:image/ami-6d0ba87b`.

*   Examples of the AWS resource behind the created artifact are, AMI's, EC2 instance, or DMS endpoint, etc.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
Unique identifier that references the migration task.

## `CreatedArtifact = [ ... ]` -- *Required*
An ARN of the AWS resource related to the migration (e.g., AMI, EC2 instance, RDS instance, etc.)
```
 CreatedArtifact = [
        "Name" => <required> ::String,
        "Description" =>  ::String
    ]
```

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`AssociateCreatedArtifactResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/AssociateCreatedArtifact)
"""
@inline associate_created_artifact(aws::AWSConfig=default_aws_config(); args...) = associate_created_artifact(aws, args)

@inline associate_created_artifact(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "AssociateCreatedArtifact", args)

@inline associate_created_artifact(args) = associate_created_artifact(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.associate_discovered_resource
    associate_discovered_resource([::AWSConfig], arguments::Dict)
    associate_discovered_resource([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, DiscoveredResource=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "AssociateDiscoveredResource", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "AssociateDiscoveredResource", ProgressUpdateStream=, MigrationTaskName=, DiscoveredResource=, <keyword arguments>)

# AssociateDiscoveredResource Operation

Associates a discovered resource ID from Application Discovery Service (ADS) with a migration task.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
The identifier given to the MigrationTask.

## `DiscoveredResource = [ ... ]` -- *Required*
Object representing a Resource.
```
 DiscoveredResource = [
        "ConfigurationId" => <required> ::String,
        "Description" =>  ::String
    ]
```

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`AssociateDiscoveredResourceResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException`, `PolicyErrorException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/AssociateDiscoveredResource)
"""
@inline associate_discovered_resource(aws::AWSConfig=default_aws_config(); args...) = associate_discovered_resource(aws, args)

@inline associate_discovered_resource(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "AssociateDiscoveredResource", args)

@inline associate_discovered_resource(args) = associate_discovered_resource(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.create_progress_update_stream
    create_progress_update_stream([::AWSConfig], arguments::Dict)
    create_progress_update_stream([::AWSConfig]; ProgressUpdateStreamName=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "CreateProgressUpdateStream", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "CreateProgressUpdateStream", ProgressUpdateStreamName=, <keyword arguments>)

# CreateProgressUpdateStream Operation

Creates a progress update stream which is an AWS resource used for access control as well as a namespace for migration task names that is implicitly linked to your AWS account. It must uniquely identify the migration tool as it is used for all updates made by the tool; however, it does not need to be unique for each AWS account because it is scoped to the AWS account.

# Arguments

## `ProgressUpdateStreamName = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`CreateProgressUpdateStreamResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation` or `InvalidInputException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/CreateProgressUpdateStream)
"""
@inline create_progress_update_stream(aws::AWSConfig=default_aws_config(); args...) = create_progress_update_stream(aws, args)

@inline create_progress_update_stream(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "CreateProgressUpdateStream", args)

@inline create_progress_update_stream(args) = create_progress_update_stream(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.delete_progress_update_stream
    delete_progress_update_stream([::AWSConfig], arguments::Dict)
    delete_progress_update_stream([::AWSConfig]; ProgressUpdateStreamName=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "DeleteProgressUpdateStream", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "DeleteProgressUpdateStream", ProgressUpdateStreamName=, <keyword arguments>)

# DeleteProgressUpdateStream Operation

Deletes a progress update stream, including all of its tasks, which was previously created as an AWS resource used for access control. This API has the following traits:

*   The only parameter needed for `DeleteProgressUpdateStream` is the stream name (same as a `CreateProgressUpdateStream` call).

*   The call will return, and a background process will asynchronously delete the stream and all of its resources (tasks, associated resources, resource attributes, created artifacts).

*   If the stream takes time to be deleted, it might still show up on a `ListProgressUpdateStreams` call.

*   `CreateProgressUpdateStream`, `ImportMigrationTask`, `NotifyMigrationTaskState`, and all Associate[*] APIs realted to the tasks belonging to the stream will throw "InvalidInputException" if the stream of the same name is in the process of being deleted.

*   Once the stream and all of its resources are deleted, `CreateProgressUpdateStream` for a stream of the same name will succeed, and that stream will be an entirely new logical resource (without any resources associated with the old stream).

# Arguments

## `ProgressUpdateStreamName = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`DeleteProgressUpdateStreamResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/DeleteProgressUpdateStream)
"""
@inline delete_progress_update_stream(aws::AWSConfig=default_aws_config(); args...) = delete_progress_update_stream(aws, args)

@inline delete_progress_update_stream(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "DeleteProgressUpdateStream", args)

@inline delete_progress_update_stream(args) = delete_progress_update_stream(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.describe_application_state
    describe_application_state([::AWSConfig], arguments::Dict)
    describe_application_state([::AWSConfig]; ApplicationId=)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "DescribeApplicationState", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "DescribeApplicationState", ApplicationId=)

# DescribeApplicationState Operation

Gets the migration status of an application.

# Arguments

## `ApplicationId = ::String` -- *Required*
The configurationId in ADS that uniquely identifies the grouped application.

# Returns

`DescribeApplicationStateResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `InvalidInputException`, `PolicyErrorException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/DescribeApplicationState)
"""
@inline describe_application_state(aws::AWSConfig=default_aws_config(); args...) = describe_application_state(aws, args)

@inline describe_application_state(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "DescribeApplicationState", args)

@inline describe_application_state(args) = describe_application_state(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.describe_migration_task
    describe_migration_task([::AWSConfig], arguments::Dict)
    describe_migration_task([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "DescribeMigrationTask", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "DescribeMigrationTask", ProgressUpdateStream=, MigrationTaskName=)

# DescribeMigrationTask Operation

Retrieves a list of all attributes associated with a specific migration task.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
The identifier given to the MigrationTask.

# Returns

`DescribeMigrationTaskResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/DescribeMigrationTask)
"""
@inline describe_migration_task(aws::AWSConfig=default_aws_config(); args...) = describe_migration_task(aws, args)

@inline describe_migration_task(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "DescribeMigrationTask", args)

@inline describe_migration_task(args) = describe_migration_task(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.disassociate_created_artifact
    disassociate_created_artifact([::AWSConfig], arguments::Dict)
    disassociate_created_artifact([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, CreatedArtifactName=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "DisassociateCreatedArtifact", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "DisassociateCreatedArtifact", ProgressUpdateStream=, MigrationTaskName=, CreatedArtifactName=, <keyword arguments>)

# DisassociateCreatedArtifact Operation

Disassociates a created artifact of an AWS resource with a migration task performed by a migration tool that was previously associated. This API has the following traits:

*   A migration user can call the `DisassociateCreatedArtifacts` operation to disassociate a created AWS Artifact from a migration task.

*   The created artifact name must be provided in ARN (Amazon Resource Name) format which will contain information about type and region; for example: `arn:aws:ec2:us-east-1:488216288981:image/ami-6d0ba87b`.

*   Examples of the AWS resource behind the created artifact are, AMI's, EC2 instance, or RDS instance, etc.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
Unique identifier that references the migration task to be disassociated with the artifact.

## `CreatedArtifactName = ::String` -- *Required*
An ARN of the AWS resource related to the migration (e.g., AMI, EC2 instance, RDS instance, etc.)

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`DisassociateCreatedArtifactResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/DisassociateCreatedArtifact)
"""
@inline disassociate_created_artifact(aws::AWSConfig=default_aws_config(); args...) = disassociate_created_artifact(aws, args)

@inline disassociate_created_artifact(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "DisassociateCreatedArtifact", args)

@inline disassociate_created_artifact(args) = disassociate_created_artifact(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.disassociate_discovered_resource
    disassociate_discovered_resource([::AWSConfig], arguments::Dict)
    disassociate_discovered_resource([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, ConfigurationId=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "DisassociateDiscoveredResource", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "DisassociateDiscoveredResource", ProgressUpdateStream=, MigrationTaskName=, ConfigurationId=, <keyword arguments>)

# DisassociateDiscoveredResource Operation

Disassociate an Application Discovery Service (ADS) discovered resource from a migration task.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
The identifier given to the MigrationTask.

## `ConfigurationId = ::String` -- *Required*
ConfigurationId of the ADS resource to be disassociated.

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`DisassociateDiscoveredResourceResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/DisassociateDiscoveredResource)
"""
@inline disassociate_discovered_resource(aws::AWSConfig=default_aws_config(); args...) = disassociate_discovered_resource(aws, args)

@inline disassociate_discovered_resource(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "DisassociateDiscoveredResource", args)

@inline disassociate_discovered_resource(args) = disassociate_discovered_resource(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.import_migration_task
    import_migration_task([::AWSConfig], arguments::Dict)
    import_migration_task([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "ImportMigrationTask", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "ImportMigrationTask", ProgressUpdateStream=, MigrationTaskName=, <keyword arguments>)

# ImportMigrationTask Operation

Registers a new migration task which represents a server, database, etc., being migrated to AWS by a migration tool.

This API is a prerequisite to calling the `NotifyMigrationTaskState` API as the migration tool must first register the migration task with Migration Hub.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
Unique identifier that references the migration task.

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`ImportMigrationTaskResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/ImportMigrationTask)
"""
@inline import_migration_task(aws::AWSConfig=default_aws_config(); args...) = import_migration_task(aws, args)

@inline import_migration_task(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "ImportMigrationTask", args)

@inline import_migration_task(args) = import_migration_task(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.list_created_artifacts
    list_created_artifacts([::AWSConfig], arguments::Dict)
    list_created_artifacts([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "ListCreatedArtifacts", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "ListCreatedArtifacts", ProgressUpdateStream=, MigrationTaskName=, <keyword arguments>)

# ListCreatedArtifacts Operation

Lists the created artifacts attached to a given migration task in an update stream. This API has the following traits:

*   Gets the list of the created artifacts while migration is taking place.

*   Shows the artifacts created by the migration tool that was associated by the `AssociateCreatedArtifact` API.

*   Lists created artifacts in a paginated interface.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
Unique identifier that references the migration task.

## `NextToken = ::String`
If a `NextToken` was returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `NextToken`.

## `MaxResults = ::Int`
Maximum number of results to be returned per page.

# Returns

`ListCreatedArtifactsResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/ListCreatedArtifacts)
"""
@inline list_created_artifacts(aws::AWSConfig=default_aws_config(); args...) = list_created_artifacts(aws, args)

@inline list_created_artifacts(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "ListCreatedArtifacts", args)

@inline list_created_artifacts(args) = list_created_artifacts(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.list_discovered_resources
    list_discovered_resources([::AWSConfig], arguments::Dict)
    list_discovered_resources([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "ListDiscoveredResources", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "ListDiscoveredResources", ProgressUpdateStream=, MigrationTaskName=, <keyword arguments>)

# ListDiscoveredResources Operation

Lists discovered resources associated with the given `MigrationTask`.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
The name of the MigrationTask.

## `NextToken = ::String`
If a `NextToken` was returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `NextToken`.

## `MaxResults = ::Int`
The maximum number of results returned per page.

# Returns

`ListDiscoveredResourcesResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/ListDiscoveredResources)
"""
@inline list_discovered_resources(aws::AWSConfig=default_aws_config(); args...) = list_discovered_resources(aws, args)

@inline list_discovered_resources(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "ListDiscoveredResources", args)

@inline list_discovered_resources(args) = list_discovered_resources(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.list_migration_tasks
    list_migration_tasks([::AWSConfig], arguments::Dict)
    list_migration_tasks([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "ListMigrationTasks", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "ListMigrationTasks", <keyword arguments>)

# ListMigrationTasks Operation

Lists all, or filtered by resource name, migration tasks associated with the user account making this call. This API has the following traits:

*   Can show a summary list of the most recent migration tasks.

*   Can show a summary list of migration tasks associated with a given discovered resource.

*   Lists migration tasks in a paginated interface.

# Arguments

## `NextToken = ::String`
If a `NextToken` was returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `NextToken`.

## `MaxResults = ::Int`
Value to specify how many results are returned per page.

## `ResourceName = ::String`
Filter migration tasks by discovered resource name.

# Returns

`ListMigrationTasksResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `InvalidInputException`, `PolicyErrorException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/ListMigrationTasks)
"""
@inline list_migration_tasks(aws::AWSConfig=default_aws_config(); args...) = list_migration_tasks(aws, args)

@inline list_migration_tasks(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "ListMigrationTasks", args)

@inline list_migration_tasks(args) = list_migration_tasks(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.list_progress_update_streams
    list_progress_update_streams([::AWSConfig], arguments::Dict)
    list_progress_update_streams([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "ListProgressUpdateStreams", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "ListProgressUpdateStreams", <keyword arguments>)

# ListProgressUpdateStreams Operation

Lists progress update streams associated with the user account making this call.

# Arguments

## `NextToken = ::String`
If a `NextToken` was returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `NextToken`.

## `MaxResults = ::Int`
Filter to limit the maximum number of results to list per page.

# Returns

`ListProgressUpdateStreamsResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException` or `InvalidInputException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/ListProgressUpdateStreams)
"""
@inline list_progress_update_streams(aws::AWSConfig=default_aws_config(); args...) = list_progress_update_streams(aws, args)

@inline list_progress_update_streams(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "ListProgressUpdateStreams", args)

@inline list_progress_update_streams(args) = list_progress_update_streams(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.notify_application_state
    notify_application_state([::AWSConfig], arguments::Dict)
    notify_application_state([::AWSConfig]; ApplicationId=, Status=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "NotifyApplicationState", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "NotifyApplicationState", ApplicationId=, Status=, <keyword arguments>)

# NotifyApplicationState Operation

Sets the migration state of an application. For a given application identified by the value passed to `ApplicationId`, its status is set or updated by passing one of three values to `Status`: `NOT_STARTED | IN_PROGRESS | COMPLETED`.

# Arguments

## `ApplicationId = ::String` -- *Required*
The configurationId in ADS that uniquely identifies the grouped application.

## `Status = "NOT_STARTED", "IN_PROGRESS" or "COMPLETED"` -- *Required*
Status of the application - Not Started, In-Progress, Complete.

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`NotifyApplicationStateResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException`, `PolicyErrorException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/NotifyApplicationState)
"""
@inline notify_application_state(aws::AWSConfig=default_aws_config(); args...) = notify_application_state(aws, args)

@inline notify_application_state(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "NotifyApplicationState", args)

@inline notify_application_state(args) = notify_application_state(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.notify_migration_task_state
    notify_migration_task_state([::AWSConfig], arguments::Dict)
    notify_migration_task_state([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, Task=, UpdateDateTime=, NextUpdateSeconds=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "NotifyMigrationTaskState", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "NotifyMigrationTaskState", ProgressUpdateStream=, MigrationTaskName=, Task=, UpdateDateTime=, NextUpdateSeconds=, <keyword arguments>)

# NotifyMigrationTaskState Operation

Notifies Migration Hub of the current status, progress, or other detail regarding a migration task. This API has the following traits:

*   Migration tools will call the `NotifyMigrationTaskState` API to share the latest progress and status.

*   `MigrationTaskName` is used for addressing updates to the correct target.

*   `ProgressUpdateStream` is used for access control and to provide a namespace for each migration tool.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
Unique identifier that references the migration task.

## `Task = [ ... ]` -- *Required*
Information about the task's progress and status.
```
 Task = [
        "Status" => <required> "NOT_STARTED", "IN_PROGRESS", "FAILED" or "COMPLETED",
        "StatusDetail" =>  ::String,
        "ProgressPercent" =>  ::Int
    ]
```

## `UpdateDateTime = timestamp` -- *Required*
The timestamp when the task was gathered.

## `NextUpdateSeconds = ::Int` -- *Required*
Number of seconds after the UpdateDateTime within which the Migration Hub can expect an update. If Migration Hub does not receive an update within the specified interval, then the migration task will be considered stale.

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`NotifyMigrationTaskStateResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/NotifyMigrationTaskState)
"""
@inline notify_migration_task_state(aws::AWSConfig=default_aws_config(); args...) = notify_migration_task_state(aws, args)

@inline notify_migration_task_state(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "NotifyMigrationTaskState", args)

@inline notify_migration_task_state(args) = notify_migration_task_state(default_aws_config(), args)

"""
    using AWSSDK.MigrationHub.put_resource_attributes
    put_resource_attributes([::AWSConfig], arguments::Dict)
    put_resource_attributes([::AWSConfig]; ProgressUpdateStream=, MigrationTaskName=, ResourceAttributeList=, <keyword arguments>)

    using AWSCore.Services.AWSMigrationHub
    AWSMigrationHub([::AWSConfig], "PutResourceAttributes", arguments::Dict)
    AWSMigrationHub([::AWSConfig], "PutResourceAttributes", ProgressUpdateStream=, MigrationTaskName=, ResourceAttributeList=, <keyword arguments>)

# PutResourceAttributes Operation

Provides identifying details of the resource being migrated so that it can be associated in the Application Discovery Service (ADS)'s repository. This association occurs asynchronously after `PutResourceAttributes` returns.

**Important**
> *   Keep in mind that subsequent calls to PutResourceAttributes will override previously stored attributes. For example, if it is first called with a MAC address, but later, it is desired to *add* an IP address, it will then be required to call it with *both* the IP and MAC addresses to prevent overiding the MAC address.

*   Note the instructions regarding the special use case of the [`ResourceAttributeList`](https://docs.aws.amazon.com/migrationhub/latest/ug/API_PutResourceAttributes.html#migrationhub-PutResourceAttributes-request-ResourceAttributeList) parameter when specifying any "VM" related value.

**Note**
> Because this is an asynchronous call, it will always return 200, whether an association occurs or not. To confirm if an association was found based on the provided details, call `ListDiscoveredResources`.

# Arguments

## `ProgressUpdateStream = ::String` -- *Required*
The name of the ProgressUpdateStream.

## `MigrationTaskName = ::String` -- *Required*
Unique identifier that references the migration task.

## `ResourceAttributeList = [[ ... ], ...]` -- *Required*
Information about the resource that is being migrated. This data will be used to map the task to a resource in the Application Discovery Service (ADS)'s repository.

**Note**
> Takes the object array of `ResourceAttribute` where the `Type` field is reserved for the following values: `IPV4_ADDRESS | IPV6_ADDRESS | MAC_ADDRESS | FQDN | VM_MANAGER_ID | VM_MANAGED_OBJECT_REFERENCE | VM_NAME | VM_PATH | BIOS_ID | MOTHERBOARD_SERIAL_NUMBER` where the identifying value can be a string up to 256 characters.

**Important**
> *   If any "VM" related value is set for a `ResourceAttribute` object, it is required that `VM_MANAGER_ID`, as a minimum, is always set. If `VM_MANAGER_ID` is not set, then all "VM" fields will be discarded and "VM" fields will not be used for matching the migration task to a server in Application Discovery Service (ADS)'s repository. See the [Example](https://docs.aws.amazon.com/migrationhub/latest/ug/API_PutResourceAttributes.html#API_PutResourceAttributes_Examples) section below for a use case of specifying "VM" related values.

*   If a server you are trying to match has multiple IP or MAC addresses, you should provide as many as you know in separate type/value pairs passed to the `ResourceAttributeList` parameter to maximize the chances of matching.
```
 ResourceAttributeList = [[
        "Type" => <required> "IPV4_ADDRESS", "IPV6_ADDRESS", "MAC_ADDRESS", "FQDN", "VM_MANAGER_ID", "VM_MANAGED_OBJECT_REFERENCE", "VM_NAME", "VM_PATH", "BIOS_ID" or "MOTHERBOARD_SERIAL_NUMBER",
        "Value" => <required> ::String
    ], ...]
```

## `DryRun = ::Bool`
Optional boolean flag to indicate whether any effect should take place. Used to test if the caller has permission to make the call.

# Returns

`PutResourceAttributesResult`

# Exceptions

`AccessDeniedException`, `InternalServerError`, `ServiceUnavailableException`, `DryRunOperation`, `UnauthorizedOperation`, `InvalidInputException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/AWSMigrationHub-2017-05-31/PutResourceAttributes)
"""
@inline put_resource_attributes(aws::AWSConfig=default_aws_config(); args...) = put_resource_attributes(aws, args)

@inline put_resource_attributes(aws::AWSConfig, args) = AWSCore.Services.AWSMigrationHub(aws, "PutResourceAttributes", args)

@inline put_resource_attributes(args) = put_resource_attributes(default_aws_config(), args)

end # module MigrationHub

#==============================================================================#
# End of file
#==============================================================================#


text:
const OBJC_NEW_PROPERTIES = 1
const K_SP = ' '
const K_exclam = '!'
const K_quotedbl = '\"'
const K_numbersign = '#'
const K_dollar = '\$'
const K_percent = '%'
const K_ampersand = '&'
const K_apostrophe = '\''
const K_parentleft = '('
const K_parentright = ')'
const K_asterisk = '*'
const K_plus = '+'
const K_comma = ','
const K_minus = '-'
const K_period = '.'
const K_slash = '/'
const K_0 = '0'
const K_1 = '1'
const K_2 = '2'
const K_3 = '3'
const K_4 = '4'
const K_5 = '5'
const K_6 = '6'
const K_7 = '7'
const K_8 = '8'
const K_9 = '9'
const K_colon = ':'
const K_semicolon = ';'
const K_less = '<'
const K_equal = '='
const K_greater = '>'
const K_question = '?'
const K_at = '@'
const K_A = 'A'
const K_B = 'B'
const K_C = 'C'
const K_D = 'D'
const K_E = 'E'
const K_F = 'F'
const K_G = 'G'
const K_H = 'H'
const K_I = 'I'
const K_J = 'J'
const K_K = 'K'
const K_L = 'L'
const K_M = 'M'
const K_N = 'N'
const K_O = 'O'
const K_P = 'P'
const K_Q = 'Q'
const K_R = 'R'
const K_S = 'S'
const K_T = 'T'
const K_U = 'U'
const K_V = 'V'
const K_W = 'W'
const K_X = 'X'
const K_Y = 'Y'
const K_Z = 'Z'
const K_bracketleft = '['
const K_backslash = '\\'
const K_bracketright = ']'
const K_circum = '^'
const K_underscore = '_'
const K_grave = '`'
const K_a = 'a'
const K_b = 'b'
const K_c = 'c'
const K_d = 'd'
const K_e = 'e'
const K_f = 'f'
const K_g = 'g'
const K_h = 'h'
const K_i = 'i'
const K_j = 'j'
const K_k = 'k'
const K_l = 'l'
const K_m = 'm'
const K_n = 'n'
const K_o = 'o'
const K_p = 'p'
const K_q = 'q'
const K_r = 'r'
const K_s = 's'
const K_t = 't'
const K_u = 'u'
const K_v = 'v'
const K_w = 'w'
const K_x = 'x'
const K_y = 'y'
const K_z = 'z'
const K_braceleft = '{'
const K_bar = '|'
const K_braceright = '}'
const K_tilde = '~'
# Skipping MacroDefinition: iup_isprint(_c)((_c)>31&&(_c)<127)
const K_BS = '\b'
const K_TAB = '\t'
const K_LF = '\n'
const K_CR = '\r'
const K_quoteleft = K_grave
const K_quoteright = K_apostrophe

macro isxkey(c)
	return $c >= 128
end
#const isxkey = iup_isXkey
# Skipping MacroDefinition: iup_isXkey(_c)((_c)>=128)

const K_PAUSE = 0xFF13
const K_ESC = 0xFF1B
const K_HOME = 0xFF50
const K_LEFT = 0xFF51
const K_UP = 0xFF52
const K_RIGHT = 0xFF53
const K_DOWN = 0xFF54
const K_PGUP = 0xFF55
const K_PGDN = 0xFF56
const K_END = 0xFF57
const K_MIDDLE = 0xFF0B
const K_Print = 0xFF61
const K_INS = 0xFF63
const K_Menu = 0xFF67
const K_DEL = 0xFFFF
const K_F1 = 0xFFBE
const K_F2 = 0xFFBF
const K_F3 = 0xFFC0
const K_F4 = 0xFFC1
const K_F5 = 0xFFC2
const K_F6 = 0xFFC3
const K_F7 = 0xFFC4
const K_F8 = 0xFFC5
const K_F9 = 0xFFC6
const K_F10 = 0xFFC7
const K_F11 = 0xFFC8
const K_F12 = 0xFFC9
const K_LSHIFT = 0xFFE1
const K_RSHIFT = 0xFFE2
const K_LCTRL = 0xFFE3
const K_RCTRL = 0xFFE4
const K_LALT = 0xFFE9
const K_RALT = 0xFFEA
const K_NUM = 0xFF7F
const K_SCROLL = 0xFF14
const K_CAPS = 0xFFE5
const K_ccedilla = 0x00E7
const K_Ccedilla = 0x00C7
const K_acute = 0x00B4
const K_diaeresis = 0x00A8
# Skipping MacroDefinition: iup_isShiftXkey(_c)((_c)&0x10000000)
# Skipping MacroDefinition: iup_isCtrlXkey(_c)((_c)&0x20000000)
# Skipping MacroDefinition: iup_isAltXkey(_c)((_c)&0x40000000)
# Skipping MacroDefinition: iup_isSysXkey(_c)((_c)&0x80000000)
# Skipping MacroDefinition: iup_XkeyBase(_c)((_c)&0x0FFFFFFF)
# Skipping MacroDefinition: iup_XkeyShift(_c)((_c)|0x10000000)
# Skipping MacroDefinition: iup_XkeyCtrl(_c)((_c)|0x20000000)
# Skipping MacroDefinition: iup_XkeyAlt(_c)((_c)|0x40000000)
# Skipping MacroDefinition: iup_XkeySys(_c)((_c)|0x80000000)

#=
const K_sHOME = iup_XkeyShift
const K_sUP = iup_XkeyShift
const K_sPGUP = iup_XkeyShift
const K_sLEFT = iup_XkeyShift
const K_sMIDDLE = iup_XkeyShift
const K_sRIGHT = iup_XkeyShift
const K_sEND = iup_XkeyShift
const K_sDOWN = iup_XkeyShift
const K_sPGDN = iup_XkeyShift
const K_sINS = iup_XkeyShift
const K_sDEL = iup_XkeyShift
const K_sSP = iup_XkeyShift
const K_sTAB = iup_XkeyShift
const K_sCR = iup_XkeyShift
const K_sBS = iup_XkeyShift
const K_sPAUSE = iup_XkeyShift
const K_sESC = iup_XkeyShift
const K_sF1 = iup_XkeyShift
const K_sF2 = iup_XkeyShift
const K_sF3 = iup_XkeyShift
const K_sF4 = iup_XkeyShift
const K_sF5 = iup_XkeyShift
const K_sF6 = iup_XkeyShift
const K_sF7 = iup_XkeyShift
const K_sF8 = iup_XkeyShift
const K_sF9 = iup_XkeyShift
const K_sF10 = iup_XkeyShift
const K_sF11 = iup_XkeyShift
const K_sF12 = iup_XkeyShift
const K_sPrint = iup_XkeyShift
const K_sMenu = iup_XkeyShift
const K_cHOME = iup_XkeyCtrl
const K_cUP = iup_XkeyCtrl
const K_cPGUP = iup_XkeyCtrl
const K_cLEFT = iup_XkeyCtrl
const K_cMIDDLE = iup_XkeyCtrl
const K_cRIGHT = iup_XkeyCtrl
const K_cEND = iup_XkeyCtrl
const K_cDOWN = iup_XkeyCtrl
const K_cPGDN = iup_XkeyCtrl
const K_cINS = iup_XkeyCtrl
const K_cDEL = iup_XkeyCtrl
const K_cSP = iup_XkeyCtrl
const K_cTAB = iup_XkeyCtrl
const K_cCR = iup_XkeyCtrl
const K_cBS = iup_XkeyCtrl
const K_cPAUSE = iup_XkeyCtrl
const K_cESC = iup_XkeyCtrl
const K_cCcedilla = iup_XkeyCtrl
const K_cF1 = iup_XkeyCtrl
const K_cF2 = iup_XkeyCtrl
const K_cF3 = iup_XkeyCtrl
const K_cF4 = iup_XkeyCtrl
const K_cF5 = iup_XkeyCtrl
const K_cF6 = iup_XkeyCtrl
const K_cF7 = iup_XkeyCtrl
const K_cF8 = iup_XkeyCtrl
const K_cF9 = iup_XkeyCtrl
const K_cF10 = iup_XkeyCtrl
const K_cF11 = iup_XkeyCtrl
const K_cF12 = iup_XkeyCtrl
const K_cPrint = iup_XkeyCtrl
const K_cMenu = iup_XkeyCtrl
const K_mHOME = iup_XkeyAlt
const K_mUP = iup_XkeyAlt
const K_mPGUP = iup_XkeyAlt
const K_mLEFT = iup_XkeyAlt
const K_mMIDDLE = iup_XkeyAlt
const K_mRIGHT = iup_XkeyAlt
const K_mEND = iup_XkeyAlt
const K_mDOWN = iup_XkeyAlt
const K_mPGDN = iup_XkeyAlt
const K_mINS = iup_XkeyAlt
const K_mDEL = iup_XkeyAlt
const K_mSP = iup_XkeyAlt
const K_mTAB = iup_XkeyAlt
const K_mCR = iup_XkeyAlt
const K_mBS = iup_XkeyAlt
const K_mPAUSE = iup_XkeyAlt
const K_mESC = iup_XkeyAlt
const K_mCcedilla = iup_XkeyAlt
const K_mF1 = iup_XkeyAlt
const K_mF2 = iup_XkeyAlt
const K_mF3 = iup_XkeyAlt
const K_mF4 = iup_XkeyAlt
const K_mF5 = iup_XkeyAlt
const K_mF6 = iup_XkeyAlt
const K_mF7 = iup_XkeyAlt
const K_mF8 = iup_XkeyAlt
const K_mF9 = iup_XkeyAlt
const K_mF10 = iup_XkeyAlt
const K_mF11 = iup_XkeyAlt
const K_mF12 = iup_XkeyAlt
const K_mPrint = iup_XkeyAlt
const K_mMenu = iup_XkeyAlt
const K_yHOME = iup_XkeySys
const K_yUP = iup_XkeySys
const K_yPGUP = iup_XkeySys
const K_yLEFT = iup_XkeySys
const K_yMIDDLE = iup_XkeySys
const K_yRIGHT = iup_XkeySys
const K_yEND = iup_XkeySys
const K_yDOWN = iup_XkeySys
const K_yPGDN = iup_XkeySys
const K_yINS = iup_XkeySys
const K_yDEL = iup_XkeySys
const K_ySP = iup_XkeySys
const K_yTAB = iup_XkeySys
const K_yCR = iup_XkeySys
const K_yBS = iup_XkeySys
const K_yPAUSE = iup_XkeySys
const K_yESC = iup_XkeySys
const K_yCcedilla = iup_XkeySys
const K_yF1 = iup_XkeySys
const K_yF2 = iup_XkeySys
const K_yF3 = iup_XkeySys
const K_yF4 = iup_XkeySys
const K_yF5 = iup_XkeySys
const K_yF6 = iup_XkeySys
const K_yF7 = iup_XkeySys
const K_yF8 = iup_XkeySys
const K_yF9 = iup_XkeySys
const K_yF10 = iup_XkeySys
const K_yF11 = iup_XkeySys
const K_yF12 = iup_XkeySys
const K_yPrint = iup_XkeySys
const K_yMenu = iup_XkeySys
const K_sPlus = iup_XkeyShift
const K_sComma = iup_XkeyShift
const K_sMinus = iup_XkeyShift
const K_sPeriod = iup_XkeyShift
const K_sSlash = iup_XkeyShift
const K_sAsterisk = iup_XkeyShift
const K_cA = iup_XkeyCtrl
const K_cB = iup_XkeyCtrl
const K_cC = iup_XkeyCtrl
const K_cD = iup_XkeyCtrl
const K_cE = iup_XkeyCtrl
const K_cF = iup_XkeyCtrl
const K_cG = iup_XkeyCtrl
const K_cH = iup_XkeyCtrl
const K_cI = iup_XkeyCtrl
const K_cJ = iup_XkeyCtrl
const K_cK = iup_XkeyCtrl
const K_cL = iup_XkeyCtrl
const K_cM = iup_XkeyCtrl
const K_cN = iup_XkeyCtrl
const K_cO = iup_XkeyCtrl
const K_cP = iup_XkeyCtrl
const K_cQ = iup_XkeyCtrl
const K_cR = iup_XkeyCtrl
const K_cS = iup_XkeyCtrl
const K_cT = iup_XkeyCtrl
const K_cU = iup_XkeyCtrl
const K_cV = iup_XkeyCtrl
const K_cW = iup_XkeyCtrl
const K_cX = iup_XkeyCtrl
const K_cY = iup_XkeyCtrl
const K_cZ = iup_XkeyCtrl
const K_c1 = iup_XkeyCtrl
const K_c2 = iup_XkeyCtrl
const K_c3 = iup_XkeyCtrl
const K_c4 = iup_XkeyCtrl
const K_c5 = iup_XkeyCtrl
const K_c6 = iup_XkeyCtrl
const K_c7 = iup_XkeyCtrl
const K_c8 = iup_XkeyCtrl
const K_c9 = iup_XkeyCtrl
const K_c0 = iup_XkeyCtrl
const K_cPlus = iup_XkeyCtrl
const K_cComma = iup_XkeyCtrl
const K_cMinus = iup_XkeyCtrl
const K_cPeriod = iup_XkeyCtrl
const K_cSlash = iup_XkeyCtrl
const K_cSemicolon = iup_XkeyCtrl
const K_cEqual = iup_XkeyCtrl
const K_cBracketleft = iup_XkeyCtrl
const K_cBracketright = iup_XkeyCtrl
const K_cBackslash = iup_XkeyCtrl
const K_cAsterisk = iup_XkeyCtrl
const K_mA = iup_XkeyAlt
const K_mB = iup_XkeyAlt
const K_mC = iup_XkeyAlt
const K_mD = iup_XkeyAlt
const K_mE = iup_XkeyAlt
const K_mF = iup_XkeyAlt
const K_mG = iup_XkeyAlt
const K_mH = iup_XkeyAlt
const K_mI = iup_XkeyAlt
const K_mJ = iup_XkeyAlt
const K_mK = iup_XkeyAlt
const K_mL = iup_XkeyAlt
const K_mM = iup_XkeyAlt
const K_mN = iup_XkeyAlt
const K_mO = iup_XkeyAlt
const K_mP = iup_XkeyAlt
const K_mQ = iup_XkeyAlt
const K_mR = iup_XkeyAlt
const K_mS = iup_XkeyAlt
const K_mT = iup_XkeyAlt
const K_mU = iup_XkeyAlt
const K_mV = iup_XkeyAlt
const K_mW = iup_XkeyAlt
const K_mX = iup_XkeyAlt
const K_mY = iup_XkeyAlt
const K_mZ = iup_XkeyAlt
const K_m1 = iup_XkeyAlt
const K_m2 = iup_XkeyAlt
const K_m3 = iup_XkeyAlt
const K_m4 = iup_XkeyAlt
const K_m5 = iup_XkeyAlt
const K_m6 = iup_XkeyAlt
const K_m7 = iup_XkeyAlt
const K_m8 = iup_XkeyAlt
const K_m9 = iup_XkeyAlt
const K_m0 = iup_XkeyAlt
const K_mPlus = iup_XkeyAlt
const K_mComma = iup_XkeyAlt
const K_mMinus = iup_XkeyAlt
const K_mPeriod = iup_XkeyAlt
const K_mSlash = iup_XkeyAlt
const K_mSemicolon = iup_XkeyAlt
const K_mEqual = iup_XkeyAlt
const K_mBracketleft = iup_XkeyAlt
const K_mBracketright = iup_XkeyAlt
const K_mBackslash = iup_XkeyAlt
const K_mAsterisk = iup_XkeyAlt
const K_yA = iup_XkeySys
const K_yB = iup_XkeySys
const K_yC = iup_XkeySys
const K_yD = iup_XkeySys
const K_yE = iup_XkeySys
const K_yF = iup_XkeySys
const K_yG = iup_XkeySys
const K_yH = iup_XkeySys
const K_yI = iup_XkeySys
const K_yJ = iup_XkeySys
const K_yK = iup_XkeySys
const K_yL = iup_XkeySys
const K_yM = iup_XkeySys
const K_yN = iup_XkeySys
const K_yO = iup_XkeySys
const K_yP = iup_XkeySys
const K_yQ = iup_XkeySys
const K_yR = iup_XkeySys
const K_yS = iup_XkeySys
const K_yT = iup_XkeySys
const K_yU = iup_XkeySys
const K_yV = iup_XkeySys
const K_yW = iup_XkeySys
const K_yX = iup_XkeySys
const K_yY = iup_XkeySys
const K_yZ = iup_XkeySys
const K_y1 = iup_XkeySys
const K_y2 = iup_XkeySys
const K_y3 = iup_XkeySys
const K_y4 = iup_XkeySys
const K_y5 = iup_XkeySys
const K_y6 = iup_XkeySys
const K_y7 = iup_XkeySys
const K_y8 = iup_XkeySys
const K_y9 = iup_XkeySys
const K_y0 = iup_XkeySys
const K_yPlus = iup_XkeySys
const K_yComma = iup_XkeySys
const K_yMinus = iup_XkeySys
const K_yPeriod = iup_XkeySys
const K_ySlash = iup_XkeySys
const K_ySemicolon = iup_XkeySys
const K_yEqual = iup_XkeySys
const K_yBracketleft = iup_XkeySys
const K_yBracketright = iup_XkeySys
const K_yBackslash = iup_XkeySys
const K_yAsterisk = iup_XkeySys
=#

const IUP_RUN = "RUN"
const IUP_ENGLISH = "ENGLISH"
const IUP_PORTUGUESE = "PORTUGUESE"
const IUP_SBH = "SBH"
const IUP_SBV = "SBV"
const IUP_DEFAULT_ACTION = "DEFAULT_ACTION"
const IUP_IDLE_ACTION = "IDLE_ACTION"
const IUP_ACTION = "ACTION"
const IUP_GETFOCUS_CB = "GETFOCUS_CB"
const IUP_KILLFOCUS_CB = "KILLFOCUS_CB"
const IUP_K_ANY = "K_ANY"
const IUP_KEYPRESS_CB = "KEYPRESS_CB"
const IUP_HELP_CB = "HELP_CB"
const IUP_SCROLL_CB = "SCROLL_CB"
const IUP_RESIZE_CB = "RESIZE_CB"
const IUP_MOTION_CB = "MOTION_CB"
const IUP_BUTTON_CB = "BUTTON_CB"
const IUP_ENTERWINDOW_CB = "ENTERWINDOW_CB"
const IUP_LEAVEWINDOW_CB = "LEAVEWINDOW_CB"
const IUP_WHEEL_CB = "WHEEL_CB"
const IUP_MASK_CB = "MASK_CB"
const IUP_OPEN_CB = "OPEN_CB"
const IUP_HIGHLIGHT_CB = "HIGHLIGHT_CB"
const IUP_MENUCLOSE_CB = "MENUCLOSE_CB"
const IUP_MAP_CB = "MAP_CB"
const IUP_CLOSE_CB = "CLOSE_CB"
const IUP_SHOW_CB = "SHOW_CB"
const IUP_DROPFILES_CB = "DROPFILES_CB"
const IUP_WOM_CB = "WOM_CB"
const IUP_DIRECTION = "DIRECTION"
const IUP_ACTIVE = "ACTIVE"
const IUP_BGCOLOR = "BGCOLOR"
const IUP_FRAMECOLOR = "FRAMECOLOR"
const IUP_FGCOLOR = "FGCOLOR"
const IUP_COLOR = "COLOR"
const IUP_WID = "WID"
const IUP_SIZE = "SIZE"
const IUP_RASTERSIZE = "RASTERSIZE"
const IUP_TITLE = "TITLE"
const IUP_VALUE = "VALUE"
const IUP_VISIBLE = "VISIBLE"
const IUP_FONT = "FONT"
const IUP_TIP = "TIP"
const IUP_EXPAND = "EXPAND"
const IUP_SEPARATOR = "SEPARATOR"
const IUP_HOTSPOT = "HOTSPOT"
const IUP_HEIGHT = "HEIGHT"
const IUP_WIDTH = "WIDTH"
const IUP_KEY = "KEY"
const IUP_MULTIPLE = "MULTIPLE"
const IUP_DROPDOWN = "DROPDOWN"
const IUP_VISIBLE_ITEMS = "VISIBLE_ITEMS"
const IUP_MARGIN = "MARGIN"
const IUP_GAP = "GAP"
const IUP_ALIGNMENT = "ALIGNMENT"
const IUP_IMAGE = "IMAGE"
const IUP_IMINACTIVE = "IMINACTIVE"
const IUP_IMPRESS = "IMPRESS"
const IUP_WIN_SAVEBITS = "WIN_SAVEBITS"
const IUP_NC = "NC"
const IUP_MASK = "MASK"
const IUP_APPEND = "APPEND"
const IUP_BORDER = "BORDER"
const IUP_CARET = "CARET"
const IUP_SELECTION = "SELECTION"
const IUP_SELECTEDTEXT = "SELECTEDTEXT"
const IUP_INSERT = "INSERT"
const IUP_CONID = "CONID"
const IUP_CURSOR = "CURSOR"
const IUP_ICON = "ICON"
const IUP_MENUBOX = "MENUBOX"
const IUP_MINBOX = "MINBOX"
const IUP_MAXBOX = "MAXBOX"
const IUP_RESIZE = "RESIZE"
const IUP_MENU = "MENU"
const IUP_STARTFOCUS = "STARTFOCUS"
const IUP_PARENTDIALOG = "PARENTDIALOG"
const IUP_SHRINK = "SHRINK"
const IUP_DEFAULTENTER = "DEFAULTENTER"
const IUP_DEFAULTESC = "DEFAULTESC"
const IUP_X = "X"
const IUP_Y = "Y"
const IUP_TOOLBOX = "TOOLBOX"
const IUP_CONTROL = "CONTROL"
const IUP_READONLY = "READONLY"
const IUP_SCROLLBAR = "SCROLLBAR"
const IUP_POSY = "POSY"
const IUP_POSX = "POSX"
const IUP_DX = "DX"
const IUP_DY = "DY"
const IUP_XMAX = "XMAX"
const IUP_XMIN = "XMIN"
const IUP_YMAX = "YMAX"
const IUP_YMIN = "YMIN"
const IUP_RED = "255 0 0"
const IUP_GREEN = "0 255 0"
const IUP_BLUE = "0 0 255"
const IUP_MIN = "MIN"
const IUP_MAX = "MAX"
const IUP_TIME = "TIME"
const IUP_DRAG = "DRAG"
const IUP_DROP = "DROP"
const IUP_REPAINT = "REPAINT"
const IUP_TOPMOST = "TOPMOST"
const IUP_CLIPCHILDREN = "CLIPCHILDREN"
const IUP_DIALOGTYPE = "DIALOGTYPE"
const IUP_FILE = "FILE"
const IUP_MULTIPLEFILES = "MULTIPLEFILES"
const IUP_FILTER = "FILTER"
const IUP_FILTERUSED = "FILTERUSED"
const IUP_FILTERINFO = "FILTERINFO"
const IUP_EXTFILTER = "EXTFILTER"
const IUP_DIRECTORY = "DIRECTORY"
const IUP_ALLOWNEW = "ALLOWNEW"
const IUP_NOOVERWRITEPROMPT = "NOOVERWRITEPROMPT"
const IUP_NOCHANGEDIR = "NOCHANGEDIR"
const IUP_FILEEXIST = "FILEEXIST"
const IUP_STATUS = "STATUS"
const IUP_LOCKLOOP = "LOCKLOOP"
const IUP_SYSTEM = "SYSTEM"
const IUP_DRIVER = "DRIVER"
const IUP_SCREENSIZE = "SCREENSIZE"
const IUP_SYSTEMLANGUAGE = "SYSTEMLANGUAGE"
const IUP_COMPUTERNAME = "COMPUTERNAME"
const IUP_USERNAME = "USERNAME"
const IUP_OPEN = "OPEN"
const IUP_SAVE = "SAVE"
const IUP_DIR = "DIR"
const IUP_HORIZONTAL = "HORIZONTAL"
const IUP_VERTICAL = "VERTICAL"
const IUP_YES = "YES"
const IUP_NO = "NO"
const IUP_ON = "ON"
const IUP_OFF = "OFF"
const IUP_ACENTER = "ACENTER"
const IUP_ALEFT = "ALEFT"
const IUP_ARIGHT = "ARIGHT"
const IUP_ATOP = "ATOP"
const IUP_ABOTTOM = "ABOTTOM"
const IUP_NORTH = "NORTH"
const IUP_SOUTH = "SOUTH"
const IUP_WEST = "WEST"
const IUP_EAST = "EAST"
const IUP_NE = "NE"
const IUP_SE = "SE"
const IUP_NW = "NW"
const IUP_SW = "SW"
const IUP_FULLSCREEN = "FULLSCREEN"
const IUP_FULL = "FULL"
const IUP_HALF = "HALF"
const IUP_THIRD = "THIRD"
const IUP_QUARTER = "QUARTER"
const IUP_EIGHTH = "EIGHTH"
const IUP_ARROW = "ARROW"
const IUP_BUSY = "BUSY"
const IUP_RESIZE_N = "RESIZE_N"
const IUP_RESIZE_S = "RESIZE_S"
const IUP_RESIZE_E = "RESIZE_E"
const IUP_RESIZE_W = "RESIZE_W"
const IUP_RESIZE_NE = "RESIZE_NE"
const IUP_RESIZE_NW = "RESIZE_NW"
const IUP_RESIZE_SE = "RESIZE_SE"
const IUP_RESIZE_SW = "RESIZE_SW"
const IUP_MOVE = "MOVE"
const IUP_HAND = "HAND"
const IUP_NONE = "NONE"
const IUP_IUP = "IUP"
const IUP_CROSS = "CROSS"
const IUP_PEN = "PEN"
const IUP_TEXT = "TEXT"
const IUP_RESIZE_C = "RESIZE_C"
const IUP_OPENHAND = "OPENHAND"
const IUP_HELVETICA_NORMAL_8 = "HELVETICA_NORMAL_8"
const IUP_HELVETICA_ITALIC_8 = "HELVETICA_ITALIC_8"
const IUP_HELVETICA_BOLD_8 = "HELVETICA_BOLD_8"
const IUP_HELVETICA_NORMAL_10 = "HELVETICA_NORMAL_10"
const IUP_HELVETICA_ITALIC_10 = "HELVETICA_ITALIC_10"
const IUP_HELVETICA_BOLD_10 = "HELVETICA_BOLD_10"
const IUP_HELVETICA_NORMAL_12 = "HELVETICA_NORMAL_12"
const IUP_HELVETICA_ITALIC_12 = "HELVETICA_ITALIC_12"
const IUP_HELVETICA_BOLD_12 = "HELVETICA_BOLD_12"
const IUP_HELVETICA_NORMAL_14 = "HELVETICA_NORMAL_14"
const IUP_HELVETICA_ITALIC_14 = "HELVETICA_ITALIC_14"
const IUP_HELVETICA_BOLD_14 = "HELVETICA_BOLD_14"
const IUP_COURIER_NORMAL_8 = "COURIER_NORMAL_8"
const IUP_COURIER_ITALIC_8 = "COURIER_ITALIC_8"
const IUP_COURIER_BOLD_8 = "COURIER_BOLD_8"
const IUP_COURIER_NORMAL_10 = "COURIER_NORMAL_10"
const IUP_COURIER_ITALIC_10 = "COURIER_ITALIC_10"
const IUP_COURIER_BOLD_10 = "COURIER_BOLD_10"
const IUP_COURIER_NORMAL_12 = "COURIER_NORMAL_12"
const IUP_COURIER_ITALIC_12 = "COURIER_ITALIC_12"
const IUP_COURIER_BOLD_12 = "COURIER_BOLD_12"
const IUP_COURIER_NORMAL_14 = "COURIER_NORMAL_14"
const IUP_COURIER_ITALIC_14 = "COURIER_ITALIC_14"
const IUP_COURIER_BOLD_14 = "COURIER_BOLD_14"
const IUP_TIMES_NORMAL_8 = "TIMES_NORMAL_8"
const IUP_TIMES_ITALIC_8 = "TIMES_ITALIC_8"
const IUP_TIMES_BOLD_8 = "TIMES_BOLD_8"
const IUP_TIMES_NORMAL_10 = "TIMES_NORMAL_10"
const IUP_TIMES_ITALIC_10 = "TIMES_ITALIC_10"
const IUP_TIMES_BOLD_10 = "TIMES_BOLD_10"
const IUP_TIMES_NORMAL_12 = "TIMES_NORMAL_12"
const IUP_TIMES_ITALIC_12 = "TIMES_ITALIC_12"
const IUP_TIMES_BOLD_12 = "TIMES_BOLD_12"
const IUP_TIMES_NORMAL_14 = "TIMES_NORMAL_14"
const IUP_TIMES_ITALIC_14 = "TIMES_ITALIC_14"
const IUP_TIMES_BOLD_14 = "TIMES_BOLD_14"
const IUP_K_exclam = "K_exclam"
const IUP_K_quotedbl = "K_quotedbl"
const IUP_K_numbersign = "K_numbersign"
const IUP_K_dollar = "K_dollar"
const IUP_K_percent = "K_percent"
const IUP_K_ampersand = "K_ampersand"
const IUP_K_quoteright = "K_quoteright"
const IUP_K_parentleft = "K_parentleft"
const IUP_K_parentright = "K_parentright"
const IUP_K_asterisk = "K_asterisk"
const IUP_K_plus = "K_plus"
const IUP_K_comma = "K_comma"
const IUP_K_minus = "K_minus"
const IUP_K_period = "K_period"
const IUP_K_slash = "K_slash"
const IUP_K_0 = "K_0"
const IUP_K_1 = "K_1"
const IUP_K_2 = "K_2"
const IUP_K_3 = "K_3"
const IUP_K_4 = "K_4"
const IUP_K_5 = "K_5"
const IUP_K_6 = "K_6"
const IUP_K_7 = "K_7"
const IUP_K_8 = "K_8"
const IUP_K_9 = "K_9"
const IUP_K_colon = "K_colon"
const IUP_K_semicolon = "K_semicolon "
const IUP_K_less = "K_less"
const IUP_K_equal = "K_equal"
const IUP_K_greater = "K_greater"
const IUP_K_question = "K_question"
const IUP_K_at = "K_at"
const IUP_K_A = "K_A"
const IUP_K_B = "K_B"
const IUP_K_C = "K_C"
const IUP_K_D = "K_D"
const IUP_K_E = "K_E"
const IUP_K_F = "K_F"
const IUP_K_G = "K_G"
const IUP_K_H = "K_H"
const IUP_K_I = "K_I"
const IUP_K_J = "K_J"
const IUP_K_K = "K_K"
const IUP_K_L = "K_L"
const IUP_K_M = "K_M"
const IUP_K_N = "K_N"
const IUP_K_O = "K_O"
const IUP_K_P = "K_P"
const IUP_K_Q = "K_Q"
const IUP_K_R = "K_R"
const IUP_K_S = "K_S"
const IUP_K_T = "K_T"
const IUP_K_U = "K_U"
const IUP_K_V = "K_V"
const IUP_K_W = "K_W"
const IUP_K_X = "K_X"
const IUP_K_Y = "K_Y"
const IUP_K_Z = "K_Z"
const IUP_K_bracketleft = "K_bracketleft"
const IUP_K_backslash = "K_backslash"
const IUP_K_bracketright = "K_bracketright"
const IUP_K_circum = "K_circum"
const IUP_K_underscore = "K_underscore"
const IUP_K_quoteleft = "K_quoteleft"
const IUP_K_a = "K_a"
const IUP_K_b = "K_b"
const IUP_K_c = "K_c"
const IUP_K_d = "K_d"
const IUP_K_e = "K_e"
const IUP_K_f = "K_f"
const IUP_K_g = "K_g"
const IUP_K_h = "K_h"
const IUP_K_i = "K_i"
const IUP_K_j = "K_j"
const IUP_K_k = "K_k"
const IUP_K_l = "K_l"
const IUP_K_m = "K_m"
const IUP_K_n = "K_n"
const IUP_K_o = "K_o"
const IUP_K_p = "K_p"
const IUP_K_q = "K_q"
const IUP_K_r = "K_r"
const IUP_K_s = "K_s"
const IUP_K_t = "K_t"
const IUP_K_u = "K_u"
const IUP_K_v = "K_v"
const IUP_K_w = "K_w"
const IUP_K_x = "K_x"
const IUP_K_y = "K_y"
const IUP_K_z = "K_z"
const IUP_K_braceleft = "K_braceleft"
const IUP_K_bar = "K_bar"
const IUP_K_braceright = "K_braceright"
const IUP_K_tilde = "K_tilde"
const IUP_K_cA = "K_cA"
const IUP_K_cB = "K_cB"
const IUP_K_cC = "K_cC"
const IUP_K_cD = "K_cD"
const IUP_K_cE = "K_cE"
const IUP_K_cF = "K_cF"
const IUP_K_cG = "K_cG"
const IUP_K_cJ = "K_cJ"
const IUP_K_cK = "K_cK"
const IUP_K_cL = "K_cL"
const IUP_K_cN = "K_cN"
const IUP_K_cO = "K_cO"
const IUP_K_cP = "K_cP"
const IUP_K_cQ = "K_cQ"
const IUP_K_cR = "K_cR"
const IUP_K_cS = "K_cS"
const IUP_K_cT = "K_cT"
const IUP_K_cU = "K_cU"
const IUP_K_cV = "K_cV"
const IUP_K_cW = "K_cW"
const IUP_K_cX = "K_cX"
const IUP_K_cY = "K_cY"
const IUP_K_cZ = "K_cZ"
const IUP_K_mA = "K_mA"
const IUP_K_mB = "K_mB"
const IUP_K_mC = "K_mC"
const IUP_K_mD = "K_mD"
const IUP_K_mE = "K_mE"
const IUP_K_mF = "K_mF"
const IUP_K_mG = "K_mG"
const IUP_K_mH = "K_mH"
const IUP_K_mI = "K_mI"
const IUP_K_mJ = "K_mJ"
const IUP_K_mK = "K_mK"
const IUP_K_mL = "K_mL"
const IUP_K_mM = "K_mM"
const IUP_K_mN = "K_mN"
const IUP_K_mO = "K_mO"
const IUP_K_mP = "K_mP"
const IUP_K_mQ = "K_mQ"
const IUP_K_mR = "K_mR"
const IUP_K_mS = "K_mS"
const IUP_K_mT = "K_mT"
const IUP_K_mU = "K_mU"
const IUP_K_mV = "K_mV"
const IUP_K_mW = "K_mW"
const IUP_K_mX = "K_mX"
const IUP_K_mY = "K_mY"
const IUP_K_mZ = "K_mZ"
const IUP_K_BS = "K_BS"
const IUP_K_TAB = "K_TAB"
const IUP_K_CR = "K_CR"
const IUP_K_SP = "K_SP"
const IUP_K_ESC = "K_ESC"
const IUP_K_sCR = "K_sCR"
const IUP_K_sTAB = "K_sTAB"
const IUP_K_cTAB = "K_cTAB"
const IUP_K_mTAB = "K_mTAB"
const IUP_K_HOME = "K_HOME"
const IUP_K_UP = "K_UP"
const IUP_K_PGUP = "K_PGUP"
const IUP_K_LEFT = "K_LEFT"
const IUP_K_RIGHT = "K_RIGHT"
const IUP_K_END = "K_END"
const IUP_K_DOWN = "K_DOWN"
const IUP_K_PGDN = "K_PGDN"
const IUP_K_MIDDLE = "K_MIDDLE"
const IUP_K_INS = "K_INS"
const IUP_K_DEL = "K_DEL"
const IUP_K_sHOME = "K_sHOME"
const IUP_K_sUP = "K_sUP"
const IUP_K_sPGUP = "K_sPGUP"
const IUP_K_sLEFT = "K_sLEFT"
const IUP_K_sRIGHT = "K_sRIGHT"
const IUP_K_sEND = "K_sEND"
const IUP_K_sDOWN = "K_sDOWN"
const IUP_K_sPGDN = "K_sPGDN"
const IUP_K_cHOME = "K_cHOME"
const IUP_K_cPGUP = "K_cPGUP"
const IUP_K_cLEFT = "K_cLEFT"
const IUP_K_cRIGHT = "K_cRIGHT"
const IUP_K_cEND = "K_cEND"
const IUP_K_cPGDN = "K_cPGDN"
const IUP_K_cUP = "K_cUP"
const IUP_K_cDOWN = "K_cDOWN"
const IUP_K_cMIDDLE = "K_cMIDDLE"
const IUP_K_cINS = "K_cINS"
const IUP_K_cDEL = "K_cDEL"
const IUP_K_mHOME = "K_mHOME"
const IUP_K_mPGUP = "K_mPGUP"
const IUP_K_mLEFT = "K_mLEFT"
const IUP_K_mRIGHT = "K_mRIGHT"
const IUP_K_mEND = "K_mEND"
const IUP_K_mPGDN = "K_mPGDN"
const IUP_K_mUP = "K_mUP"
const IUP_K_mDOWN = "K_mDOWN"
const IUP_K_mINS = "K_mINS"
const IUP_K_mDEL = "K_mDEL"
const IUP_K_F1 = "K_F1"
const IUP_K_F2 = "K_F2"
const IUP_K_F3 = "K_F3"
const IUP_K_F4 = "K_F4"
const IUP_K_F5 = "K_F5"
const IUP_K_F6 = "K_F6"
const IUP_K_F7 = "K_F7"
const IUP_K_F8 = "K_F8"
const IUP_K_F9 = "K_F9"
const IUP_K_F10 = "K_F10"
const IUP_K_F11 = "K_F11"
const IUP_K_F12 = "K_F12"
const IUP_K_sF1 = "K_sF1"
const IUP_K_sF2 = "K_sF2"
const IUP_K_sF3 = "K_sF3"
const IUP_K_sF4 = "K_sF4"
const IUP_K_sF5 = "K_sF5"
const IUP_K_sF6 = "K_sF6"
const IUP_K_sF7 = "K_sF7"
const IUP_K_sF8 = "K_sF8"
const IUP_K_sF9 = "K_sF9"
const IUP_K_sF10 = "K_sF10"
const IUP_K_sF11 = "K_sF11"
const IUP_K_sF12 = "K_sF12"
const IUP_K_cF1 = "K_cF1"
const IUP_K_cF2 = "K_cF2"
const IUP_K_cF3 = "K_cF3"
const IUP_K_cF4 = "K_cF4"
const IUP_K_cF5 = "K_cF5"
const IUP_K_cF6 = "K_cF6"
const IUP_K_cF7 = "K_cF7"
const IUP_K_cF8 = "K_cF8"
const IUP_K_cF9 = "K_cF9"
const IUP_K_cF10 = "K_cF10"
const IUP_K_cF11 = "K_cF11"
const IUP_K_cF12 = "K_cF12"
const IUP_K_mF1 = "K_mF1"
const IUP_K_mF2 = "K_mF2"
const IUP_K_mF3 = "K_mF3"
const IUP_K_mF4 = "K_mF4"
const IUP_K_mF5 = "K_mF5"
const IUP_K_mF6 = "K_mF6"
const IUP_K_mF7 = "K_mF7"
const IUP_K_mF8 = "K_mF8"
const IUP_K_mF9 = "K_mF9"
const IUP_K_mF10 = "K_mF10"
const IUP_K_m1 = "K_m1"
const IUP_K_m2 = "K_m2"
const IUP_K_m3 = "K_m3"
const IUP_K_m4 = "K_m4"
const IUP_K_m5 = "K_m5"
const IUP_K_m6 = "K_m6"
const IUP_K_m7 = "K_m7"
const IUP_K_m8 = "K_m8"
const IUP_K_m9 = "K_m9"
const IUP_K_m0 = "K_m0"
const IUP_NUM_PARTS = "NUM_PARTS"
const IUP_NUM_CELLS = "NUM_CELLS"
const IUP_CELL = "CELL"
const IUP_PREVIEW_SIZE = "PREVIEW_SIZE"
const IUP_SHOW_PREVIEW = "SHOW_PREVIEW"
const IUP_SHOW_SECONDARY = "SHOW_SECONDARY"
const IUP_PRIMARY_CELL = "PRIMARY_CELL"
const IUP_SECONDARY_CELL = "SECONDARY_CELL"
const IUP_ORIENTATION = "ORIENTATION"
const IUP_SQUARED = "SQUARED"
const IUP_SHADOWED = "SHADOWED"
const IUP_BUFFERIZE = "BUFFERIZE"
const IUP_TRANSPARENCY = "TRANSPARENCY"
const IUP_EXTENDED_CB = "EXTENDED_CB"
const IUP_SELECT_CB = "SELECT_CB"
const IUP_SWITCH_CB = "SWITCH_CB"
const IUP_ALL = "ALL"
const IUP_BOXED = "BOXED"
const IUP_CLIPPED = "CLIPPED"
const IUP_TRANSPARENT = "TRANSPARENT"
const IUP_NON_SCROLLABLE_LINES = "NON_SCROLLABLE_LINES"
const IUP_NON_SCROLLABLE_COLS = "NON_SCROLLABLE_COLS"
const IUP_ORIGIN = "ORIGIN"
const IUP_NO_COLOR = "NO_COLOR"
const IUP_FIRST_LINE = "FIRST_LINE"
const IUP_FIRST_COL = "FIRST_COL"
const IUP_DOUBLE_BUFFER = "DOUBLE_BUFFER"
const IUP_LIMITS = "LIMITS"
const IUP_CANVAS = "CANVAS"
const IUP_IMAGE_CANVAS = "IMAGE_CANVAS"
const IUP_FULL_VISIBLE = "FULL_VISIBLE"
const IUP_MOUSECLICK_CB = "MOUSECLICK_CB"
const IUP_MOUSEMOTION_CB = "MOUSEMOTION_CB"
const IUP_DRAW_CB = "DRAW_CB"
const IUP_WIDTH_CB = "WIDTH_CB"
const IUP_HEIGHT_CB = "HEIGHT_CB"
const IUP_NLINES_CB = "NLINES_CB"
const IUP_NCOLS_CB = "NCOLS_CB"
const IUP_HSPAN_CB = "HSPAN_CB"
const IUP_VSPAN_CB = "VSPAN_CB"
const IUP_SCROLLING_CB = "SCROLLING_CB"
const IUP_RGB = "RGB"
const IUP_CHANGE_CB = "CHANGE_CB"
const IUP_DRAG_CB = "DRAG_CB"
const ICTL_MOUSEMOVE_CB = "MOUSEMOVE_CB"
const ICTL_BUTTON_PRESS_CB = "BUTTON_PRESS_CB"
const ICTL_BUTTON_RELEASE_CB = "BUTTON_RELEASE_CB"
const ICTL_HORIZONTAL = "HORIZONTAL"
const ICTL_VERTICAL = "VERTICAL"
const ICTL_SHOWTICKS = "SHOWTICKS"
const ICTL_TOP = "TOP"
const ICTL_BOTTOM = "BOTTOM"
const ICTL_LEFT = "LEFT"
const ICTL_RIGHT = "RIGHT"
const ICTL_TABTYPE = "TABTYPE"
const ICTL_TABTITLE = "TABTITLE"
const ICTL_TABSIZE = "TABSIZE"
const ICTL_TABCHANGE_CB = "TABCHANGE_CB"
const ICTL_FONT = "FONT"
const ICTL_FONT_ACTIVE = "FONT_ACTIVE"
const ICTL_FONT_INACTIVE = "FONT_INACTIVE"
const ICTL_SHOW_TEXT = "SHOW_TEXT"
const ICTL_DASHED = "DASHED"
const ICTL_MARGIN = "MARGIN"
const ICTL_TEXT = "TEXT"
const ICTL_DENSITY = "DENSITY"
const ICTL_CIRCULAR = "CIRCULAR"
const ICTL_UNIT = "UNIT"
const IUP_ENTERITEM_CB = "ENTERITEM_CB"
const IUP_LEAVEITEM_CB = "LEAVEITEM_CB"
const IUP_EDITION_CB = "EDITION_CB"
const IUP_CLICK_CB = "CLICK_CB"
const IUP_DROP_CB = "DROP_CB"
const IUP_DROPSELECT_CB = "DROPSELECT_CB"
const IUP_DROPCHECK_CB = "DROPCHECK_CB"
const IUP_VALUE_CB = "VALUE_CB"
const IUP_VALUE_EDIT_CB = "VALUE_EDIT_CB"
const IUP_FIELD_CB = "FIELD_CB"
const IUP_RESIZEMATRIX = "RESIZEMATRIX"
const IUP_ADDLIN = "ADDLIN"
const IUP_ADDCOL = "ADDCOL"
const IUP_DELLIN = "DELLIN"
const IUP_DELCOL = "DELCOL"
const IUP_NUMLIN = "NUMLIN"
const IUP_NUMCOL = "NUMCOL"
const IUP_NUMLIN_VISIBLE = "NUMLIN_VISIBLE"
const IUP_NUMCOL_VISIBLE = "NUMCOL_VISIBLE"
const IUP_MARKED = "MARKED"
const IUP_WIDTHDEF = "WIDTHDEF"
const IUP_HEIGHTDEF = "HEIGHTDEF"
const IUP_AREA = "AREA"
const IUP_MARK_MODE = "MARK_MODE"
const IUP_LIN = "LIN"
const IUP_COL = "COL"
const IUP_LINCOL = "LINCOL"
const IUP_EDIT_MODE = "EDIT_MODE"
const IUP_FOCUS_CELL = "FOCUS_CELL"
const IUP_REDRAW = "REDRAW"
const IUP_PREVIOUSVALUE = "PREVIOUSVALUE"
const IUP_MOUSEMOVE_CB = "MOUSEMOVE_CB"
const IUP_ADDLEAF = "ADDLEAF"
const IUP_ADDBRANCH = "ADDBRANCH"
const IUP_DELNODE = "DELNODE"
const IUP_IMAGELEAF = "IMAGELEAF"
const IUP_IMAGEBRANCHCOLLAPSED = "IMAGEBRANCHCOLLAPSED"
const IUP_IMAGEBRANCHEXPANDED = "IMAGEBRANCHEXPANDED"
const IUP_IMAGEEXPANDED = "IMAGEEXPANDED"
const IUP_KIND = "KIND"
const IUP_PARENT = "PARENT"
const IUP_DEPTH = "DEPTH"
const IUP_ADDEXPANDED = "ADDEXPANDED"
const IUP_CTRL = "CTRL"
const IUP_SHIFT = "SHIFT"
const IUP_STATE = "STATE"
const IUP_STARTING = "STARTING"
const IUP_LEAF = "LEAF"
const IUP_BRANCH = "BRANCH"
const IUP_SELECTED = "SELECTED"
const IUP_CHILDREN = "CHILDREN"
const IUP_ROOT = "ROOT"
const IUP_LAST = "LAST"
const IUP_PGUP = "PGUP"
const IUP_PGDN = "PGDN"
const IUP_NEXT = "NEXT"
const IUP_PREVIOUS = "PREVIOUS"
const IUP_INVERT = "INVERT"
const IUP_BLOCK = "BLOCK"
const IUP_CLEARALL = "CLEARALL"
const IUP_MARKALL = "MARKALL"
const IUP_INVERTALL = "INVERTALL"
const IUP_COLLAPSED = "COLLAPSED"
const IUP_EXPANDED = "EXPANDED"
const IUP_SELECTION_CB = "SELECTION_CB"
const IUP_BRANCHOPEN_CB = "BRANCHOPEN_CB"
const IUP_BRANCHCLOSE_CB = "BRANCHCLOSE_CB"
const IUP_RIGHTCLICK_CB = "RIGHTCLICK_CB"
const IUP_EXECUTELEAF_CB = "EXECUTELEAF_CB"
const IUP_RENAMENODE_CB = "RENAMENODE_CB"
const IUP_IMGLEAF = "IMGLEAF"
const IUP_IMGCOLLAPSED = "IMGCOLLAPSED"
const IUP_IMGEXPANDED = "IMGEXPANDED"
const IUP_IMGBLANK = "IMGBLANK"
const IUP_IMGPAPER = "IMGPAPER"
const IUP_NAME = "IUP - Portable User Interface"
const IUP_COPYRIGHT = "Copyright (C) 1994-2014 Tecgraf, PUC-Rio."
const IUP_DESCRIPTION = "Multi-platform toolkit for building graphical user interfaces."
const IUP_VERSION = "3.10"
const IUP_VERSION_NUMBER = 310000
const IUP_VERSION_DATE = "2014/01/17"
const IUP_ERROR = 1
const IUP_NOERROR = 0
const IUP_OPENED = -1
const IUP_INVALID = -1
const IUP_INVALID_ID = -10
const IUP_IGNORE = -1
const IUP_DEFAULT = -2
const IUP_CLOSE = -3
const IUP_CONTINUE = -4
const IUP_CENTER = 0xFFFF
const IUP_LEFT = 0xFFFE
const IUP_RIGHT = 0xFFFD
const IUP_MOUSEPOS = 0xFFFC
const IUP_CURRENT = 0xFFFB
const IUP_CENTERPARENT = 0xFFFA
const IUP_TOP = IUP_LEFT
const IUP_BOTTOM = IUP_RIGHT
const IUP_BUTTON1 = '1'
const IUP_BUTTON2 = '2'
const IUP_BUTTON3 = '3'
const IUP_BUTTON4 = '4'
const IUP_BUTTON5 = '5'
# Skipping MacroDefinition: iup_isshift(_s)(_s[0]=='S')
# Skipping MacroDefinition: iup_iscontrol(_s)(_s[1]=='C')
# Skipping MacroDefinition: iup_isbutton1(_s)(_s[2]=='1')
# Skipping MacroDefinition: iup_isbutton2(_s)(_s[3]=='2')
# Skipping MacroDefinition: iup_isbutton3(_s)(_s[4]=='3')
# Skipping MacroDefinition: iup_isdouble(_s)(_s[5]=='D')
# Skipping MacroDefinition: iup_isalt(_s)(_s[6]=='A')
# Skipping MacroDefinition: iup_issys(_s)(_s[7]=='Y')
# Skipping MacroDefinition: iup_isbutton4(_s)(_s[8]=='4')
# Skipping MacroDefinition: iup_isbutton5(_s)(_s[9]=='5')

#=
const isshift = iup_isshift
const iscontrol = iup_iscontrol
const isbutton1 = iup_isbutton1
const isbutton2 = iup_isbutton2
const isbutton3 = iup_isbutton3
const isdouble = iup_isdouble
const isalt = iup_isalt
const issys = iup_issys
const isbutton4 = iup_isbutton4
const isbutton5 = iup_isbutton5
=#

const IUP_MASK_FLOAT = "[+/-]?(/d+/.?/d*|/./d+)"
const IUP_MASK_UFLOAT = "(/d+/.?/d*|/./d+)"
const IUP_MASK_EFLOAT = "[+/-]?(/d+/.?/d*|/./d+)([eE][+/-]?/d+)?"
const IUP_MASK_INT = "[+/-]?/d+"
const IUP_MASK_UINT = "/d+"
const IUPMASK_FLOAT = IUP_MASK_FLOAT
const IUPMASK_UFLOAT = IUP_MASK_UFLOAT
const IUPMASK_EFLOAT = IUP_MASK_EFLOAT
const IUPMASK_INT = IUP_MASK_INT
const IUPMASK_UINT = IUP_MASK_UINT
const IUP_GETPARAM_OK = -1
const IUP_GETPARAM_INIT = -2
const IUP_GETPARAM_CANCEL = -3
const IUP_GETPARAM_HELP = -4
typealias Ihandle Void
typealias Icallback Ptr{Void}
typealias Iparamcb Ptr{Void}
# begin enum ANONYMOUS_1
const IUP_SHOW = 0
const IUP_RESTORE = 1
const IUP_MINIMIZE = 2
const IUP_MAXIMIZE = 3
const IUP_HIDE = 4
# end enum ANONYMOUS_1
# begin enum ANONYMOUS_2
const IUP_SBUP = 0
const IUP_SBDN = 1
const IUP_SBPGUP = 2
const IUP_SBPGDN = 3
const IUP_SBPOSV = 4
const IUP_SBDRAGV = 5
const IUP_SBLEFT = 6
const IUP_SBRIGHT = 7
const IUP_SBPGLEFT = 8
const IUP_SBPGRIGHT = 9
const IUP_SBPOSH = 10
const IUP_SBDRAGH = 11
# end enum ANONYMOUS_2
# begin enum ANONYMOUS_3
const IUP_RECBINARY = 0
const IUP_RECTEXT = 1
# end enum ANONYMOUS_3


text:
# Tests for loss function functionality
function t_loss()

Ac = j4pr.DataGenerator.fish(20)  # classification dataset
Ar = j4pr.DataGenerator.fishr(20) # regression dataset

# Test losses for classification
tr,ts = j4pr.splitobs(j4pr.shuffleobs(Ac),0.5)
wt = tr |> j4pr.lindisc(rand(),rand())
ts = j4pr.getobs(ts)
wlc1 = j4pr.loss(()->MLLabelUtils.convertlabel(MLLabelUtils.LabelEnc.OneOfK{Float64}, -ts, wt.x.properties.labels.label)::Matrix{Float64 }) # for array input
wlc2 = j4pr.loss((x)->MLLabelUtils.convertlabel(MLLabelUtils.LabelEnc.OneOfK{Float64}, x, wt.x.properties.labels.label)::Matrix{Float64 }) # for Tuple/datacell input
	r1 = +ts |> wt+wlc1
	r2 = ts |> wt+wlc2
	r3 = j4pr.strip(ts|>wt) |> wlc2
Test.@test  r1==r2==r3

# Test losses for regression
tr,ts = j4pr.splitobs(j4pr.shuffleobs(Ar),0.5)
wt = tr |> j4pr.stumpr()
ts = j4pr.getobs(ts)
wlc1 = j4pr.loss(()->-ts, vec) # for array input
wlc2 = j4pr.loss(identity,vec) # for Tuple/datacell input
	r1 = +ts |> wt+wlc1
	r2 = ts |> wt+wlc2
	r3 = j4pr.strip(ts|>wt) |> wlc2
Test.@test  r1==r2==r3

end


text:
"""
Structure containing the agents definitions and the simulation space. It incorporates a function evolve that is the compiled program for running simulations.
"""
struct Model
    agent::Agent

    program::Expr
    evolve::Function
end


text:
import GameZero: rungame
using ProgressBars
import SnakeAI: Game
import SnakeAI.Agent: AbstractAgent

function play_snake()
    rungame("src/extras/HumanPlayableSnake.jl")
end

function demo_agent()
    rungame("src/extras/AgentPlayedSnake.jl")
end

function train_agent(agent::AbstractAgent; N::Int=100)

    # Creating the initial game
    game = Game()

    iter = ProgressBar(1:N)
    set_description(iter, "Training the agent on $N games:")

    for _ in iter
        done = false

        while !done
            done = train!(agent, game)
        end
    end

    @info "Agent high score after $N games => $(agent.record) pts"
end


text:
include("../src/Steganography.jl")
VERSION > v"0.7-" ? using Test : using Base.Test

@testset "steganography" begin
    U = read("reliance.txt")
    n = length(U)
    @testset "$S in $T" for S in [UInt8], T in [Int32, Int64, UInt32, UInt64,
        Float16, Float32, Float64]
        A = rand(T, n)
        for j in 1:n
            r = Steganography.setlast8(A[j], U[j])
            s = Steganography.setlastbits(A[j], U[j], UInt8(8))
            u = Steganography.setlast7(A[j], U[j])
            v = Steganography.setlastbits(A[j], U[j], UInt8(7))
            @test r == s
            @test u == v
            @test Steganography.getlast8(u) == Steganography.getlastbits(u, UInt8(8))
            @test Steganography.getlast7(u) == Steganography.getlastbits(u, UInt8(7))
            @test U[j] == Steganography.getlast7(u)
            @test U[j] == Steganography.getlast8(r)
        end
        A = rand(T, n + 2)
        B = Steganography.embed(A, U)
        V = Steganography.extract(B)
        @test U == V
    end
    @testset "$S in $T" for S in [UInt8], T in [Complex32, Complex64, Complex128]
        A = rand(T, n + 2)
        B = Steganography.embed(A, U)
        V = Steganography.extract(B)
        @test U == V
    end
end


text:
using PhysConsts
using Base.Test

# TODO: improve test coverage

@testset "Data File" begin
    datafile = DataFile("test.url", "directory", "filename")

    @test url(datafile) == "test.url"
    @test dirname(datafile) == abspath("directory")
    @test filename(datafile) == "filename"
    @test filepath(datafile) == joinpath(dirname(datafile), "filename")
end