text:
using ForwardDiff
using LinearAlgebra
using Plots
using Formatting
using TrajectoryOptimization
import TrajectoryOptimization: get_num_terminal_constraints, generate_constraint_functions

opts = SolverOptions()

model,obj = Dynamics.pendulum
obj.cost.Q .= Diagonal(I,2)*1
obj.cost.R .= Diagonal(I,1)*1
obj.cost.Qf .= Diagonal(I,2)*100
obj_c = ConstrainedObjective(obj,u_max=3)
obj_c = update_objective(obj_c,tf=5)

model,obj = Dynamics.dubinscar
obj.cost.Q .= Diagonal(I,3)*1e-1
obj.cost.R .= Diagonal(I,2)*1e-1
obj.cost.Qf .= Diagonal(I,3)*100
obj_c = ConstrainedObjective(obj,u_max=0.75,u_min=-0.75,x_min=[-0.5;-0.1;-Inf],x_max=[0.5,1.1,Inf])

opts = SolverOptions()
opts.verbose = false
opts.cost_tolerance = 1e-6
opts.cost_tolerance_intermediate = 0.01
opts.constraint_tolerance = 1e-4
opts.resolve_feasible = true
opts.outer_loop_update_type = :default
opts.use_nesterov = true
opts.penalty_scaling = 50
opts.penalty_initial = 10
opts.R_infeasible = 1
opts.square_root = true
opts.cost_tolerance_infeasible = 1e-6
model,obj_c,circles = Dynamics.dubinscar_escape
X_guess = [2.5 2.5 0.;4. 5. .785;5. 6.25 0.;7.5 6.25 -.261;9 5. -1.57;7.5 2.5 0.]

model,obj = Dynamics.quadrotor
obj_c = ConstrainedObjective(obj,u_max=4,u_min=0)

opts = SolverOptions()
opts.penalty_initial = 0.01
model,obj_c = Dynamics.quadrotor_3obs

model,obj = Dynamics.double_integrator
obj_c = ConstrainedObjective(obj)

# obj_c = ConstrainedObjective(obj,u_min=-0.3,u_max=0.4)
solver = Solver(model,obj_c,N=21,opts=opts)
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_function!, c_jacobian!, c_labels, cI!, cE! = generate_constraint_functions(solver.obj)
dt = solver.dt
U0 = ones(m,N-1)
# X0 = TrajectoryOptimization.interp_rows(N,obj.tf,Array(X_guess'))
solver.opts.verbose = true
solver.opts.cost_tolerance_infeasible = 1e-3
solver.opts.cost_tolerance = 1e-3
solver.opts.constraint_tolerance = 1e-3
solver.opts.resolve_feasible = false
res,stats = solve(solver,U0)
plot()
plot_trajectory!(res)
plot(res.X)
plot(res.U)

function mycost(Z)
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx .+ (1:Nu)],m,N-1)
    J = 0.0
    for k = 1:N-1
        J += stage_cost(costfun,X[:,k],U[:,k])*solver.dt
    end
    J += stage_cost(costfun,X[:,N])
    return J
end

function lagrangian(V)
    nu = V[Nz .+ (1:Nx)]
    λ = V[(Nz + Nx) .+ (1:Nh)]
    μ = V[(Nz + Nx + Nh) .+ (1:Ng)]
    Z = V[1:Nz]
    J = mycost(Z) + nu'dynamics(Z) + λ'cE(Z) + μ'cI(Z)
end

function al_lagrangian(V,ρ)
    eps = 1e-4
    Z = V[1:Nz]
    d = dynamics(Z)
    h = cE(Z)
    g = cI(Z)
    a = g .> eps
    lagrangian(V) + ρ/2*sqrt(d'd + h'h + g[a]'g[a])
end

function cE(Z)
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx.+(1:Nu)],m,N-1)

    C = zeros(eltype(Z),pE,N-1)
    for k = 1:N-1
        cE!(view(C,1:pE,k),X[:,k],U[:,k])
    end
    CN = zeros(eltype(Z),pE_N)
    cE!(CN,X[:,N])
    CE = [vec(C); CN]
    return CE
end

function cI(Z)
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx.+(1:Nu)],m,N-1)

    C = zeros(eltype(Z),pI,N-1)
    for k = 1:N-1
        cI!(view(C,1:pI,k),X[:,k],U[:,k])
    end
    CN = zeros(eltype(Z),pI_N)
    cI!(CN,X[:,N])
    CI = [vec(C); CN]
    return CI
end

function dynamics(Z)
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx.+(1:Nu)],m,N-1)

    D = zeros(eltype(Z),n,N)
    D[:,1] = X[:,1] - solver.obj.x0
    for k = 2:N
        solver.fd(view(D,1:n,k),X[:,k-1],U[:,k-1])
        D[:,k] -= X[:,k]
    end
    return vec(D)
end

function active_set(Z,eps=0)
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx.+(1:Nu)],m,N-1)

    a = ones(Bool,NN)
    ci = cI(Z)
    c_inds = ones(Bool,length(ci))
    c_inds = ci .>= -eps

    a[(Nz + Nx + Nh) .+ (1:Ng)] = c_inds
    # a[ind1.s] = c_inds
    return a
end

function armijo_line_search(merit::Function,V,d,grad; max_iter=10, ϕ=0.01)
    α = 1
    J_prev = merit(V)
    J = merit(V+α*d)
    iter = 1
    while J > J_prev + α*ϕ*grad'd
        α *= 0.75
        J = merit(V + α*d)
        if iter > max_iter
            println("Max iterations")
            α = 0
            break
        end
        iter += 1
    end
    return α
end

function solve_newton(V,ρ,type; iters=10, verbose=false, iters_linesearch=10)
    # Define merit function
    meritfun(V) = al_lagrangian(V,ρ)
    max_c(V) = norm(dynamics(V),Inf)

    # Initial cost
    J0 = meritfun(V)
    V_ = copy(V)
    println("Initial Cost: $J0")

    # Stats
    cost = zeros(iters)
    grad = zeros(iters)
    c_max = zeros(iters)

    # Take Newton Steps
    for i = 1:iters
        A,b = buildKKT(V_,ρ,type)
        δV = -A\b
        α = armijo_line_search(meritfun,V_,δV,b, max_iter=iters_linesearch)
        V_ = V_ + α*δV
        J = meritfun(V_)
        cost[i] = J
        grad[i] = norm(b)
        c_max[i] = max_c(V_)
        if verbose
            println("Iter $i:")
            println("  cost: $J")
            println("  grad: $(grad[i])")
            println("  c_max: $(c_max[i])")
            println("  α: $α")
            println("  rank: $(rank(A))")
            println("  cond: $(cond(A))")
        end
    end
    stats = Dict("cost"=>cost,"grad"=>grad,"c_max"=>c_max)
    return V, stats
end

function buildKKT(V,ρ,type=:penalty)
    Z = V[1:Nz]
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx+1:end],m,N-1)
    nu = V[Nz.+(1:Nx)]
    λ = V[(Nz + Nx) .+ (1:Nh)]
    μ = V[(Nz + Nx + Nh) .+ (1:Ng)]

    ∇²J = ForwardDiff.hessian(mycost,Z)
    ∇J = ForwardDiff.gradient(mycost,Z)
    D = ForwardDiff.jacobian(dynamics,Z)
    H = ForwardDiff.jacobian(cE,Z)
    G = ForwardDiff.jacobian(cI,Z)
    d = dynamics(Z)
    h = cE(Z)
    g = cI(Z)

    if type == :penalty
        A = [∇²J   D'         H'      G';
             D   -1/ρ*I       zeros(Nx,Nh+Ng) ;
             H   zeros(Nh,Nx) -1/ρ*I zeros(Nh,Ng);
             G   zeros(Ng,Nx) zeros(Ng,Nh) -1/ρ*I]
        b = [∇J + D'nu + H'λ + G'μ; d; h; g]
    elseif type == :kkt
        A = [∇²J   D'    H'                G';
             D   zeros(Nx,Nx) zeros(Nx,Nh) zeros(Nx,Ng);
             H   zeros(Nh,Nx) zeros(Nh,Nh) zeros(Nh,Ng);
             G   zeros(Ng,Nx) zeros(Ng,Nh) zeros(Ng,Ng)]
        b = [∇J + D'nu + H'λ + G'μ; d; h; g]
    elseif type == :ad_lagrangian
        A = ForwardDiff.hessian(lagrangian,V)
        b = ForwardDiff.gradient(lagrangian,V)
    elseif type == :ad_aulag
        meritfun(V) = al_lagrangian(V,ρ)
        A = ForwardDiff.hessian(meritfun,V)
        b = ForwardDiff.gradient(meritfun,V)
    elseif type == :gradient_descent
        A = Diagonal(I,NN)
        b = ForwardDiff.gradient(lagrangian,V)
    end
    return A,b
end

function viewmatrix(A::Matrix,filename="logs/viewer.txt")
    io = open(filename,"w")
    show(IOContext(io, :limit=>false),"text/plain",A)
    close(io)
end

function create_results_from_newton(V)
    results = ConstrainedVectorResults(n,m,p,N,p_N)
    Z = V[1:Nz]
    X = reshape(Z[1:Nx],n,N)
    U = reshape(Z[Nx+1:end],m,N-1)
    nu = V[Nz.+(1:Nx)]
    λ = V[(Nz + Nx) .+ (1:Nh)]
    μ = V[(Nz + Nx + Nh) .+ (1:Ng)]
    copyto!(results.X,X)
    copyto!(results.U,U)

    lambda = reshape(view(λ,1:(N-1)*pE),pE,N-1)
    mu = reshape(view(μ,1:(N-1)*pI),pI,N-1)
    copyto!(results.λ,[mu; lambda])
    results.λ[N] = [μ[end-pI_N+1:end]; λ[end-pE_N+1:end]]
    return results
end

function newton_step(V,ρ,type;
    eps=1e-2, verbose=false, iters_linesearch=10, projection=:none, reg=Diagonal(zero(V)), meritfun=al_lagrangian(V,ρ))

    # Define constraint violation functions
    max_c2(V) = max(Ng > 0 ? maximum(cI(V)) : 0, norm(cE(V),Inf))
    max_c(V) = max(norm(dynamics(V),Inf),max_c2(V))

    V_ = copy(V)
    a = active_set(V_,eps)
    amu = a[ind1.μ]
    Z = V_[ind1.z]
    ∇²J = ForwardDiff.hessian(mycost,Z)
    d1 = dynamics(Z)
    h1 = cE(Z)
    g1 = cI(Z)
    y = [d1;h1;g1[amu]]
    println("max y: $(maximum(abs.(y)))")
    while maximum(abs.(y)) > 1e-6
        D = ForwardDiff.jacobian(dynamics,Z)
        H = ForwardDiff.jacobian(cE,Z)
        G = ForwardDiff.jacobian(cI,Z)
        Y = [D;H;G[amu,:]]

        #δV̂ = -(∇²J\(Y'))*(Y*(∇²J\(Y'))\y)
        δV̂ = -Y'*((Y*Y')\y)
        V_[ind1.z] += δV̂
        Z = V_[ind1.z]

        d1 = dynamics(Z)
        h1 = cE(Z)
        g1 = cI(Z)
        y = [d1;h1;g1[amu]]
        println("max y: $(maximum(abs.(y)))")
    end

    # Initial cost
    J0 = meritfun(V_)
    println("Initial Cost: $J0")

    # Build and solve KKT
    A,b = buildKKT(V_,ρ,type)
    a = active_set(V_,eps)
    amu = a[ind1.μ]
    δV = zero(V)
    Ā = A[a,a] + reg[a,a]
    δV[a] = -Ā\b[a]

    # Line Search
    ϕ=0.01
    α = 2
    δV1 = α.*δV
    J = J0+1e8
    while J > J0 #+ α*ϕ*b'δV1
        α *= 0.5
        δV1 = α.*δV
        V1 = V_ + δV1
        Z1 = V1[ind1.z]

        d1 = dynamics(Z1)
        h1 = cE(Z1)
        g1 = cI(Z1)
        y = [d1;h1;g1[amu]]
        println("max y: $(maximum(abs.(y)))")
        while maximum(abs.(y)) > 1e-6
            D = ForwardDiff.jacobian(dynamics,Z1)
            H = ForwardDiff.jacobian(cE,Z1)
            G = ForwardDiff.jacobian(cI,Z1)
            Y = [D;H;G[amu,:]]

            #δV̂ = -(∇²J\(Y'))*(Y*(∇²J\(Y'))\y)
            δV̂ = -Y'*((Y*Y')\y)
            δV1[ind1.z] += δV̂

            V1 = V_ + δV1
            Z1 = V1[ind1.z]

            d1 = dynamics(Z1)
            h1 = cE(Z1)
            g1 = cI(Z1)
            y = [d1;h1;g1[amu]]
            println("max y: $(maximum(abs.(y)))")
        end

        J = meritfun(V_ + δV1)
        println("New Cost: $J")
    end
    V1 = V_ + δV1
    Z1 = V1[ind1.z]

    # Multiplier projection
    ∇J = ForwardDiff.gradient(mycost,Z1)
    d1 = dynamics(Z1)
    h1 = cE(Z1)
    g1 = cI(Z1)
    y = [d1;h1;g1[amu]]
    D = ForwardDiff.jacobian(dynamics,Z1)
    H = ForwardDiff.jacobian(cE,Z1)
    G = ForwardDiff.jacobian(cI,Z1)
    Y = [D;H;G[amu,:]]
    ν = V1[ind1.ν]
    λ = V1[ind1.λ]
    μ = V1[ind1.μ]
    lambda = [ν;λ;μ[amu]]
    r = ∇J + Y'lambda
    δlambda = zero(lambda)
    println("max residual before: $(norm(r,Inf))")
    δlambda -= (Y*Y')\(Y*r)
    lambda1 = lambda + δlambda
    r = ∇J + Y'lambda1
    println("max residual after: $(norm(r,Inf))")
    # while norm(r,Inf) > 1e-6
    #     δlambda -= (Y*Y')\(Y*r)
    #     lambda1 = lambda + δlambda
    #     r = ∇J + Y'lambda1
    #     println("max residual: $(norm(r,Inf))")
    # end
    ν1 = lambda1[1:Nx]
    λ1 = lambda1[Nx .+ (1:Nh)]
    μ1 = lambda1[(Nx+Nh) .+ (1:count(amu))]
    V1[ind1.ν] = ν1
    V1[ind1.λ] = λ1
    V1[ind1.μ[amu]] = μ1
    J = meritfun(V1)
    println("New Cost: $J")

    # Take KKT Step
    cost = J
    A,b = buildKKT(V1,ρ,type)
    a = active_set(V1,eps)
    grad = norm(b[a])
    c_max = max_c(V1)

    change(x,x0) = format((x0-x)/x0*100,precision=4) * "%"
    println()
    println("  cost: $J $(change(J,J0))")
    println("  step: $(norm(δV1))")
    println("  grad: $(grad)")
    println("  c_max: $(c_max)")
    println("  c_max2: $(max_c2(V1))")
    println("  α: $α")
    println("  rank: $(rank(Ā))")
    println("  cond: $(cond(Ā))")
    stats = Dict("cost"=>cost,"grad"=>grad,"c_max"=>c_max)
    return V1
end

function createV(res)
    x = vec(res.X)
    u = vec(res.U)
    nu = zeros(n,N)

    for k = 1:N
        nu[:,k] = res.s[k]*0
    end
    nu = vec(nu)

    if res isa ConstrainedVectorResults
        s = zeros(pI,N-1)
        μ = zeros(pI,N-1)
        λ = zeros(pE,N-1)
        ineq = 1:pI
        eq = pI .+ (1:pE)
        for k = 1:N-1
            μ[:,k] = res.λ[k][ineq]
            λ[:,k] = res.λ[k][eq]
        end
        s = vec(s)
        μ = vec(μ)
        λ = vec(λ)
        append!(s, sqrt.(2.0*max.(0,-res.C[N][1:pI_N])))
        append!(μ, res.λ[N][1:pI_N])
        append!(λ, res.λ[N][pI_N .+ (1:pE_N)])
    else
        λ = Float64[]
        μ = Float64[]
    end
    return [x;u; nu; λ; μ]
end

function KKT_reg(;z=0,ν=0,λ=0,μ=0)
    r = ones(NN)
    r[ind1.z] .= z
    r[ind1.ν] .= -ν
    r[ind1.λ] .= -λ
    r[ind1.μ] .= -μ
    Diagonal(r)
end

isfullrank(A) = rank(A) == minimum(size(A))

Nx = N*n
Nu = (N-1)*m
Nz = Nx + Nu
Nh = (N-1)*pE + pE_N
Ng = (N-1)*pI + pI_N
NN = 2Nx + Nu + Nh + Ng
names = (:z,:ν,:λ,:μ)
ind1 = TrajectoryOptimization.create_partition([Nz,Nx,Nh,Ng],names)
ind2 = TrajectoryOptimization.create_partition2([Nz,Nx,Nh,Ng],names)
ind_pd = TrajectoryOptimization.create_partition([Nz,Nx+Nh+Ng],(:primal,:dual))

inds = (z=1:Nz, ν=Nz.+(1:Nx), λ=(Nz+Nx) .+ (1:Nh), μ=(Nz+Nx+Nh) .+ (1:Ng))

# Build KKT System
V = createV(res)
Z = V[ind1.z]

ρ = 1000
meritfun(V) = al_lagrangian(V,1)

V = createV(res)
reg = KKT_reg(z=1e-2,ν=1e-2,λ=1e-2,μ=1e-2)
V = newton_step(V,1,:kkt,projection=:jacobian,eps=1e-3,meritfun=meritfun)
Z = V[ind1.z]
cI(Z)

gen_usrfun_newton(solver)
newton_step2, = gen_newton_functions(solver)
V2 = NewtonVars(res)

newton_step2(V2,1,eps=1e-3)

import TrajectoryOptimization: NewtonSolver, gen_newton_functions, create_V
nsolver = NewtonSolver(solver)
nsolver.opts[:ϵ_as] = 1e-2
Vn = create_V(nsolver,res)
solve(nsolver,Vn,ρ,meritfun=meritfun)
solve(nsolver,res)
newton_step2,buildAb,act_set = gen_newton_functions(nsolver)

# Comparison
max_c2(V) = max(Ng > 0 ? maximum(cI(V)) : 0, norm(cE(V),Inf))
max_c(V) = max(norm(dynamics(V),Inf),max_c2(V))

ρ = 1
meritfun(V) = al_lagrangian(V,ρ)
V = createV(res)
Vn = create_V(nsolver,res)
V == Vn

A,b = buildKKT(V,ρ,:kkt)
An,bn = buildAb(Vn,ρ,:kkt)
A == An
b == bn

a = active_set(V,1e-2)
an = act_set(V,1e-2)
a == an

dv = dvn = zero(V)
dv[a] = -A[a,a]\b[a]
dvn[a] = -An[a,a]\bn[a]
dv == dvn

armijo_line_search(meritfun,V,dv,b)
armijo_line_search(meritfun,Vn,dvn,bn)

V1 = V + dv
V1n = Vn + dvn
V1 == V1n

max_c(V1)
max_c2(V1)

# Build KKT
# A,b = buildKKT(V,ρ,:penalty)
A_,b_ = buildKKT(V,ρ,:kkt)
a = active_set(Z,1e-2)
n_active = Ng - (length(a) - count(a))
amu = a[inds.μ]
reg = 0
A = A_[a,a] + KKT_reg(z=0,ν=reg,λ=reg,μ=reg)[a,a]
b = b_[a]

# Take Step
dv = zero(V)
dv[a] = -A\b
# dv[a] = -A[a,a]\b[a]
rank(A)
isfullrank(A)
cond(A)
r = A*dv[a] + b
norm(r)
# dv[a] = -A[a,a]\b[a]

α = armijo_line_search(meritfun,V,dv,b_)

# Projection corection
V1 = V + dv
Z1 = V1[ind1.z]
∇J = ForwardDiff.gradient(mycost,Z)
D = ForwardDiff.jacobian(dynamics,Z)
H = ForwardDiff.jacobian(cE,Z)
G = ForwardDiff.jacobian(cI,Z)
Y = [D;H;G[amu,:]]
d1 = dynamics(Z1)
h1 = cE(Z1)
g1 = cI(Z1)
y = [d1;h1;g1[amu]]
# dv̂ = -D'*((D*D')\d1)
dv̂ = -Y'*((Y*Y')\y)
dv[ind1.z] += dv̂
b̂ = [∇J + D'nu + H'λ + G'μ; d1; h1; g1]

# Resolve for constraints
_,b1 = buildKKT(V1,ρ,:kkt)
meritfun(V)
dv2[a] = A_[a,a]\b1[a]
α = armijo_line_search(meritfun,V,dv2,b1)

V1 = V + dv*α

# Evaluatex
dJ = J0 - lagrangian(V1)
d = dynamics(V1)
norm(d)
norm(d,Inf)
argmax(abs.(dynamics(V1)))
norm(cE(V1))
norm(b_)

g = cI(V1)
maximum(g[amu])
maximum(g)
amu[argmax(g)] == false

# Cap multipier
mu1 .= max.(0,mu1)
V1[inds.μ] = mu1
V = copy(V1)

results = create_results_from_newton(V)
copyto!(results.μ,res.μ)
TrajectoryOptimization.update_constraints!(results,solver)
TrajectoryOptimization.update_jacobians!(results,solver)
max_violation(results)
plot()
plot_trajectory!(results)

J0 = cost(solver,results)
copyto!(results.U_,results.U)
rollout!(results.X_,results.U_,solver)
TrajectoryOptimization.update_constraints!(results,solver,results.X_,results.U_)
max_violation(results)
plot_trajectory!(to_array(results.X_))
results.X_

∇v = backwardpass!(results,solver)
rollout!(results,solver,0.0)
max_violation(results)
J = cost(solver,results,results.X_,results.U_)
J0 - J
max_violation(res)

copyto!(results.X,results.X_)
copyto!(results.U,results.U_)
plot_trajectory!(to_array(results.X_))
Vnew = createV(results)
norm(dynamics(Vnew),Inf)

ForwardDiff.gradient(lagrangian,V1)
Z = V1[1:Nz]
X = reshape(Z[1:Nx],n,N)
U = reshape(Z[Nx.+(1:Nu)],m,N-1)
plot(X')
lagrangian(V1)

Afd,bfd = buildKKT(V,ρ,:ad_lagrangian)

meritfun(V) = al_lagrangian(V,ρ)
armijo_line_search(meritfun,V,-b,b; max_iter=15)
J0 = meritfun(V)
meritfun(V + 0.001*b)

# Check difference in Hessians
norm(Afd-A_)
norm(bfd-b_)

ρ = 1e10
V_pen, stats_pen = solve_newton(V,ρ,:penalty,verbose=true)
V_kkt, stats_kkt = solve_newton(V,ρ,:kkt,verbose=true)
V_fd, stats_fd = solve_newton(V,ρ,:ad_lagrangian,verbose=true)
V_fda, stats_fda = solve_newton(V,ρ,:ad_aulag)

mycost(V_pen[1:Nz])
val = "c_max"
plot(stats_pen[val],label="penalty",title=val,xlabel="iteration",yscale=:log10)
plot!(stats_kkt[val],label="kkt")
plot!(stats_fd[val],label="fd-lagrangian")
plot!(stats_fda[val],label="fd-aug lagrangian")


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder

name = "Libgcrypt"
version = v"1.8.5"

# Collection of sources required to build libgcrypt
sources = [
    ArchiveSource("https://gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-$(version).tar.bz2",
                  "3b4a2a94cb637eff5bdebbcaf46f4d95c4f25206f459809339cdada0eb577ac3"),
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir/libgcrypt-*/
./configure --prefix=${prefix} --build=${MACHTYPE} --host=${target} \
    --disable-padlock-support \
    --disable-asm
make -j${nproc}
make install
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line.  We are manually disabling
# many platforms that do not seem to work.
platforms = supported_platforms()

# The products that we will ensure are always built
products = [
    LibraryProduct("libgcrypt", :libgcrypt),
]

# Dependencies that must be installed before this package can be built
dependencies = [
    Dependency("Libgpg_error_jll"),
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies)

ext: jl

max_stars_repo_name: waralex/Yggdrasil

max_issues_repo_name: waralex/Yggdrasil

max_forks_repo_name: waralex/Yggdrasil


text:
using GDAL
using Base.Test

info(unsafe_string(GDAL.C.GDALVersionInfo("--version")))

# drivers
# before being able to use any drivers, they must be registered first
GDAL.allregister()
info(GDAL.getdrivercount(), " GDAL drivers found")
info(GDAL.ogrgetdrivercount(), " OGR drivers found")
@test GDAL.getdrivercount() > 0
@test GDAL.ogrgetdrivercount() > 0

# test if the registered error handler also handles CE_Fatal
@test_throws GDAL.GDALError GDAL.emergencyerror("we've got a problem")
GDAL.errorreset()

# throw errors on non existing files
@test_throws GDAL.GDALError GDAL.open("NonExistent", GDAL.GA_ReadOnly)
# if a driver is not found, the C API returns a null
@test GDAL.C.GDALGetDriverByName("NonExistent") == C_NULL
# whilst the rewritten API throws a GDALError
@test_throws GDAL.GDALError GDAL.getdriverbyname("NonExistent")

srs = GDAL.newspatialreference(C_NULL)
GDAL.importfromepsg(srs, 4326) # fails if GDAL_DATA is not set correctly

xmlnode_pointer = GDAL.C.CPLParseXMLString("hi")
@test unsafe_string(GDAL.C.CPLGetXMLValue(xmlnode_pointer, "b", "")) == "hi"
# load into Julia struct, mutate, and put back as Ref into GDAL
xmlnode = unsafe_load(xmlnode_pointer)
xmlnode.pszValue = Base.unsafe_convert(Cstring, "c")  # rename "a" to "c"
@test unsafe_string(GDAL.C.CPLSerializeXMLTree(Ref(xmlnode))) == "<c>\n  hi\n</c>\n"
GDAL.C.CPLDestroyXMLNode(xmlnode_pointer)

# ref https://github.com/JuliaGeo/GDAL.jl/pull/41#discussion_r143345433
gfld = GDAL.gfld_create("name-a", GDAL.wkbPoint)
@test gfld isa Ptr{GDAL.OGRGeomFieldDefnH}
@test GDAL.getnameref(gfld) == "name-a"
@test GDAL.gettype(gfld) == GDAL.wkbPoint
# same as above but for the lower level C API
gfld = GDAL.C.OGR_GFld_Create("name-b", GDAL.C.wkbPolygon)
@test gfld isa Ptr{Void}
@test unsafe_string(GDAL.C.OGR_GFld_GetNameRef(gfld)) == "name-b"
@test GDAL.C.OGR_GFld_GetType(gfld) == GDAL.C.wkbPolygon

cd(dirname(@__FILE__)) do
    isdir("tmp") || mkpath("tmp") # ensure it exists

    include("tutorial_raster.jl")
    include("tutorial_vector.jl")
    include("tutorial_vrt.jl")
    include("gdal_utils.jl")
end

GDAL.destroydrivermanager()


text:
"""
$(TYPEDEF)

An custum bath object defined by the two-point correlation function and the corresponding spectrum.

$(FIELDS)
"""
mutable struct CustomBath <: AbstractBath
    "correlation function"
    cfun::Any
    "spectrum"
    γ::Any
end

CustomBath(; correlation=nothing, spectrum=nothing) =
    CustomBath(correlation, spectrum)
correlation(τ, bath::CustomBath) = bath.cfun(τ)
spectrum(ω, bath::CustomBath) = bath.γ(ω)

function build_correlation(bath::CustomBath)
    isnothing(bath.cfun) && error("Correlation function is not specified.")
    if numargs(bath.cfun) == 1
        SingleFunctionMatrix((t₁, t₂) -> bath.cfun(t₁ - t₂))
    else
        SingleFunctionMatrix(bath.cfun)
    end
end

build_spectrum(bath::CustomBath) =
    isnothing(bath.γ) ? error("Noise spectrum is not specified.") : bath.γ

"""
$(TYPEDEF)

`CorrelatedBath` defines a correlated bath type by the matrices of its two-point correlation functions and corresponding spectrums.

$(FIELDS)
"""
mutable struct CorrelatedBath <: AbstractBath
    "correlation function"
    cfun::Any
    "spectrum"
    γ::Any
    "bath correlator idx"
    inds::Any
end

CorrelatedBath(inds; correlation=nothing, spectrum=nothing) = CorrelatedBath(correlation, spectrum, inds)
build_correlation(bath::CorrelatedBath) = isnothing(bath.cfun) ? throw(ArgumentError("Correlation function is not specified.")) : bath.cfun
build_spectrum(bath::CorrelatedBath) = isnothing(bath.γ) ? error("Spectrum is not specified.") : bath.γ
build_inds(bath::CorrelatedBath) = bath.inds

function build_lambshift(ω_range::AbstractVector, turn_on::Bool, bath::CorrelatedBath, ::Nothing)
    gamma = build_spectrum(bath)
    l = size(gamma)
    if turn_on == true
        if isempty(ω_range)
            S_loc = Array{Function,2}(undef, l)
            for i in eachindex(gamma)
                S_loc[i] = (w) -> lambshift(w, gamma[i])
            end
        else
            S_loc = Array{Any,2}(undef, l)
            for i in eachindex(gamma)
                s_list = [lambshift(ω, gamma[i]) for ω in ω_range]
                S_loc[i] = construct_interpolations(ω_range, s_list)
            end
        end
    else
        S_loc = Array{Function,2}(undef, size(gamma))
        S_loc .= (ω) -> 0.0
    end
    S_loc
end

function build_lambshift(ω_range::AbstractVector, ::Bool, bath::CorrelatedBath, lambshift_S::Dict)
    gamma = build_spectrum(bath)
    l = size(gamma)
    if isempty(ω_range)
        S_loc = Array{Function,2}(undef, l)
        for i in eachindex(gamma)
            S_loc[i] = (w) -> lambshift(w, gamma[i]; lambshift_S...)
        end
    else
        S_loc = Array{Any,2}(undef, l)
        for i in eachindex(gamma)
            s_list = [lambshift(ω, gamma[i]; lambshift_S...) for ω in ω_range]
            S_loc[i] = construct_interpolations(ω_range, s_list)
        end
    end
    S_loc
end

"""
$(TYPEDEF)

A bath object to hold jump correlator of ULE.

$(FIELDS)
"""
struct ULEBath <: AbstractBath
    "correlation function"
    cfun::Any
end

function build_jump_correlation(bath::ULEBath)
    if numargs(bath.cfun) == 1
        SingleFunctionMatrix((t₁, t₂) -> bath.cfun(t₁ - t₂))
    else
        SingleFunctionMatrix(bath.cfun)
    end
end


text:
#
# location from range measurement code
#
# author: Atsushi Sakai
#

using Base.Test
using PyPlot
const __MAIN__ = length(PROGRAM_FILE)!=0 && contains(@__FILE__, PROGRAM_FILE)

include("../../argmin.jl")
using argmin

function main()
    println(PROGRAM_FILE," start!!")
   	A = [ 1.8  2.5;
   	  	  2.0  1.7;
		  1.5  1.5;
		  1.5  2.0;
		  2.5  1.5]

	x0 = [3.0, 1.0]

   	dist(x) = sqrt.( (x[1] .- A[:,1]).^2 + (x[2] .- A[:,2]).^2 )
	f(x) = dist(x)
	Df(x) = diagm(1 ./ dist(x)) * [ (x[1] .- A[:,1])  (x[2] .- A[:,2]) ]

	xhat, status = solve_nonlinear_least_square_with_levenberg_marquardt(f,Df,x0, 1.0)
	println(xhat)

	plot(A[:,1], A[:,2], "*r")
	plot(status["x_history"][:,1], status["x_history"][:,2], "-k")
	plot(xhat[1], xhat[2], "ob")
	axis("equal")
	show()

    println(PROGRAM_FILE," Done!!")
end

if __MAIN__
    @time main()
end


text:
struct And end

(a::And)(x::BOOL, y::BOOL) = x(y, FALSE())

struct Or end

(o::Or)(x::BOOL, y::BOOL) = x(TRUE(), y)

struct Not end

(n::Not)(x::BOOL) = x(FALSE(), TRUE())

struct Eq end

(e::Eq)(x::BOOL, y::BOOL) = x(y, Not()(y))

struct Xor end

(o::Xor)(x::BOOL, y::BOOL) = Not()(Eq()(x, y))


text:
using Base.Threads

function simulate_signal(sigtype, f_s, t_length, prns, cn0s, f_ds, fd_rates,
                         phis, code_start_idxs, h_parms;
                         nADC=8, f_if=0, Tsys=535)
    @assert (length(prns) == length(cn0s) == length(f_ds) == length(fd_rates) ==
             length(phis) == length(code_start_idxs))
    if nADC == 8
        dtype = Complex{Int8}
    elseif nADC == 16
        dtype = Complex{Int16}
    elseif nADC == 32
        dtype = Complex{Int32}
    elseif nADC == 64
        dtype = Complex{Int64}
    else
        error("Invalid nADC value. Supported values are 8, 16, 32, and 64.")
    end
    sig_freq = sigtype.sig_freq
    sample_num = floor(Int, f_s*t_length)
    iq_values = zeros(Complex{Float64}, sample_num)
    signal = definesignal(sigtype, f_s, t_length; Tsys=Tsys, receiver_h_parms=h_parms)
    M = length(prns)
    # Add signal carriers
    for i in 1:M
        prn = prns[i]
        cn0 = cn0s[i]
        f_d = f_ds[i]
        fd_rate = fd_rates[i]
        phi = phis[i]
        code_start_idx = code_start_idxs[i]
        definesignal!(signal; prn=prn, f_if=f_if, f_d=f_d, fd_rate=fd_rate,
                      Tsys=Tsys, CN0=cn0, phi=phi, nADC=nADC, include_carrier=true,
                      include_thermal_noise=false, include_phase_noise=false,
                      include_adc=false, code_start_idx=code_start_idx)
        generatesignal!(signal)
        @threads for j in 1:sample_num
            iq_values[j] = iq_values[j] + signal.data[j]
        end
    end
    B = maximum(skipmissing([sigtype.B_I, sigtype.B_Q]))
    σₙ = sqrt(GNSSTools.k*B*Tsys)
    # Add noise sources
    @threads for i in 1:sample_num
        iq_values[i] = iq_values[i]*cis(real(signal.phase_noise[i])) + σₙ*signal.thermal_noise[i]
    end
    # Quantize signal
    adc_scale = 2^(nADC-1)
    sigmax = sqrt(maximum(abs2, iq_values))
    @threads for i in 1:sample_num
        iq_values[i] = round(iq_values[i]*adc_scale/sigmax)
        if real(iq_values[i]) == adc_scale
            iq_values[i] = iq_values[i] - 1
        end
        if imag(iq_values[i]) == adc_scale
            iq_values[i] = iq_values[i] - 1im
        end
    end
    # Export GNSSTools.GNSSData struct
    data_type = string("sc", "$nADC")
    start_data_idx = 0
    data_start_time = missing
    site_loc_lla = missing
    total_data_length = t_length
    start_t = 0
    data = GNSSTools.GNSSData("", f_s, f_if, t_length, start_data_idx,
                              dtype.(iq_values), data_type, data_start_time,
                              site_loc_lla, sample_num, total_data_length, nADC,
                              start_t)
    return data
end

function get_signal_truth_parms(t_length, f_s, code_start_idx, f_d, fd_rate, phi0, cn0,
                                Tsys, sigtype, channel, T; dt=0.001,
                                B=maximum(skipmissing([sigtype.B_I, sigtype.B_Q])))
    t = calctvector(floor(Int, t_length/dt), 1/dt)
    sig_freq = sigtype.sig_freq
    if channel == "Q"
        codes = sigtype.Q_codes
    elseif channel == "I"
        codes = sigtype.I_codes
    else
        error("Invalid channel value. Only 'I' and 'Q' allowed.")
    end
    code_length = codes.code_lengths[1]
    chipping_rate = codes.chipping_rates[1]
    phi = phi0 .+ 2π*(f_d .* t .+ 0.5 .* fd_rate .* t.^2)
    A = sqrt(2*GNSSTools.k*Tsys)*10^(cn0/20)
    σ = sqrt(GNSSTools.k*B*Tsys)
    PS = floor(Int, f_s*T)*A^2
    PN = σ^2
    snr = calc_snr(PS,PN)
    doppler = f_d .+ fd_rate.*t
    doppler_rate = fill(fd_rate, length(t))
    f_code_d, f_code_dd = GNSSTools.calc_doppler_code_rate(chipping_rate,
                                                           sig_freq, f_d, fd_rate)
    n0 = calcinitcodephase(code_length, f_code_d, f_code_dd, f_s,
                           code_start_idx)
    code_phase = n0 .+ f_code_d .* t .+ 0.5 .* f_code_dd .* t.^2
    return (t, doppler, doppler_rate, phi, code_phase, snr)
end


text:
using Colors
using CSV
using DataFrames
using Distributed
using Distributions
using GLM
using InformationMeasures
using MultivariateStats
using Plots
using Statistics

# num_bins is in infotheory_handling.jl
# num_populations is in sample_handling.jl

debug = false

include("fcsfile_handling.jl")
include("infotheory_handling.jl")
include("sample_handling.jl")

tubelist_location = "./tubelist_wiggled_br1-2.csv" # use wiggled tube list
println("Reading in tubelist...")
tubelist = CSV.read(tubelist_location, DataFrame)

crs = unique(tubelist[!, Symbol("Regulator")])
bioreps = unique(tubelist[!, Symbol("Biorep")])
inputs = [0, 2, 3, 5]

# timepoint_dict = Dict(
#     "KRAB" => [0, 1, 2, 5, 7, 10, 13, 24, 31],
#     "EED" => [0, 1, 2, 3, 7, 16, 21, 24, 27, 31],
#     "DNMT3B" => [0, 1, 2, 4, 5, 7, 10, 28, 31],
#     "HDAC4" => [0, 1, 2, 5, 7, 10, 13, 24, 31])

timepoint_dict = Dict(
    "KRAB" => [0, 3.5, 5, 10, 12.5, 17],
    "EED" => [0, 3, 9.5, 12, 15.5],
    "DNMT3B" => [0, 3.5, 5, 10, 13, 17.5],
    "HDAC4" => [0, 3.5, 5, 10, 12.5, 17])

cr_input_dict = Dict(cr =>
                     Dict(timepoint =>
                          Dict(input =>
                               Dict(biorep =>
                                    String[] for biorep in bioreps)
                               for input in inputs)
                          for timepoint in timepoint_dict[cr])
                     for cr in crs)

#= Returns a dictionary of input => output fcs files from given tubelist csv

INPUTS: tubelist_location is a string that gives the path to a csv file containing
a list of tubes/conditions

OUTPUTS: cr_input_dict is a dict from cr => timepoint => input => [fcsfiles]
len(fcsfiles) in that is just the # of bioreps
=#
function get_cr_input_dict()
    for cr in crs
        for timepoint in timepoint_dict[cr]
            for input in inputs
                for biorep in bioreps
                    tubedf = tubelist[
                                    (tubelist[!, Symbol("Regulator")].==cr).&
                                    (tubelist[!, Symbol("Biorep")].==biorep).&
                                    (tubelist[!, Symbol("Days after dox")].==timepoint).&
                                    (tubelist[!, Symbol("Days with dox")].==input),
                                : ]
                    if length(tubedf[!, Symbol("Tube")]) != 1
                        println("WHOOPS: ")
                        print(cr)
                        print(", biorep ")
                        print(biorep)
                        print(", +dox ")
                        print(input)
                        print(", -dox ")
                        print(timepoint)
                        println(tubedf[!, Symbol("Tube")])
                    end
                    fcsfile = "./fcs/"*tubedf[!, Symbol("Tube")][1]
                    if fcsfile[length(fcsfile)-3:end]!=".fcs"
                        fcsfile = fcsfile*".fcs"
                    end
                    cr_input_dict[cr][timepoint][input][biorep] = [fcsfile]
                end
            end
        end
    end

    return cr_input_dict
end

println("Getting CR input dict...")
cr_input_dict = get_cr_input_dict()
println("Done.")

#= Given dict of input => fcs paths, returns dict of input => fitc vals =#
function get_input_fitc_dict(input_fcs_dict)
    # assumes input_fcs_dict is a dict from input => fcs paths

    input_biorep_fitc_dict = Dict(input=>
                                  Dict(biorep=>
                                       Float64[] for biorep in keys(input_fcs_dict[input]))
                                  for input in keys(input_fcs_dict))

    # input_fitc_dict = Dict(k=>Float64[] for k in keys(input_fcs_dict))

    for input in keys(input_fcs_dict)
        for (biorep, fcsfile) in input_fcs_dict[input]
            @assert length(fcsfile)==1
            input_biorep_fitc_dict[input][biorep] = get_filtered_flow_data(fcsfile[1])
        end
    end

    # for (input, fcsfilelist) in input_fcs_dict
    #     for fcsfile in fcsfilelist
    #         fitcs = get_filtered_flow_data(fcsfile)
    #         append!(input_fitc_dict[input], fitcs)
    #     end
    # end

    return input_biorep_fitc_dict
end
#= Given input CR dict, returns the mapping to FITCs with separated bioreps =#
function get_cr_fitc_dict(cr_input_dict)
    cr_fitc_dict = Dict(cr =>
                        Dict(timepoint =>
                             Dict(input =>
                                  Dict(biorep => Float64[] for biorep in bioreps)
                                  for input in inputs)
                             for timepoint in timepoint_dict[cr])
                        for cr in crs)

    for cr in crs
        for timepoint in timepoint_dict[cr]
            cr_fitc_dict[cr][timepoint] = get_input_fitc_dict(cr_input_dict[cr][timepoint])
        end
    end

    return cr_fitc_dict
end

# function get_channel_capacities(cr_fitc_dict)
#     cr_cc_dict = Dict(cr =>
#                       Dict(timepoint => Float64[] for timepoint in timepoint_dict[cr])
#                       for cr in crs)
#
#     for cr in crs
#         for timepoint in timepoint_dict[cr]
#             fitc_dict = cr_fitc_dict[cr][timepoint]
#             pop_cc = get_pop_cc(fitc_dict)
#             snc_cc = get_sc_c(fitc_dict)
#             cr_cc_dict[cr][timepoint] = [snc_cc, pop_cc]
#         end
#     end
#
#     return cr_cc_dict
# end

#= Gets dict from timepoint => input => biorep => FITC[] from input_dict, cr =#
function get_fitcs_for_cr(cr_input_dict, cr)
    fitc_dict = Dict(timepoint =>
                     Dict(input =>
                          Dict(biorep => Float64[] for biorep in bioreps)
                          for input in inputs)
                     for timepoint in timepoint_dict[cr])

    for timepoint in timepoint_dict[cr]
        fitc_dict[timepoint] = get_input_fitc_dict(cr_input_dict[cr][timepoint])
    end

    return fitc_dict
end

function get_ccs_for_timepoints(timepoint_input_fitc_dict)
    timepoints = keys(timepoint_input_fitc_dict)
    cc_dict = Dict(timepoint => [] for timepoint in timepoints)
    for timepoint in timepoints
        input_dict = timepoint_input_fitc_dict[timepoint]
        sc_cc = get_split_sc_cc(input_dict)
        print(timepoint)
        println(" => ")
        pop_cc = get_split_pop_cc(input_dict)
        cc_dict[timepoint] = [Float64.(sc_cc), Float64.(pop_cc)]
    end
    return cc_dict
end

function get_ccs_for_cr(cr)
    fitcs = get_fitcs_for_cr(cr_input_dict, cr)
    ccs = get_ccs_for_timepoints(fitcs)
    return ccs
end

println("HDAC4: ")
println(get_ccs_for_cr("HDAC4"))
println()
println()
println("KRAB: ")
println(get_ccs_for_cr("KRAB"))


text:
# Skipping MacroDefinition: va_start(ap,param)__builtin_va_start(ap,param)
# Skipping MacroDefinition: va_end(ap)__builtin_va_end(ap)
# Skipping MacroDefinition: va_arg(ap,type)__builtin_va_arg(ap,type)
# Skipping MacroDefinition: va_copy(dest,src)__builtin_va_copy(dest,src)
const CD_CTXPLUS_COUNT = 6
const CD_CTX_PLUS = 0xFF00
# Skipping MacroDefinition: CD_ALPHA_BLEND(_src,_dst,_alpha)(unsignedchar)(((_src)*(_alpha)+(_dst)*(255-(_alpha)))/255)
const CD_NAME = "CD - Canvas Draw"
const CD_DESCRIPTION = "A 2D Graphics Library"
const CD_COPYRIGHT = "Copyright (C) 1994-2014 Tecgraf, PUC-Rio."
const CD_VERSION = "5.8"
const CD_VERSION_NUMBER = 508000
const CD_VERSION_DATE = "2014/07/25"
# Skipping MacroDefinition: cdAlpha(_)(unsignedchar)(~(((_)>>24)&0xFF))
# Skipping MacroDefinition: cdReserved(_)(unsignedchar)(((_)>>24)&0xFF)
# Skipping MacroDefinition: cdRed(_)(unsignedchar)(((_)>>16)&0xFF)
# Skipping MacroDefinition: cdGreen(_)(unsignedchar)(((_)>>8)&0xFF)
# Skipping MacroDefinition: cdBlue(_)(unsignedchar)(((_)>>0)&0xFF)
const CD_QUERY = -1
const CD_POLYCUSTOM = 10
const CD_CAP_NONE = 0x00000000
const CD_CAP_FLUSH = 0x00000001
const CD_CAP_CLEAR = 0x00000002
const CD_CAP_PLAY = 0x00000004
const CD_CAP_YAXIS = 0x00000008
const CD_CAP_CLIPAREA = 0x00000010
const CD_CAP_CLIPPOLY = 0x00000020
const CD_CAP_REGION = 0x00000040
const CD_CAP_RECT = 0x00000080
const CD_CAP_CHORD = 0x00000100
const CD_CAP_IMAGERGB = 0x00000200
const CD_CAP_IMAGERGBA = 0x00000400
const CD_CAP_IMAGEMAP = 0x00000800
const CD_CAP_GETIMAGERGB = 0x00001000
const CD_CAP_IMAGESRV = 0x00002000
const CD_CAP_BACKGROUND = 0x00004000
const CD_CAP_BACKOPACITY = 0x00008000
const CD_CAP_WRITEMODE = 0x00010000
const CD_CAP_LINESTYLE = 0x00020000
const CD_CAP_LINEWITH = 0x00040000
const CD_CAP_FPRIMTIVES = 0x00080000
const CD_CAP_HATCH = 0x00100000
const CD_CAP_STIPPLE = 0x00200000
const CD_CAP_PATTERN = 0x00400000
const CD_CAP_FONT = 0x00800000
const CD_CAP_FONTDIM = 0x01000000
const CD_CAP_TEXTSIZE = 0x02000000
const CD_CAP_TEXTORIENTATION = 0x04000000
const CD_CAP_PALETTE = 0x08000000
const CD_CAP_LINECAP = 0x10000000
const CD_CAP_LINEJOIN = 0x20000000
const CD_CAP_PATH = 0x40000000
const CD_CAP_BEZIER = 0x80000000
const CD_CAP_ALL = 0xFFFFFFFF
const CD_SIZECB = 0
const CD_ABORT = 1
const CD_CONTINUE = 0
const CD_SIM_NONE = 0x0000
const CD_SIM_LINE = 0x0001
const CD_SIM_RECT = 0x0002
const CD_SIM_BOX = 0x0004
const CD_SIM_ARC = 0x0008
const CD_SIM_SECTOR = 0x0010
const CD_SIM_CHORD = 0x0020
const CD_SIM_POLYLINE = 0x0040
const CD_SIM_POLYGON = 0x0080
const CD_SIM_TEXT = 0x0100
const CD_SIM_ALL = 0xFFFF
const CD_SIM_LINES = (CD_SIM_LINE|CD_SIM_RECT|CD_SIM_ARC|CD_SIM_POLYLINE)
const CD_SIM_FILLS = (CD_SIM_BOX|CD_SIM_SECTOR|CD_SIM_CHORD|CD_SIM_POLYGON)
const CD_RED = 0xFF0000
const CD_DARK_RED = 0x800000
const CD_GREEN = 0x00FF00
const CD_DARK_GREEN = 0x008000
const CD_BLUE = 0x0000FF
const CD_DARK_BLUE = 0x000080
const CD_YELLOW = 0xFFFF00
const CD_DARK_YELLOW = 0x808000
const CD_MAGENTA = 0xFF00FF
const CD_DARK_MAGENTA = 0x800080
const CD_CYAN = 0x00FFFF
const CD_DARK_CYAN = 0x008080
const CD_WHITE = 0xFFFFFF
const CD_BLACK = 0x000000
const CD_DARK_GRAY = 0x808080
const CD_GRAY = 0xC0C0C0
const CD_MM2PT = 2.834645669
const CD_RAD2DEG = 57.295779513
const CD_DEG2RAD = 0.01745329252
# Skipping MacroDefinition: cdPutImageRGB(iw,ih,r,g,b,x,y,w,h)cdPutImageRectRGB((iw),(ih),(r),(g),(b),(x),(y),(w),(h),0,0,0,0)
# Skipping MacroDefinition: cdPutImageRGBA(iw,ih,r,g,b,a,x,y,w,h)cdPutImageRectRGBA((iw),(ih),(r),(g),(b),(a),(x),(y),(w),(h),0,0,0,0)
# Skipping MacroDefinition: cdPutImageMap(iw,ih,index,colors,x,y,w,h)cdPutImageRectMap((iw),(ih),(index),(colors),(x),(y),(w),(h),0,0,0,0)
# Skipping MacroDefinition: cdPutImage(image,x,y)cdPutImageRect((image),(x),(y),0,0,0,0)
# Skipping MacroDefinition: cdCanvas2Raster(x,y){(void)x;cdUpdateYAxis(y);}
#		typealias va_list __builtin_va_list

type cdCtxCanvasBase
    canvas::Ptr{Cint}
end

type cdPoint
    x::Cint
    y::Cint
end
type cdfPoint
    x::Cdouble
    y::Cdouble
end
type cdRect
    xmin::Cint
    xmax::Cint
    ymin::Cint
    ymax::Cint
end
type cdfRect
    xmin::Cdouble
    xmax::Cdouble
    ymin::Cdouble
    ymax::Cdouble
end
type cdAttribute
    name::Ptr{UInt8}
    set::Ptr{Void}
    get::Ptr{Void}
end
type cdImage
    w::Cint
    h::Cint
    ctximage::Ptr{Void}
    cxGetImage::Ptr{Void}
    cxPutImageRect::Ptr{Void}
    cxKillImage::Ptr{Void}
end
type cdContext
    caps::Culong
    _type::Cint
    cxCreateCanvas::Ptr{Void}
    cxInitTable::Ptr{Void}
    cxPlay::Ptr{Void}
    cxRegisterCallback::Ptr{Void}
end

immutable Array_2_UInt8
    d1::UInt8
    d2::UInt8
end
immutable Array_6_Cdouble
    d1::Cdouble
    d2::Cdouble
    d3::Cdouble
    d4::Cdouble
    d5::Cdouble
    d6::Cdouble
end
immutable Array_1024_UInt8
    d1::UInt8
    d2::UInt8
    d3::UInt8
    d4::UInt8
    d5::UInt8
    d6::UInt8
    d7::UInt8
    d8::UInt8
    d9::UInt8
    d10::UInt8
    d11::UInt8
    d12::UInt8
    d13::UInt8
    d14::UInt8
    d15::UInt8
    d16::UInt8
    d17::UInt8
    d18::UInt8
    d19::UInt8
    d20::UInt8
    d21::UInt8
    d22::UInt8
    d23::UInt8
    d24::UInt8
    d25::UInt8
    d26::UInt8
    d27::UInt8
    d28::UInt8
    d29::UInt8
    d30::UInt8
    d31::UInt8
    d32::UInt8
    d33::UInt8
    d34::UInt8
    d35::UInt8
    d36::UInt8
    d37::UInt8
    d38::UInt8
    d39::UInt8
    d40::UInt8
    d41::UInt8
    d42::UInt8
    d43::UInt8
    d44::UInt8
    d45::UInt8
    d46::UInt8
    d47::UInt8
    d48::UInt8
    d49::UInt8
    d50::UInt8
    d51::UInt8
    d52::UInt8
    d53::UInt8
    d54::UInt8
    d55::UInt8
    d56::UInt8
    d57::UInt8
    d58::UInt8
    d59::UInt8
    d60::UInt8
    d61::UInt8
    d62::UInt8
    d63::UInt8
    d64::UInt8
    d65::UInt8
    d66::UInt8
    d67::UInt8
    d68::UInt8
    d69::UInt8
    d70::UInt8
    d71::UInt8
    d72::UInt8
    d73::UInt8
    d74::UInt8
    d75::UInt8
    d76::UInt8
    d77::UInt8
    d78::UInt8
    d79::UInt8
    d80::UInt8
    d81::UInt8
    d82::UInt8
    d83::UInt8
    d84::UInt8
    d85::UInt8
    d86::UInt8
    d87::UInt8
    d88::UInt8
    d89::UInt8
    d90::UInt8
    d91::UInt8
    d92::UInt8
    d93::UInt8
    d94::UInt8
    d95::UInt8
    d96::UInt8
    d97::UInt8
    d98::UInt8
    d99::UInt8
    d100::UInt8
    d101::UInt8
    d102::UInt8
    d103::UInt8
    d104::UInt8
    d105::UInt8
    d106::UInt8
    d107::UInt8
    d108::UInt8
    d109::UInt8
    d110::UInt8
    d111::UInt8
    d112::UInt8
    d113::UInt8
    d114::UInt8
    d115::UInt8
    d116::UInt8
    d117::UInt8
    d118::UInt8
    d119::UInt8
    d120::UInt8
    d121::UInt8
    d122::UInt8
    d123::UInt8
    d124::UInt8
    d125::UInt8
    d126::UInt8
    d127::UInt8
    d128::UInt8
    d129::UInt8
    d130::UInt8
    d131::UInt8
    d132::UInt8
    d133::UInt8
    d134::UInt8
    d135::UInt8
    d136::UInt8
    d137::UInt8
    d138::UInt8
    d139::UInt8
    d140::UInt8
    d141::UInt8
    d142::UInt8
    d143::UInt8
    d144::UInt8
    d145::UInt8
    d146::UInt8
    d147::UInt8
    d148::UInt8
    d149::UInt8
    d150::UInt8
    d151::UInt8
    d152::UInt8
    d153::UInt8
    d154::UInt8
    d155::UInt8
    d156::UInt8
    d157::UInt8
    d158::UInt8
    d159::UInt8
    d160::UInt8
    d161::UInt8
    d162::UInt8
    d163::UInt8
    d164::UInt8
    d165::UInt8
    d166::UInt8
    d167::UInt8
    d168::UInt8
    d169::UInt8
    d170::UInt8
    d171::UInt8
    d172::UInt8
    d173::UInt8
    d174::UInt8
    d175::UInt8
    d176::UInt8
    d177::UInt8
    d178::UInt8
    d179::UInt8
    d180::UInt8
    d181::UInt8
    d182::UInt8
    d183::UInt8
    d184::UInt8
    d185::UInt8
    d186::UInt8
    d187::UInt8
    d188::UInt8
    d189::UInt8
    d190::UInt8
    d191::UInt8
    d192::UInt8
    d193::UInt8
    d194::UInt8
    d195::UInt8
    d196::UInt8
    d197::UInt8
    d198::UInt8
    d199::UInt8
    d200::UInt8
    d201::UInt8
    d202::UInt8
    d203::UInt8
    d204::UInt8
    d205::UInt8
    d206::UInt8
    d207::UInt8
    d208::UInt8
    d209::UInt8
    d210::UInt8
    d211::UInt8
    d212::UInt8
    d213::UInt8
    d214::UInt8
    d215::UInt8
    d216::UInt8
    d217::UInt8
    d218::UInt8
    d219::UInt8
    d220::UInt8
    d221::UInt8
    d222::UInt8
    d223::UInt8
    d224::UInt8
    d225::UInt8
    d226::UInt8
    d227::UInt8
    d228::UInt8
    d229::UInt8
    d230::UInt8
    d231::UInt8
    d232::UInt8
    d233::UInt8
    d234::UInt8
    d235::UInt8
    d236::UInt8
    d237::UInt8
    d238::UInt8
    d239::UInt8
    d240::UInt8
    d241::UInt8
    d242::UInt8
    d243::UInt8
    d244::UInt8
    d245::UInt8
    d246::UInt8
    d247::UInt8
    d248::UInt8
    d249::UInt8
    d250::UInt8
    d251::UInt8
    d252::UInt8
    d253::UInt8
    d254::UInt8
    d255::UInt8
    d256::UInt8
    d257::UInt8
    d258::UInt8
    d259::UInt8
    d260::UInt8
    d261::UInt8
    d262::UInt8
    d263::UInt8
    d264::UInt8
    d265::UInt8
    d266::UInt8
    d267::UInt8
    d268::UInt8
    d269::UInt8
    d270::UInt8
    d271::UInt8
    d272::UInt8
    d273::UInt8
    d274::UInt8
    d275::UInt8
    d276::UInt8
    d277::UInt8
    d278::UInt8
    d279::UInt8
    d280::UInt8
    d281::UInt8
    d282::UInt8
    d283::UInt8
    d284::UInt8
    d285::UInt8
    d286::UInt8
    d287::UInt8
    d288::UInt8
    d289::UInt8
    d290::UInt8
    d291::UInt8
    d292::UInt8
    d293::UInt8
    d294::UInt8
    d295::UInt8
    d296::UInt8
    d297::UInt8
    d298::UInt8
    d299::UInt8
    d300::UInt8
    d301::UInt8
    d302::UInt8
    d303::UInt8
    d304::UInt8
    d305::UInt8
    d306::UInt8
    d307::UInt8
    d308::UInt8
    d309::UInt8
    d310::UInt8
    d311::UInt8
    d312::UInt8
    d313::UInt8
    d314::UInt8
    d315::UInt8
    d316::UInt8
    d317::UInt8
    d318::UInt8
    d319::UInt8
    d320::UInt8
    d321::UInt8
    d322::UInt8
    d323::UInt8
    d324::UInt8
    d325::UInt8
    d326::UInt8
    d327::UInt8
    d328::UInt8
    d329::UInt8
    d330::UInt8
    d331::UInt8
    d332::UInt8
    d333::UInt8
    d334::UInt8
    d335::UInt8
    d336::UInt8
    d337::UInt8
    d338::UInt8
    d339::UInt8
    d340::UInt8
    d341::UInt8
    d342::UInt8
    d343::UInt8
    d344::UInt8
    d345::UInt8
    d346::UInt8
    d347::UInt8
    d348::UInt8
    d349::UInt8
    d350::UInt8
    d351::UInt8
    d352::UInt8
    d353::UInt8
    d354::UInt8
    d355::UInt8
    d356::UInt8
    d357::UInt8
    d358::UInt8
    d359::UInt8
    d360::UInt8
    d361::UInt8
    d362::UInt8
    d363::UInt8
    d364::UInt8
    d365::UInt8
    d366::UInt8
    d367::UInt8
    d368::UInt8
    d369::UInt8
    d370::UInt8
    d371::UInt8
    d372::UInt8
    d373::UInt8
    d374::UInt8
    d375::UInt8
    d376::UInt8
    d377::UInt8
    d378::UInt8
    d379::UInt8
    d380::UInt8
    d381::UInt8
    d382::UInt8
    d383::UInt8
    d384::UInt8
    d385::UInt8
    d386::UInt8
    d387::UInt8
    d388::UInt8
    d389::UInt8
    d390::UInt8
    d391::UInt8
    d392::UInt8
    d393::UInt8
    d394::UInt8
    d395::UInt8
    d396::UInt8
    d397::UInt8
    d398::UInt8
    d399::UInt8
    d400::UInt8
    d401::UInt8
    d402::UInt8
    d403::UInt8
    d404::UInt8
    d405::UInt8
    d406::UInt8
    d407::UInt8
    d408::UInt8
    d409::UInt8
    d410::UInt8
    d411::UInt8
    d412::UInt8
    d413::UInt8
    d414::UInt8
    d415::UInt8
    d416::UInt8
    d417::UInt8
    d418::UInt8
    d419::UInt8
    d420::UInt8
    d421::UInt8
    d422::UInt8
    d423::UInt8
    d424::UInt8
    d425::UInt8
    d426::UInt8
    d427::UInt8
    d428::UInt8
    d429::UInt8
    d430::UInt8
    d431::UInt8
    d432::UInt8
    d433::UInt8
    d434::UInt8
    d435::UInt8
    d436::UInt8
    d437::UInt8
    d438::UInt8
    d439::UInt8
    d440::UInt8
    d441::UInt8
    d442::UInt8
    d443::UInt8
    d444::UInt8
    d445::UInt8
    d446::UInt8
    d447::UInt8
    d448::UInt8
    d449::UInt8
    d450::UInt8
    d451::UInt8
    d452::UInt8
    d453::UInt8
    d454::UInt8
    d455::UInt8
    d456::UInt8
    d457::UInt8
    d458::UInt8
    d459::UInt8
    d460::UInt8
    d461::UInt8
    d462::UInt8
    d463::UInt8
    d464::UInt8
    d465::UInt8
    d466::UInt8
    d467::UInt8
    d468::UInt8
    d469::UInt8
    d470::UInt8
    d471::UInt8
    d472::UInt8
    d473::UInt8
    d474::UInt8
    d475::UInt8
    d476::UInt8
    d477::UInt8
    d478::UInt8
    d479::UInt8
    d480::UInt8
    d481::UInt8
    d482::UInt8
    d483::UInt8
    d484::UInt8
    d485::UInt8
    d486::UInt8
    d487::UInt8
    d488::UInt8
    d489::UInt8
    d490::UInt8
    d491::UInt8
    d492::UInt8
    d493::UInt8
    d494::UInt8
    d495::UInt8
    d496::UInt8
    d497::UInt8
    d498::UInt8
    d499::UInt8
    d500::UInt8
    d501::UInt8
    d502::UInt8
    d503::UInt8
    d504::UInt8
    d505::UInt8
    d506::UInt8
    d507::UInt8
    d508::UInt8
    d509::UInt8
    d510::UInt8
    d511::UInt8
    d512::UInt8
    d513::UInt8
    d514::UInt8
    d515::UInt8
    d516::UInt8
    d517::UInt8
    d518::UInt8
    d519::UInt8
    d520::UInt8
    d521::UInt8
    d522::UInt8
    d523::UInt8
    d524::UInt8
    d525::UInt8
    d526::UInt8
    d527::UInt8
    d528::UInt8
    d529::UInt8
    d530::UInt8
    d531::UInt8
    d532::UInt8
    d533::UInt8
    d534::UInt8
    d535::UInt8
    d536::UInt8
    d537::UInt8
    d538::UInt8
    d539::UInt8
    d540::UInt8
    d541::UInt8
    d542::UInt8
    d543::UInt8
    d544::UInt8
    d545::UInt8
    d546::UInt8
    d547::UInt8
    d548::UInt8
    d549::UInt8
    d550::UInt8
    d551::UInt8
    d552::UInt8
    d553::UInt8
    d554::UInt8
    d555::UInt8
    d556::UInt8
    d557::UInt8
    d558::UInt8
    d559::UInt8
    d560::UInt8
    d561::UInt8
    d562::UInt8
    d563::UInt8
    d564::UInt8
    d565::UInt8
    d566::UInt8
    d567::UInt8
    d568::UInt8
    d569::UInt8
    d570::UInt8
    d571::UInt8
    d572::UInt8
    d573::UInt8
    d574::UInt8
    d575::UInt8
    d576::UInt8
    d577::UInt8
    d578::UInt8
    d579::UInt8
    d580::UInt8
    d581::UInt8
    d582::UInt8
    d583::UInt8
    d584::UInt8
    d585::UInt8
    d586::UInt8
    d587::UInt8
    d588::UInt8
    d589::UInt8
    d590::UInt8
    d591::UInt8
    d592::UInt8
    d593::UInt8
    d594::UInt8
    d595::UInt8
    d596::UInt8
    d597::UInt8
    d598::UInt8
    d599::UInt8
    d600::UInt8
    d601::UInt8
    d602::UInt8
    d603::UInt8
    d604::UInt8
    d605::UInt8
    d606::UInt8
    d607::UInt8
    d608::UInt8
    d609::UInt8
    d610::UInt8
    d611::UInt8
    d612::UInt8
    d613::UInt8
    d614::UInt8
    d615::UInt8
    d616::UInt8
    d617::UInt8
    d618::UInt8
    d619::UInt8
    d620::UInt8
    d621::UInt8
    d622::UInt8
    d623::UInt8
    d624::UInt8
    d625::UInt8
    d626::UInt8
    d627::UInt8
    d628::UInt8
    d629::UInt8
    d630::UInt8
    d631::UInt8
    d632::UInt8
    d633::UInt8
    d634::UInt8
    d635::UInt8
    d636::UInt8
    d637::UInt8
    d638::UInt8
    d639::UInt8
    d640::UInt8
    d641::UInt8
    d642::UInt8
    d643::UInt8
    d644::UInt8
    d645::UInt8
    d646::UInt8
    d647::UInt8
    d648::UInt8
    d649::UInt8
    d650::UInt8
    d651::UInt8
    d652::UInt8
    d653::UInt8
    d654::UInt8
    d655::UInt8
    d656::UInt8
    d657::UInt8
    d658::UInt8
    d659::UInt8
    d660::UInt8
    d661::UInt8
    d662::UInt8
    d663::UInt8
    d664::UInt8
    d665::UInt8
    d666::UInt8
    d667::UInt8
    d668::UInt8
    d669::UInt8
    d670::UInt8
    d671::UInt8
    d672::UInt8
    d673::UInt8
    d674::UInt8
    d675::UInt8
    d676::UInt8
    d677::UInt8
    d678::UInt8
    d679::UInt8
    d680::UInt8
    d681::UInt8
    d682::UInt8
    d683::UInt8
    d684::UInt8
    d685::UInt8
    d686::UInt8
    d687::UInt8
    d688::UInt8
    d689::UInt8
    d690::UInt8
    d691::UInt8
    d692::UInt8
    d693::UInt8
    d694::UInt8
    d695::UInt8
    d696::UInt8
    d697::UInt8
    d698::UInt8
    d699::UInt8
    d700::UInt8
    d701::UInt8
    d702::UInt8
    d703::UInt8
    d704::UInt8
    d705::UInt8
    d706::UInt8
    d707::UInt8
    d708::UInt8
    d709::UInt8
    d710::UInt8
    d711::UInt8
    d712::UInt8
    d713::UInt8
    d714::UInt8
    d715::UInt8
    d716::UInt8
    d717::UInt8
    d718::UInt8
    d719::UInt8
    d720::UInt8
    d721::UInt8
    d722::UInt8
    d723::UInt8
    d724::UInt8
    d725::UInt8
    d726::UInt8
    d727::UInt8
    d728::UInt8
    d729::UInt8
    d730::UInt8
    d731::UInt8
    d732::UInt8
    d733::UInt8
    d734::UInt8
    d735::UInt8
    d736::UInt8
    d737::UInt8
    d738::UInt8
    d739::UInt8
    d740::UInt8
    d741::UInt8
    d742::UInt8
    d743::UInt8
    d744::UInt8
    d745::UInt8
    d746::UInt8
    d747::UInt8
    d748::UInt8
    d749::UInt8
    d750::UInt8
    d751::UInt8
    d752::UInt8
    d753::UInt8
    d754::UInt8
    d755::UInt8
    d756::UInt8
    d757::UInt8
    d758::UInt8
    d759::UInt8
    d760::UInt8
    d761::UInt8
    d762::UInt8
    d763::UInt8
    d764::UInt8
    d765::UInt8
    d766::UInt8
    d767::UInt8
    d768::UInt8
    d769::UInt8
    d770::UInt8
    d771::UInt8
    d772::UInt8
    d773::UInt8
    d774::UInt8
    d775::UInt8
    d776::UInt8
    d777::UInt8
    d778::UInt8
    d779::UInt8
    d780::UInt8
    d781::UInt8
    d782::UInt8
    d783::UInt8
    d784::UInt8
    d785::UInt8
    d786::UInt8
    d787::UInt8
    d788::UInt8
    d789::UInt8
    d790::UInt8
    d791::UInt8
    d792::UInt8
    d793::UInt8
    d794::UInt8
    d795::UInt8
    d796::UInt8
    d797::UInt8
    d798::UInt8
    d799::UInt8
    d800::UInt8
    d801::UInt8
    d802::UInt8
    d803::UInt8
    d804::UInt8
    d805::UInt8
    d806::UInt8
    d807::UInt8
    d808::UInt8
    d809::UInt8
    d810::UInt8
    d811::UInt8
    d812::UInt8
    d813::UInt8
    d814::UInt8
    d815::UInt8
    d816::UInt8
    d817::UInt8
    d818::UInt8
    d819::UInt8
    d820::UInt8
    d821::UInt8
    d822::UInt8
    d823::UInt8
    d824::UInt8
    d825::UInt8
    d826::UInt8
    d827::UInt8
    d828::UInt8
    d829::UInt8
    d830::UInt8
    d831::UInt8
    d832::UInt8
    d833::UInt8
    d834::UInt8
    d835::UInt8
    d836::UInt8
    d837::UInt8
    d838::UInt8
    d839::UInt8
    d840::UInt8
    d841::UInt8
    d842::UInt8
    d843::UInt8
    d844::UInt8
    d845::UInt8
    d846::UInt8
    d847::UInt8
    d848::UInt8
    d849::UInt8
    d850::UInt8
    d851::UInt8
    d852::UInt8
    d853::UInt8
    d854::UInt8
    d855::UInt8
    d856::UInt8
    d857::UInt8
    d858::UInt8
    d859::UInt8
    d860::UInt8
    d861::UInt8
    d862::UInt8
    d863::UInt8
    d864::UInt8
    d865::UInt8
    d866::UInt8
    d867::UInt8
    d868::UInt8
    d869::UInt8
    d870::UInt8
    d871::UInt8
    d872::UInt8
    d873::UInt8
    d874::UInt8
    d875::UInt8
    d876::UInt8
    d877::UInt8
    d878::UInt8
    d879::UInt8
    d880::UInt8
    d881::UInt8
    d882::UInt8
    d883::UInt8
    d884::UInt8
    d885::UInt8
    d886::UInt8
    d887::UInt8
    d888::UInt8
    d889::UInt8
    d890::UInt8
    d891::UInt8
    d892::UInt8
    d893::UInt8
    d894::UInt8
    d895::UInt8
    d896::UInt8
    d897::UInt8
    d898::UInt8
    d899::UInt8
    d900::UInt8
    d901::UInt8
    d902::UInt8
    d903::UInt8
    d904::UInt8
    d905::UInt8
    d906::UInt8
    d907::UInt8
    d908::UInt8
    d909::UInt8
    d910::UInt8
    d911::UInt8
    d912::UInt8
    d913::UInt8
    d914::UInt8
    d915::UInt8
    d916::UInt8
    d917::UInt8
    d918::UInt8
    d919::UInt8
    d920::UInt8
    d921::UInt8
    d922::UInt8
    d923::UInt8
    d924::UInt8
    d925::UInt8
    d926::UInt8
    d927::UInt8
    d928::UInt8
    d929::UInt8
    d930::UInt8
    d931::UInt8
    d932::UInt8
    d933::UInt8
    d934::UInt8
    d935::UInt8
    d936::UInt8
    d937::UInt8
    d938::UInt8
    d939::UInt8
    d940::UInt8
    d941::UInt8
    d942::UInt8
    d943::UInt8
    d944::UInt8
    d945::UInt8
    d946::UInt8
    d947::UInt8
    d948::UInt8
    d949::UInt8
    d950::UInt8
    d951::UInt8
    d952::UInt8
    d953::UInt8
    d954::UInt8
    d955::UInt8
    d956::UInt8
    d957::UInt8
    d958::UInt8
    d959::UInt8
    d960::UInt8
    d961::UInt8
    d962::UInt8
    d963::UInt8
    d964::UInt8
    d965::UInt8
    d966::UInt8
    d967::UInt8
    d968::UInt8
    d969::UInt8
    d970::UInt8
    d971::UInt8
    d972::UInt8
    d973::UInt8
    d974::UInt8
    d975::UInt8
    d976::UInt8
    d977::UInt8
    d978::UInt8
    d979::UInt8
    d980::UInt8
    d981::UInt8
    d982::UInt8
    d983::UInt8
    d984::UInt8
    d985::UInt8
    d986::UInt8
    d987::UInt8
    d988::UInt8
    d989::UInt8
    d990::UInt8
    d991::UInt8
    d992::UInt8
    d993::UInt8
    d994::UInt8
    d995::UInt8
    d996::UInt8
    d997::UInt8
    d998::UInt8
    d999::UInt8
    d1000::UInt8
    d1001::UInt8
    d1002::UInt8
    d1003::UInt8
    d1004::UInt8
    d1005::UInt8
    d1006::UInt8
    d1007::UInt8
    d1008::UInt8
    d1009::UInt8
    d1010::UInt8
    d1011::UInt8
    d1012::UInt8
    d1013::UInt8
    d1014::UInt8
    d1015::UInt8
    d1016::UInt8
    d1017::UInt8
    d1018::UInt8
    d1019::UInt8
    d1020::UInt8
    d1021::UInt8
    d1022::UInt8
    d1023::UInt8
    d1024::UInt8
end

immutable Array_50_Ptr
    d1::Ptr{cdAttribute}
    d2::Ptr{cdAttribute}
    d3::Ptr{cdAttribute}
    d4::Ptr{cdAttribute}
    d5::Ptr{cdAttribute}
    d6::Ptr{cdAttribute}
    d7::Ptr{cdAttribute}
    d8::Ptr{cdAttribute}
    d9::Ptr{cdAttribute}
    d10::Ptr{cdAttribute}
    d11::Ptr{cdAttribute}
    d12::Ptr{cdAttribute}
    d13::Ptr{cdAttribute}
    d14::Ptr{cdAttribute}
    d15::Ptr{cdAttribute}
    d16::Ptr{cdAttribute}
    d17::Ptr{cdAttribute}
    d18::Ptr{cdAttribute}
    d19::Ptr{cdAttribute}
    d20::Ptr{cdAttribute}
    d21::Ptr{cdAttribute}
    d22::Ptr{cdAttribute}
    d23::Ptr{cdAttribute}
    d24::Ptr{cdAttribute}
    d25::Ptr{cdAttribute}
    d26::Ptr{cdAttribute}
    d27::Ptr{cdAttribute}
    d28::Ptr{cdAttribute}
    d29::Ptr{cdAttribute}
    d30::Ptr{cdAttribute}
    d31::Ptr{cdAttribute}
    d32::Ptr{cdAttribute}
    d33::Ptr{cdAttribute}
    d34::Ptr{cdAttribute}
    d35::Ptr{cdAttribute}
    d36::Ptr{cdAttribute}
    d37::Ptr{cdAttribute}
    d38::Ptr{cdAttribute}
    d39::Ptr{cdAttribute}
    d40::Ptr{cdAttribute}
    d41::Ptr{cdAttribute}
    d42::Ptr{cdAttribute}
    d43::Ptr{cdAttribute}
    d44::Ptr{cdAttribute}
    d45::Ptr{cdAttribute}
    d46::Ptr{cdAttribute}
    d47::Ptr{cdAttribute}
    d48::Ptr{cdAttribute}
    d49::Ptr{cdAttribute}
    d50::Ptr{cdAttribute}
end

type cdCanvas
    signature::Array_2_UInt8
    cxPixel::Ptr{Void}
    cxLine::Ptr{Void}
    cxPoly::Ptr{Void}
    cxRect::Ptr{Void}
    cxBox::Ptr{Void}
    cxArc::Ptr{Void}
    cxSector::Ptr{Void}
    cxChord::Ptr{Void}
    cxText::Ptr{Void}
    cxKillCanvas::Ptr{Void}
    cxFont::Ptr{Void}
    cxPutImageRectMap::Ptr{Void}
    cxPutImageRectRGB::Ptr{Void}
    cxGetFontDim::Ptr{Void}
    cxGetTextSize::Ptr{Void}
    cxFlush::Ptr{Void}
    cxClear::Ptr{Void}
    cxFLine::Ptr{Void}
    cxFPoly::Ptr{Void}
    cxFRect::Ptr{Void}
    cxFBox::Ptr{Void}
    cxFArc::Ptr{Void}
    cxFSector::Ptr{Void}
    cxFChord::Ptr{Void}
    cxFText::Ptr{Void}
    cxClip::Ptr{Void}
    cxClipArea::Ptr{Void}
    cxFClipArea::Ptr{Void}
    cxBackOpacity::Ptr{Void}
    cxWriteMode::Ptr{Void}
    cxLineStyle::Ptr{Void}
    cxLineWidth::Ptr{Void}
    cxLineJoin::Ptr{Void}
    cxLineCap::Ptr{Void}
    cxInteriorStyle::Ptr{Void}
    cxHatch::Ptr{Void}
    cxStipple::Ptr{Void}
    cxPattern::Ptr{Void}
    cxNativeFont::Ptr{Void}
    cxTextAlignment::Ptr{Void}
    cxTextOrientation::Ptr{Void}
    cxPalette::Ptr{Void}
    cxBackground::Ptr{Void}
    cxForeground::Ptr{Void}
    cxTransform::Ptr{Void}
    cxPutImageRectRGBA::Ptr{Void}
    cxGetImageRGB::Ptr{Void}
    cxScrollArea::Ptr{Void}
    cxCreateImage::Ptr{Void}
    cxKillImage::Ptr{Void}
    cxGetImage::Ptr{Void}
    cxPutImageRect::Ptr{Void}
    cxNewRegion::Ptr{Void}
    cxIsPointInRegion::Ptr{Void}
    cxOffsetRegion::Ptr{Void}
    cxGetRegionBox::Ptr{Void}
    cxActivate::Ptr{Void}
    cxDeactivate::Ptr{Void}
    w::Cint
    h::Cint
    w_mm::Cdouble
    h_mm::Cdouble
    xres::Cdouble
    yres::Cdouble
    bpp::Cint
    invert_yaxis::Cint
    matrix::Array_6_Cdouble
    use_matrix::Cint
    clip_mode::Cint
    clip_rect::cdRect
    clip_frect::cdfRect
    clip_poly_n::Cint
    clip_poly::Ptr{cdPoint}
    clip_fpoly::Ptr{cdfPoint}
    new_region::Cint
    combine_mode::Cint
    foreground::Clong
    background::Clong
    back_opacity::Cint
    write_mode::Cint
    mark_type::Cint
    mark_size::Cint
    line_style::Cint
    line_width::Cint
    line_cap::Cint
    line_join::Cint
    line_dashes::Ptr{Cint}
    line_dashes_count::Cint
    interior_style::Cint
    hatch_style::Cint
    fill_mode::Cint
    font_type_face::Array_1024_UInt8
    font_style::Cint
    font_size::Cint
    text_alignment::Cint
    text_orientation::Cdouble
    native_font::Array_1024_UInt8
    pattern_w::Cint
    pattern_h::Cint
    pattern_size::Cint
    pattern::Ptr{Clong}
    stipple_w::Cint
    stipple_h::Cint
    stipple_size::Cint
    stipple::Ptr{Cuchar}
    use_origin::Cint
    origin::cdPoint
    forigin::cdfPoint
    poly_mode::Cint
    poly_n::Cint
    poly_size::Cint
    fpoly_size::Cint
    poly::Ptr{cdPoint}
    fpoly::Ptr{cdfPoint}
    use_fpoly::Cint
    path_n::Cint
    path_size::Cint
    path::Ptr{Cint}
    path_arc_index::Cint
    sim_mode::Cint
    s::Cdouble
    sx::Cdouble
    tx::Cdouble
    sy::Cdouble
    ty::Cdouble
    window::cdfRect
    viewport::cdRect
    attrib_list::Array_50_Ptr
    attrib_n::Cint
    vector_font::Ptr{Void}
    simulation::Ptr{Void}
    ctxcanvas::Ptr{Void}
    context::Ptr{Cint}
end

# begin enum ANONYMOUS_1
const CD_BASE_WIN = 0
const CD_BASE_X = 1
const CD_BASE_GDK = 2
const CD_BASE_HAIKU = 3
# end enum ANONYMOUS_1
# begin enum ANONYMOUS_2
const CD_CTXPLUS_NATIVEWINDOW = 0
const CD_CTXPLUS_IMAGE = 1
const CD_CTXPLUS_DBUFFER = 2
const CD_CTXPLUS_PRINTER = 3
const CD_CTXPLUS_EMF = 4
const CD_CTXPLUS_CLIPBOARD = 5
# end enum ANONYMOUS_2
type cdBitmap
    w::Cint
    h::Cint
    _type::Cint
    data::Ptr{Void}
end

typealias cdCallback Ptr{Void}
# begin enum ANONYMOUS_3
const CD_RGB = 0
const CD_MAP = 1
const CD_RGBA = 256
# end enum ANONYMOUS_3
# begin enum ANONYMOUS_4
const CD_IRED = 0
const CD_IGREEN = 1
const CD_IBLUE = 2
const CD_IALPHA = 3
const CD_INDEX = 4
const CD_COLORS = 5
# end enum ANONYMOUS_4
# begin enum ANONYMOUS_5
const CD_ERROR = -1
const CD_OK = 0
# end enum ANONYMOUS_5
# begin enum ANONYMOUS_6
const CD_CLIPOFF = 0
const CD_CLIPAREA = 1
const CD_CLIPPOLYGON = 2
const CD_CLIPREGION = 3
# end enum ANONYMOUS_6
# begin enum ANONYMOUS_7
const CD_UNION = 0
const CD_INTERSECT = 1
const CD_DIFFERENCE = 2
const CD_NOTINTERSECT = 3
# end enum ANONYMOUS_7
# begin enum ANONYMOUS_8
const CD_FILL = 0
const CD_OPEN_LINES = 1
const CD_CLOSED_LINES = 2
const CD_CLIP = 3
const CD_BEZIER = 4
const CD_REGION = 5
const CD_PATH = 6
# end enum ANONYMOUS_8
# begin enum ANONYMOUS_9
const CD_PATH_NEW = 0
const CD_PATH_MOVETO = 1
const CD_PATH_LINETO = 2
const CD_PATH_ARC = 3
const CD_PATH_CURVETO = 4
const CD_PATH_CLOSE = 5
const CD_PATH_FILL = 6
const CD_PATH_STROKE = 7
const CD_PATH_FILLSTROKE = 8
const CD_PATH_CLIP = 9
# end enum ANONYMOUS_9
# begin enum ANONYMOUS_10
const CD_EVENODD = 0
const CD_WINDING = 1
# end enum ANONYMOUS_10
# begin enum ANONYMOUS_11
const CD_MITER = 0
const CD_BEVEL = 1
const CD_ROUND = 2
# end enum ANONYMOUS_11
# begin enum ANONYMOUS_12
const CD_CAPFLAT = 0
const CD_CAPSQUARE = 1
const CD_CAPROUND = 2
# end enum ANONYMOUS_12
# begin enum ANONYMOUS_13
const CD_OPAQUE = 0
const CD_TRANSPARENT = 1
# end enum ANONYMOUS_13
# begin enum ANONYMOUS_14
const CD_REPLACE = 0
const CD_XOR = 1
const CD_NOT_XOR = 2
# end enum ANONYMOUS_14
# begin enum ANONYMOUS_15
const CD_POLITE = 0
const CD_FORCE = 1
# end enum ANONYMOUS_15
# begin enum ANONYMOUS_16
const CD_CONTINUOUS = 0
const CD_DASHED = 1
const CD_DOTTED = 2
const CD_DASH_DOT = 3
const CD_DASH_DOT_DOT = 4
const CD_CUSTOM = 5
# end enum ANONYMOUS_16
# begin enum ANONYMOUS_17
const CD_PLUS = 0
const CD_STAR = 1
const CD_CIRCLE = 2
const CD_X = 3
const CD_BOX = 4
const CD_DIAMOND = 5
const CD_HOLLOW_CIRCLE = 6
const CD_HOLLOW_BOX = 7
const CD_HOLLOW_DIAMOND = 8
# end enum ANONYMOUS_17
# begin enum ANONYMOUS_18
const CD_HORIZONTAL = 0
const CD_VERTICAL = 1
const CD_FDIAGONAL = 2
const CD_BDIAGONAL = 3
const CD_CROSS = 4
const CD_DIAGCROSS = 5
# end enum ANONYMOUS_18
# begin enum ANONYMOUS_19
const CD_SOLID = 0
const CD_HATCH = 1
const CD_STIPPLE = 2
const CD_PATTERN = 3
const CD_HOLLOW = 4
# end enum ANONYMOUS_19
# begin enum ANONYMOUS_20
const CD_NORTH = 0
const CD_SOUTH = 1
const CD_EAST = 2
const CD_WEST = 3
const CD_NORTH_EAST = 4
const CD_NORTH_WEST = 5
const CD_SOUTH_EAST = 6
const CD_SOUTH_WEST = 7
const CD_CENTER = 8
const CD_BASE_LEFT = 9
const CD_BASE_CENTER = 10
const CD_BASE_RIGHT = 11
# end enum ANONYMOUS_20
# begin enum ANONYMOUS_21
const CD_PLAIN = 0
const CD_BOLD = 1
const CD_ITALIC = 2
const CD_UNDERLINE = 4
const CD_STRIKEOUT = 8
# end enum ANONYMOUS_21
# begin enum ANONYMOUS_22
const CD_SMALL = 8
const CD_STANDARD = 12
const CD_LARGE = 18
# end enum ANONYMOUS_22
# begin enum ANONYMOUS_23
const CD_CTX_WINDOW = 0
const CD_CTX_DEVICE = 1
const CD_CTX_IMAGE = 2
const CD_CTX_FILE = 3
# end enum ANONYMOUS_23
typealias cdSizeCB Ptr{Void}
# begin enum ANONYMOUS_24
const CD_A0 = 0
const CD_A1 = 1
const CD_A2 = 2
const CD_A3 = 3
const CD_A4 = 4
const CD_A5 = 5
const CD_LETTER = 6
const CD_LEGAL = 7
# end enum ANONYMOUS_24
# begin enum ANONYMOUS_25
const CD_SYSTEM = 0
const CD_COURIER = 1
const CD_TIMES_ROMAN = 2
const CD_HELVETICA = 3
const CD_NATIVE = 4
# end enum ANONYMOUS_25

const CD_CLIPON = CD_CLIPAREA
const CD_CENTER_BASE = CD_BASE_CENTER
const CD_LEFT_BASE = CD_BASE_LEFT
const CD_RIGHT_BASE = CD_BASE_RIGHT
const CD_BOLD_ITALIC = (CD_BOLD|CD_ITALIC)
const CD_ITALIC_BOLD = CD_BOLD_ITALIC

# This one is in cd_old.h so probably a left-over
#const cdScrollImage = cdScrollArea


text:
"""
```
Model(y, H, A, Q, R, x, P)
```

The type Model contains the measurements matrix as well as the kalman setup:
Measurement:  y(t)     = H * x(t) + e(t) ~ N(0, R)
State:        x(t + 1) = A * x(t) + n(t) ~ N(0, Q)

where both y(t) and x(t+1) are vectors of length p and m, respectively.
The state transition matrix, A, is (m x m) while the measurement model matrix, H, is (p x m).

Time-varying H and A are not currently implemented. Similarly, the covariances
R and Q are constant across time.
"""

mutable struct Model
    y::Matrix{<:Real}
    H::Matrix{<:Real}
    A::Matrix{<:Real}
    Q::Matrix{<:Real}
    R::Matrix{<:Real}
    x::Vector{<:Real}
    P::Matrix{<:Real}
    predicted::Predicted
    filtered::Filtered
    smoothed::Smoothed
    function Model(y, H, A, Q, R, x, P)
        str = "y is $(size(y, 1))x$(size(y, 2)), " *
              "H is $(size(H, 1))x$(size(H, 2)), " *
              "A is $(size(A, 1))x$(size(A, 2)), " *
              "Q is $(size(Q, 1))x$(size(Q, 2)), " *
              "R is $(size(R, 1))x$(size(R, 2)), " *
              "x is $(length(x)), " *
              "P is $(size(P, 1))x$(size(P, 2))"
        !(size(H, 2) == size(A, 1) == size(A, 2) == size(Q, 1) == size(Q, 2) == length(x) == size(P, 1) == size(P, 2)) && throw(DimensionMismatch(str))
        !(size(y, 2) == size(H, 1) == size(R, 1) == size(R, 2)) && throw(DimensionMismatch(str))

        @assert all(diag(R) .> 0) "All diagonal elements of R must be strictly positive."
        @assert all(diag(Q) .> 0) "All diagonal elements of Q must be strictly positive."
        @assert all(diag(P) .> 0) "All diagonal elements of P must be strictly positive."

        predicted = Predicted(size(y, 1), size(y, 2), length(x))
        filtered  = Filtered( size(y, 1), size(y, 2), length(x))
        smoothed  = Smoothed( size(y, 1), size(y, 2), length(x))

        return new(y, H, A, Q, R, x, P, predicted, filtered, smoothed)
    end
end

# Outer constructors for univariate measurement
Model(y::Vector{<:Real}, H::Vector{<:Real}, A::Matrix{<:Real}, Q::Matrix{<:Real}, R::Real, x::Vector{<:Real}, P::Matrix{<:Real}) = Model(repeat(y, 1, 1), reshape(H, 1, length(H)), A, Q, fill(R, 1, 1), x, P)
Model(y::Vector{<:Real}, H::Real, A::Real, Q::Real, R::Real, x::Real, P::Real) = Model(repeat(y, 1, 1), fill(H, 1, 1), fill(A, 1, 1), fill(Q, 1, 1), fill(R, 1, 1), [x], fill(P, 1, 1))

# Univariate state
Model(y::Matrix{<:Real}, H::Matrix{<:Real}, A::Real, Q::Real, R::Matrix{<:Real}, x::Real, P::Real) = Model(y, H, fill(A, 1, 1), fill(Q, 1, 1), R, [x], fill(P, 1, 1))

# Special constructors
LocalLevel(y::Vector{<:Real}, Q::Real, R::Real, x::Real, P::Real) = Model(repeat(y, 1, 1), ones(1, 1), ones(1, 1), fill(Q, 1, 1), fill(R, 1, 1), [x], fill(P, 1, 1))
LocalLevel(y::Matrix{<:Real}, Q::Real, R::Matrix, x::Real, P::Real) = Model(y, ones(size(y, 2), 1), ones(1, 1), fill(Q, 1, 1), R, [x], fill(P, 1, 1))

LocalLevel(y::Vector{<:Real}, Q::Real, R::Real) = Model(repeat(y, 1, 1), ones(1, 1), ones(1, 1), fill(Q, 1, 1), fill(R, 1, 1), [y[1]], fill(1000.0, 1, 1))
LocalLevel(y::Matrix{<:Real}, Q::Real, R::Matrix) = Model(repeat(y, 1, 1), ones(1, 1), ones(1, 1), fill(Q, 1, 1), R, [0.0], fill(1000.0, 1, 1))

"""
```
Output
```

The type Output contains the means and covariances for the measurements
and states, respectively, at each time step, t = 1,...,T:
x is a (T x m) matrix of state means
P is a (T x m x m) array of state covariances
μ is a (T x p) matrix of measurement means
Σ is a (T x p x p) matrix of measurement covariances
"""

abstract type Output end

mutable struct Predicted <: Output
    x::Matrix{AbstractFloat}
    P::Array{AbstractFloat, 3}
    μ::Matrix{AbstractFloat}
    Σ::Array{AbstractFloat, 3}
end

mutable struct Filtered <: Output
    x::Matrix{AbstractFloat}
    P::Array{AbstractFloat, 3}
    μ::Matrix{AbstractFloat}
    Σ::Array{AbstractFloat, 3}
end

mutable struct Smoothed <: Output
    x::Matrix{AbstractFloat}
    P::Array{AbstractFloat, 3}
    μ::Matrix{AbstractFloat}
    Σ::Array{AbstractFloat, 3}
end

Predicted(T::Int, p::Int, m::Int) = Predicted(zeros(T, m), zeros(T, m, m), zeros(T, p), zeros(T, p, p))
Filtered(T::Int, p::Int, m::Int)  = Filtered(zeros(T, m), zeros(T, m, m), zeros(T, p), zeros(T, p, p))
Smoothed(T::Int, p::Int, m::Int)  = Smoothed(zeros(T, m), zeros(T, m, m), zeros(T, p), zeros(T, p, p))


text:
"""
    TypeX()

Lorem ipsum dolor sit amet.
"""
struct TypeX

end


text:
module Diana
abstract type Rule end

struct GraphQLError <:Exception
    msg::String
end

tabla_simbolos = Dict()

include("client.jl")
include("token.jl")
include("lexer.jl")
include("Parser.jl")
include("Validate.jl")
include("Schema.jl")
include("execute.jl")

export Queryclient, GraphQLClient,Tokensgraphql,Parse,Schema,Validatequery

end # module


text:
"""
    mutable struct FreeVariables <: MOI.ModelLike
        n::Int64
        FreeVariables() = new(0)
    end

A struct for storing free variables that can be used as the `variables` field
of [`GenericModel`](@ref) or [`GenericModel`](@ref). It represents a model
that does not support any constraint nor objective function.

## Example

The following model type represents a conic model in geometric form. As opposed
to [`VariablesContainer`](@ref), `FreeVariables` does not support constraint
bounds so they are bridged into an affine constraint in the
[`MathOptInterface.Nonnegatives`](@ref) cone as expected for the geometric
conic form.
```jldocstest
julia> MOI.Utilities.@product_of_sets(
    Cones,
    MOI.Zeros,
    MOI.Nonnegatives,
    MOI.SecondOrderCone,
    MOI.PositiveSemidefiniteConeTriangle,
);

julia> const ConicModel{T} = MOI.Utilities.GenericOptimizer{
    T,
    MOI.Utilities.ObjectiveContainer{T},
    MOI.Utilities.FreeVariables,
    MOI.Utilities.MatrixOfConstraints{
        T,
        MOI.Utilities.MutableSparseMatrixCSC{
            T,
            Int,
            MOI.Utilities.OneBasedIndexing,
        },
        Vector{T},
        Cones{T},
    },
};

julia> model = MOI.instantiate(ConicModel{Float64}, with_bridge_type=Float64);

julia> x = MOI.add_variable(model)
MathOptInterface.VariableIndex(1)

julia> c = MOI.add_constraint(model, x, MOI.GreaterThan(1.0))
MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}(1)

julia> MOI.Bridges.is_bridged(model, c)
true

julia> bridge = MOI.Bridges.bridge(model, c)
MathOptInterface.Bridges.Constraint.VectorizeBridge{Float64, MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Nonnegatives, MathOptInterface.VariableIndex}(MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Nonnegatives}(1), 1.0)

julia> bridge.vector_constraint
MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Nonnegatives}(1)

julia> MOI.Bridges.is_bridged(model, bridge.vector_constraint)
false
```
"""
mutable struct FreeVariables <: MOI.ModelLike
    n::Int64
    FreeVariables() = new(0)
end

function MOI.empty!(model::FreeVariables)
    model.n = 0
    return
end

MOI.is_empty(model::FreeVariables) = iszero(model.n)

function MOI.add_variable(model::FreeVariables)
    model.n += 1
    return MOI.VariableIndex(model.n)
end

function MOI.get(model::FreeVariables, ::MOI.ListOfVariableIndices)
    return MOI.VariableIndex[MOI.VariableIndex(i) for i in 1:model.n]
end

function MOI.is_valid(model::FreeVariables, vi::MOI.VariableIndex)
    return 1 <= vi.value <= model.n
end

MOI.get(model::FreeVariables, ::MOI.NumberOfVariables) = model.n

function MOI.get(model::FreeVariables, ::MOI.ListOfConstraintTypesPresent)
    return Tuple{Type,Type}[]
end


text:
#!/usr/bin/env julia

# This file is used to run the experiments on the deterministic version of
# the covariance estimation problem.
include("./optimizers_abstract.jl")
using Distributions: MvNormal, mean
using Base. Iterators
using LinearAlgebra
using Plots
using Random
using JLD

for (d,NUM_GRADS) in [(20,100000),
                      (40,100000),
                      (60, 100000)
                      ]
  # Global parameters:
  ε = 1e-6
  γ = 1.0
  ηList = [0.005, 0.025, 0.1, 0.4]

  cdict = Dict(ηList[1] => :red,
               ηList[2] => :green,
               ηList[3] => :blue,
               ηList[4] => :brown)

  # Creating the problem:
  U = randn(d,d)
  Σ = U * U'

  # Objective functions of the two players
  f(W,V) = - sum(reshape(W,d,d) .* Σ) +
    sum(reshape(W,d,d) .* (reshape(V,d,d) * reshape(V,d,d)'))
  g(W,V) = + sum(reshape(W,d,d) .* Σ) -
    sum(reshape(W,d,d) .* (reshape(V,d,d) * reshape(V,d,d)'))

  # Get the derivatives
  ∇x_f, ∇y_f, ∇x∇x_f, ∇x∇y_f, ∇y∇y_f, ∇y∇x_f = get_closures(f)
  ∇x_g, ∇y_g, ∇x∇x_g, ∇x∇y_g, ∇y∇y_g, ∇y∇x_g = get_closures(g)

  # Function that returns the residual
  resi(W,V) = sqrt(norm(reshape(V,d,d) * reshape(V,d,d)' - Σ)^2 +
                   norm((reshape(W,d,d) + reshape(W,d,d)')/2)^2)

  # Creating the initial values as small perturbation of an equilibrium point
  W0 = rand(d,d) .- 0.5
  W0 = vec(W0 * W0')
  V0 = vec(rand(d,d) .- 0.5)
  W0 .= vec(zero(W0) + 1.0 * W0)
  V0 .= vec(U) + 1.0 * V0

  # Create the iteration variables
  V = similar(V0)
  ΔV = similar(V0)
  W = similar(W0)
  ΔW = similar(W0)

  # Start of the experiments:
  # Start an experiment:

  errsCGDA = []
  gradsCGDA = []
  sgradsCGDA = []

  for η in ηList
    newErrs = zeros(NUM_GRADS + 1)
    newGrads = zeros(Int, NUM_GRADS + 1)

    V .= V0
    W .= W0
    ΔW .= 0.0
    ΔV .= 0.0
    newErrs[1] = resi(W, V)
    cumGrad = 0
    for k = 1 : NUM_GRADS
      if iseven(k)
        newGrads[k+1] = CG_CGDA_x!(W, V, ΔW, ΔV,
                                    ∇x_f, ∇x∇y_f,
                                    ∇y_g, ∇y∇x_g, η, ε)
      else
        newGrads[k+1] = CG_CGDA_y!(W, V, ΔW, ΔV,
                                    ∇x_f, ∇x∇y_f,
                                    ∇y_g, ∇y∇x_g, η, ε)
      end
      cumGrad += newGrads[k+1]
      if cumGrad > NUM_GRADS
        resize!(newGrads, k)
        resize!(newErrs, k)
        break
      end
      newErrs[k + 1] = resi(W, V)
    end

    push!(errsCGDA, newErrs)
    push!(gradsCGDA, newGrads)
    push!(sgradsCGDA, cumsum(newGrads))
  end

  #Start an experiment:

  errsOGDA = []
  gradsOGDA = []
  sgradsOGDA = []

  for η in ηList
    newErrs = zeros(NUM_GRADS + 1)
    newGrads = zeros(Int, NUM_GRADS + 1)

    V .= V0
    W .= W0
    ΔW .= 0.0
    ΔV .= 0.0
    newErrs[1] = resi(W, V)
    cumGrad = 0
    for k = 1 : NUM_GRADS
      newGrads[k+1] = OGDA!(W, V, ΔW, ΔV, ∇x_f, ∇y_g, η)
      newErrs[k + 1] = resi(W, V)
      cumGrad += newGrads[k+1]
      if cumGrad > NUM_GRADS
        resize!(newGrads, k)
        resize!(newErrs, k)
        break
      end

    end

    push!(errsOGDA, newErrs)
    push!(gradsOGDA, newGrads)
    push!(sgradsOGDA, cumsum(newGrads))
  end

  #Start an experiment:
  errsSGA = []
  gradsSGA = []
  sgradsSGA = []

  for η in ηList
    newErrs = zeros(NUM_GRADS + 1)
    newGrads = zeros(Int, NUM_GRADS + 1)

    V .= V0
    W .= W0
    ΔW .= 0.0
    ΔV .= 0.0
    newErrs[1] = resi(W, V)
    cumGrad = 0
    for k = 1 : NUM_GRADS
      newGrads[k+1] = SGA!(W, V, ∇x_f, ∇y_g, ∇x∇y_f, ∇y∇x_g, η, γ)
      newErrs[k + 1] = resi(W, V)
      cumGrad += newGrads[k+1]
      if cumGrad > NUM_GRADS
        resize!(newGrads, k)
        resize!(newErrs, k)
        break
      end

    end

    push!(errsSGA, newErrs)
    push!(gradsSGA, newGrads)
    push!(sgradsSGA, cumsum(newGrads))
  end

  #Start an experiment:
  errsConOpt = []
  gradsConOpt = []
  sgradsConOpt = []

  for η in ηList
    newErrs = zeros(NUM_GRADS + 1)
    newGrads = zeros(Int, NUM_GRADS + 1)

    V .= V0
    W .= W0
    ΔW .= 0.0
    ΔV .= 0.0
    newErrs[1] = resi(W, V)
    cumGrad = 0
    for k = 1 : NUM_GRADS
      newGrads[k+1] = conOpt!(W, V, ∇x_f, ∇y_g,
                                  ∇x∇x_f, ∇x∇y_f,
                                  ∇y∇y_g, ∇y∇x_g, η, γ)
      newErrs[k + 1] = resi(W, V)
      cumGrad += newGrads[k+1]
      if cumGrad > NUM_GRADS
        resize!(newGrads, k)
        resize!(newErrs, k)
        break
      end

    end

    push!(errsConOpt, newErrs)
    push!(gradsConOpt, newGrads)
    push!(sgradsConOpt, cumsum(newGrads))
  end

  #Plotting the results:
  cvgPlot = plot(xlim=(0:NUM_GRADS),
                 xlabel="Number of forward passes",
                 ylabel="\$\\log_{10}(\\|V V^{T} - \\Sigma\\|_{Fro}^2 + \\|W_{SYM}\\|_{Fro}^2)/2\$",
                 legend=:bottomleft)

  for (sgrads, errs, η) in zip(sgradsCGDA, errsCGDA, ηList)
    mxg = findlast(x -> ~isnan(x), errs)
    if findfirst(isnan, errs) == nothing
      plot!(cvgPlot, sgrads[1:mxg], log10.(errs[1:mxg]),
            linestyle=:solid, linecolor=cdict[η],
            label="CGD, \\eta = $η")
    end
  end

  for (sgrads, errs, η) in zip(sgradsOGDA, errsOGDA, ηList)
    mxg = findlast(x -> ~isnan(x), errs)
    if findfirst(isnan, errs) == nothing
      plot!(cvgPlot, sgrads[1:mxg], log10.(errs[1:mxg]),
            linestyle=:dot, linecolor=cdict[η],
            label="OGDA, \\eta = $η")
    end
  end

  for (sgrads, errs, η) in zip(sgradsSGA, errsSGA, ηList)
    mxg = findlast(x -> ~isnan(x), errs)
    if findfirst(isnan, errs) == nothing
      plot!(cvgPlot, sgrads[1:mxg], log10.(errs[1:mxg]),
            linestyle=:dash, linecolor=cdict[η],
            label="SGA, \\eta = $η")
    end
  end

  for (sgrads, errs, η) in zip(sgradsConOpt, errsConOpt, ηList)
    mxg = findlast(x -> ~isnan(x), errs)
    if findfirst(isnan, errs) == nothing
      plot!(cvgPlot, sgrads[1:mxg], log10.(errs[1:mxg]),
            linestyle=:dashdot, linecolor=cdict[η],
            label="ConOpt, \\eta = $η")
    end
  end

  savefig(cvgPlot, "./out/exp2/cvest_d_$(d)_maxIter_$NUM_GRADS")
  @save "./out/exp2/cvest_d_$(d)_maxIter_$NUM_GRADS.jld" sgradsCGDA sgradsConOpt sgradsOGDA sgradsSGA gradsCGDA gradsConOpt gradsOGDA gradsSGA errsCGDA errsConOpt errsOGDA errsSGA ηList cdict
end


text:
# wrappers of low-level functionality

function curandGetProperty(property::CUDAapi.libraryPropertyType)
  value_ref = Ref{Cint}()
  curandGetProperty(property, value_ref)
  value_ref[]
end

version() = VersionNumber(curandGetProperty(CUDAapi.MAJOR_VERSION),
                          curandGetProperty(CUDAapi.MINOR_VERSION),
                          curandGetProperty(CUDAapi.PATCH_LEVEL))


text:
function add_epsilon!(x::ListOfPoints{d}, i, epsilon) where d
  ei = SVector{d,Float64}([(j==i ? epsilon : 0.0) for j=1:d])
  for i=1:length(x)
    x[i] += ei
  end
end

function DiffFun(fun, x0::Vector{ListOfPoints{n_x}}, epsilon=1e-6) where n_x
    xi = deepcopy(x0)
    N = length(x0[1])
    n_m = length(x0)
    r0 = fun(x0)::Vector{ListOfPoints{n_x}}
    JMat = [zeros(n_x, n_x, N) for i=1:n_m]
    for i_x=1:n_x
      for i_m=1:n_m
        add_epsilon!(xi[i_m], i_x, epsilon)
      end
      fi = fun(xi)::Vector{ListOfPoints{n_x}}
      di = (fi-r0)/epsilon
      for i_m=1:n_m
        JMat[i_m][:,i_x,:] = reshape(reinterpret(Float64, vec(di[i_m])), (n_x, N))
        add_epsilon!(xi[i_m], i_x, -epsilon)
      end
    end
    J = [reshape(reinterpret(SMatrix{n_x,n_x,Float64,n_x^2},vec(JMat[i])),(N,)) for i=1:n_m]
    return (r0,J) #::Tuple{Vector{ListOfPoints{n_x}},Vector{Vector{SMatrix{n_x,n_x,Float64,n_x*n_x}}}}
end

struct NewtonResult
    solution
    iterations::Int64
    maxit::Int64
    err_ϵ::Float64
    tol_ϵ::Float64
    err_η::Float64
    tol_η::Float64
    errors::Vector{Float64}
end

function newton(fun::Function, x0::Vector{ListOfPoints{n_x}}, a::Union{Vector{ListOfPoints{n_x}},Nothing}=nothing, b::Union{Vector{ListOfPoints{n_x}},Nothing}=nothing; maxit=10, verbose=false, n_bsteps=5, lam_bsteps=0.5) where n_x

    steps = (lam_bsteps).^collect(0:n_bsteps)

    n_m = length(x0)
    N = length(x0[1])
    x = x0::Vector{ListOfPoints{n_x}}
    err_e_0 = 10.0
    tol_e = 1e-8
    err_η = 10.0
    tol_η = -1.0
    it=0
    errors = Float64[]

    while (it<maxit) && (err_e_0>tol_e)
        it += 1
        R_i, D_i = DiffFun(fun, x) # ::Tuple{Vector{ListOfPoints{n_x}},Vector{Vector{SMatrix{n_x,n_x,Float64,n_x*n_x}}}}
        if !(a isa Nothing)
            PhiPhi!(R_i,x,a,b,D_i)
        end
        err_e_0 = maxabs(R_i)
        push!(errors, err_e_0)
        err_e_1 = err_e_0
        dx = [[D_i[i][n]\R_i[i][n] for n=1:N] for i=1:n_m]::Vector{ListOfPoints{n_x}}
        err_η = maxabs(dx)
        i_bckstps = 0
        new_x = x
        while err_e_1>=err_e_0 && i_bckstps<length(steps)
            i_bckstps += 1
            new_x = x-dx*steps[i_bckstps]
            new_res = fun(new_x)::Vector{ListOfPoints{n_x}} # no diff
            if !(a isa Nothing)
                new_res = [PhiPhi0.(new_res[i],new_x[i],a[i],b[i]) for i=1:n_m]
            end
            err_e_1 = maxabs(new_res)
        end
        x = new_x::Vector{ListOfPoints{n_x}}
        if verbose
            println(i_bckstps, " : ", err_x, " : ", err_e_1, )
        end
    end

    res = NewtonResult(
        x,
        it,
        maxit,
        err_e_0,
        tol_e,
        err_η,
        tol_η,
        errors
    )
    return res
end


text:
@testset "Filtration" begin
    # create empty filtration
    flt = Filtration(SimplicialComplex)

    # fill it with (cell, 'filtration value') pairs
    push!(flt, Simplex(1), 1.0)
    @test size(complex(flt)) == (1,)
    push!(flt, Simplex(2), 2.0)
    @test size(complex(flt)) == (2,)
    push!(flt, Simplex(1,2), 3.0, recursive=true)
    @test size(complex(flt)) == (2,1)
    push!(flt, Simplex(1,3), 4.0, recursive=true)
    @test size(complex(flt)) == (3,2)

    # test iterator
    dimhash(s) = (dim(s), hash(s))
    @testset "Iterator" for ((f1,ss1),(f2,ss2)) in zip(flt,
            enumerate([
                [dimhash(Simplex(1))],
                [dimhash(Simplex(2))],
                [dimhash(Simplex(1,2))],
                [dimhash(Simplex(3)), dimhash(Simplex(1,3))]
            ]))
        @test f1 == f2
        for (s1, s2) in zip(ss1, ss2)
            @test s1 == s2
        end
    end

    # test io
    let io = IOBuffer()
        write(io, flt)
        @test String(take!(copy(io))) == "1,1.0\n2,2.0\n1,2,3.0\n3,4.0\n1,3,4.0\n"
        seekstart(io)
        tmp = read(io, Filtration{SimplicialComplex, Float64}, Simplex{0,Int})
        @testset for ((f1,ss1),(f2,ss2)) in zip(flt, tmp)
            @test f1 == f2
            for (s1, s2) in zip(ss1, ss2)
                @test s1 == s2
            end
        end
    end

    # compute boundary matrix
    ∂ = boundary(flt)
    @test length(∂) == sum(size(complex(flt)))
    @test count(!iszero, sparse(∂)) == 4

    # create filtration from an existed complex
    cplx = SimplicialComplex()
    push!(cplx, Simplex('a','b','c'), recursive=true)
    flt = filtration(cplx)
    @test order(flt)[end] == (2, hash(Simplex('a','b','c')), 7)

    # compute boundary matrix
    ∂ = boundary(flt)
    @test length(∂) == 7
    @test count(!iszero, sparse(∂)) == 9

    Random.seed!(9236493643764);
    N = 10
    X = rand(3,N)
    cplx, w = vietorisrips(X, 0.4)
    flt = filtration(cplx, w)
    ∂ = boundary(flt)
    @test length(∂) == sum(size(cplx))
    @test count(!iszero, sparse(∂)) == sum(size(cplx))

    @test length(flt) == 9
    @test length(complex(flt)) == 19
    let (fv, ss) = first(flt)
        @test fv == 0.0
        @test length(ss) == N
    end
    @test sum(length(last(v)) for v in flt) == 19
    @testset for (l1, l2) in zip((length(last(v)) for v in flt), [10, 1, 1, 1, 1, 1, 1, 2, 1])
        @test l1 == l2
    end

    @test isinf(flt.divisions)
    @test minimum(flt) == 0.0
    @test maximum(flt) == mapreduce(e->e[3], max, order(flt))
    flt.divisions = 3
    @test sum(length(last(v)) for v in flt) == 19
    @testset for (l1, l2) in zip((length(last(v)) for v in flt), [11, 2, 6])
        @test l1 == l2
    end
end


text:
module WeightNormalizedRBMs
    import Flux
    import ValueHistories
    import RestrictedBoltzmannMachines as RBMs
    using RestrictedBoltzmannMachines: RBM, AbstractLayer

    include("rbm.jl")
    include("wnorm.jl")
    include("train/cd.jl")
    include("train/pcd.jl")
end


text:
#----------------------------------------------------------------
function mergeTimeSeries(t1, t2, a, b)
#----------------------------------------------------------------
    R = pairwise(Cityblock(), t1, t2)

    limit = 0.5

    time_stamps = Array{Float64}(undef, 0)
    value_a     = Array{Float64}(undef, 0)
    value_b     = Array{Float64}(undef, 0)

    for row_index in 1:size(R,1)

        minval, mincol = findmin(R[row_index, :])

        if minval <= limit
            nothing
        else
            continue
        end

        minval_in_column = minimum(R[:, mincol])
        if minval <= minval_in_column
            # we have a match
            push!(time_stamps, t1[row_index])
            push!(value_a,      a[row_index])
            push!(value_b,      b[mincol])

        else
            nothing
        end

    end

    @assert(length(time_stamps) == length(value_a) == length(value_b))

    return time_stamps, value_a, value_b

end


text:
################################################################################
#                          TEST: Euler Angle and Axis
################################################################################

# Test constructions.
for k = 1:samples
    # Sample a random vector and angle.
    a = rand()
    v = rand(3)

    angleaxis = EulerAngleAxis(a,v)

    # Check if the representation was created correctly.
    @test angleaxis.a == a
    @test angleaxis.v == v

    # Test multiplication.
    a1  = -π + 2*π*rand()
    v1  = randn(3)
    v1 /= norm(v1)

    a2  = -π + 2*π*rand()
    v2  = randn(3)
    v2 /= norm(v2)

    ea1 = EulerAngleAxis(a1, v1)
    ea2 = EulerAngleAxis(a2, v2)
    ear = ea2*ea1

    q1  = angleaxis_to_quat(ea1)
    q2  = angleaxis_to_quat(ea2)
    qr  = q1*q2
    eaq = quat_to_angleaxis(qr)

    @test    ear.a ≈ eaq.a
    @test ear.v[1] ≈ eaq.v[1]
    @test ear.v[2] ≈ eaq.v[2]
    @test ear.v[3] ≈ eaq.v[3]

    # Test inversion.
    a   = -π + 2*π*rand()
    v   = randn(3)
    v  /= norm(v)
    ea  = EulerAngleAxis(a,v)
    ear = ea*inv_rotation(ea)

    @test norm(ear.v) ≈ 0 atol = 1e-7
    @test       ear.a ≈ 0 atol = 1e-7
end

# Test types.
@test typeof(EulerAngleAxis(1, [1;1;1])) <: EulerAngleAxis{Int}
@test typeof(EulerAngleAxis(1.f0, Float32[1;1;1])) <: EulerAngleAxis{Float32}
@test typeof(EulerAngleAxis(1.f0, [1;1;1])) <: EulerAngleAxis{Float32}

# Test multiplication that leads to unitary rotation.
let
    ea1 = EulerAngleAxis(pi, [1.0,0.0,0.0])
    ear = ea1*ea1

    @test norm(ear.v) ≈ 0
    @test       ear.a ≈ 0
end

# Test `Float16` and `Float32` multiplications.
let
    v   = SVector{3,Float32}(1,0,0)
    a   = Float32(1)
    ea1 = EulerAngleAxis(a,v)
    ear = ea1*ea1

    @test eltype(ear.a) == eltype(ear.v) == Float32

    v   = SVector{3,Float16}(1,0,0)
    a   = Float16(1)
    ea1 = EulerAngleAxis(a,v)
    ear = ea1*ea1

    @test eltype(ear.a) == eltype(ear.v) == Float16
end

# Test printing
# =============

# Color test is not working on AppVeyor. Hence, if the OS is windows, then we
# just skip.
if !Sys.iswindows()
    # With colors.
    expected = """
EulerAngleAxis{Float64}:
\e[33;1m  Euler angle: \e[0m  0.7854 rad ( 45.0000 deg)
\e[33;1m   Euler axis: \e[0m[  0.5774,   0.5774,   0.5774]"""

    result = sprint((io,ea)->show(io, MIME"text/plain"(), ea),
                    EulerAngleAxis(π/4, [sqrt(3)/3, sqrt(3)/3, sqrt(3)/3]),
                    context = :color => true)
    @test result == expected
end

# Without colors.
expected = """
EulerAngleAxis{Float64}:
  Euler angle:   0.7854 rad ( 45.0000 deg)
   Euler axis: [  0.5774,   0.5774,   0.5774]"""

result = sprint((io,ea)->show(io, MIME"text/plain"(), ea),
				EulerAngleAxis(π/4, [sqrt(3)/3, sqrt(3)/3, sqrt(3)/3]),
				context = :color => false)
@test result == expected

expected = """
EulerAngleAxis{Float64}:   0.7854 rad, [  0.5774,   0.5774,   0.5774]"""

result = sprint(print, EulerAngleAxis(π/4, [sqrt(3)/3, sqrt(3)/3, sqrt(3)/3]))
@test result == expected

# Test exceptions
# ===============

@test_throws Exception EulerAngleAxis(randn(), [1;1;1;1])
@test_throws Exception EulerAngleAxis(randn(), [1;1])
@test_throws Exception EulerAngleAxis(randn(), SVector{4,Int}[1;1;1;1])
@test_throws Exception EulerAngleAxis(randn(), SVector{4,Float32}[1;1;1;1])
@test_throws Exception EulerAngleAxis(randn(), SVector{4,Float64}[1;1;1;1])
@test_throws Exception EulerAngleAxis(randn(), SVector{2,Int}[1;1])
@test_throws Exception EulerAngleAxis(randn(), SVector{2,Float32}[1;1])
@test_throws Exception EulerAngleAxis(randn(), SVector{2,Float64}[1;1])


text:
module Validation

using SearchLight, Millboard, Nullables

import Base.show

export ValidationResult, valid, invalid
export ValidtionError, ValidationRule, ModelValidator

const valid = true
const invalid = false

abstract type ValidationAbstractType end

struct ValidationResult <: ValidationAbstractType
  validation_status::Bool
  error_type::Symbol
  error_message::String

  ValidationResult(validation_status, error_type, error_message) = new(validation_status, error_type, error_message)
end
ValidationResult(; validation_status = false, error_type = :validation_error, validation_message = "Model's data is invalid") = ValidationResult(validation_status, error_type, error_message)
ValidationResult(validation_status::Bool) = validation_status ? ValidationResult(true, :no_error, "") : ValidationResult(validation_status = false)

struct ValidationError <: ValidationAbstractType
  field::Symbol
  error_type::Symbol
  error_message::String
end

struct ValidationRule <: ValidationAbstractType
  field::Symbol
  validator_function::Function
  validator_arguments::Tuple

  ValidationRule(field, validator_function, validator_arguments = ()) = new(field, validator_function, validator_arguments)
end

show(io::IO, t::T) where {T<:ValidationAbstractType} = print(io, validationabstracttype_to_print(t))

"""
    validatortype_to_print{T<:ValidationAbstractType}(m::T) :: String

Pretty printing of SearchLight types.
"""
function validationabstracttype_to_print(m::T) :: String where {T<:ValidationAbstractType}
  output = "\n" # "\n" * "$(typeof(m))" * "\n"

  try
    if (has_field(m, :error_message))
      output *= "$(m.field) $(m.error_message)"
    else
      output *= "$(m.field) $(m.validator_function)"
    end
  catch ex
    output *= string(ex) * "\n"
  end

  # Millboard.table(output) |> string
  output
end

"""
The object that defines the rules and stores the validation errors associated with the fields of a `model`.
"""
struct ModelValidator
  rules::Vector{ValidationRule} # [(:title, :not_empty), (:title, :min_length, (20)), (:content, :not_empty_if_published), (:email, :matches, (r"(.*)@(.*)"))]
  errors::Vector{ValidationError} # [(:title, :not_empty, "title not empty"), (:title, :min_length, "min length 20"), (:content, :min_length, "min length 200")]

  ModelValidator(rules) = new(rules, Vector{ValidationError}())
end

#
# errors manipulation
#

"""
    push_error!(m::T, validation_error::ValidationError)::Bool where {T<:AbstractModel}

Pushes the `error` and its corresponding `error_message` to the errors stack of the validator of the model `m` for the field `field`.
"""
function push_error!(m::T, validation_error::ValidationError)::Bool where {T<:AbstractModel}
  push!(errors!!(m), validation_error)

  true # this must be bool cause it's used for chaining Bool values
end

"""
    clear_errors!(m::T)::Nothing where {T<:AbstractModel}

Clears all the errors associated with the validator of `m`.
"""
function clear_errors!(m::T)::Nothing where {T<:AbstractModel}
  errors!!(m) |> empty!

  nothing
end

#
# validation logic
#

"""
    validate!(m::T)::Bool where {T<:AbstractModel}

Validates `m`'s data. A `bool` is return and existing errors are pushed to the validator's error stack.
"""
function validate!(m::T)::Bool where {T<:AbstractModel}
  has_validator(m) || return true

  clear_errors!(m)

  for r in rules!!(m)
    vr::ValidationResult = r.validator_function(r.field, m, r.validator_arguments...)
    vr.validation_status || push_error!(m, ValidationError(r.field, vr.error_type, vr.error_message))
  end

  is_valid(m)
end

"""
    rules!!(m::T)::Vector{ValidationRule} where {T<:AbstractModel}

Returns the `vector` of validation rules. An error is thrown if no validator is defined.
"""
function rules!!(m::T)::Vector{ValidationRule} where {T<:AbstractModel}
  validator!!(m).rules
end

"""
    rules(m::T)::Nullable{Vector{ValidationRule}} where {T<:AbstractModel}

Returns the `vector` of validation rules wrapped in a `Nullable`.
"""
function rules(m::T)::Nullable{Vector{ValidationRule}} where {T<:AbstractModel}
  v = validator(m)
  isnull(v) ? Nullable{Vector{ValidationRule}}() : Nullable{Vector{ValidationRule}}(Base.get(v).errors)
end

"""
    errors!!(m::T)::Vector{ValidationError} where {T<:AbstractModel}

Returns the `vector` of validation errors. An error is thrown if no validator is defined.
"""
function errors!!(m::T)::Vector{ValidationError} where {T<:AbstractModel}
  validator!!(m).errors
end

"""
    errors(m::T)::Nullable{Vector{ValidationError}} where {T<:AbstractModel}

Returns the `vector` of validation errors wrapped in a `Nullable`.
"""
function errors(m::T)::Nullable{Vector{ValidationError}} where {T<:AbstractModel}
  v = validator(m)
  isnull(v) ? Nullable{Vector{ValidationError}}() : Nullable{Vector{ValidationError}}(Base.get(v).errors)
end

"""
    validator!!(m::T)::ModelValidator where {T<:AbstractModel}

Returns the `ModelValidator` instance associated with `m`. Errors if no validator is defined.
"""
function validator!!(m::T)::ModelValidator where {T<:AbstractModel}
  m.validator
end

"""
    validator(m::T)::Nullable{ModelValidator} where {T<:AbstractModel}

`m`'s validator, wrapped in a Nullable.
"""
function validator(m::T)::Nullable{ModelValidator} where {T<:AbstractModel}
  has_validator(m) ? Nullable{ModelValidator}(m.validator) : Nullable{ModelValidator}()
end

"""
    has_validator(m::T)::Bool where {T<:AbstractModel}

Whether or not `m` has a validator defined.
"""
function has_validator(m::T)::Bool where {T<:AbstractModel}
  has_field(m, :validator)
end

"""
    has_errors(m::T)::Bool where {T<:AbstractModel}

Whether or not `m` has validation errors.
"""
function has_errors(m::T)::Bool where {T<:AbstractModel}
  ! isempty( errors!!(m) )
end

"""
    has_errors_for(m::T, field::Symbol)::Bool where {T<:AbstractModel}

True if `m.field` has validation errors.
"""
function has_errors_for(m::T, field::Symbol)::Bool where {T<:AbstractModel}
  ! isempty(errors_for(m, field))
end

"""
    is_valid(m::T)::Bool where {T<:AbstractModel}

Returns true if `m` has no validation errors.
"""
function is_valid(m::T)::Bool where {T<:AbstractModel}
  ! has_errors(m)
end

"""
    errors_for(m::T, field::Symbol)::Vector{ValidationError} where {T<:AbstractModel}

The vector of validation errors corresponding to `m.field`.
"""
function errors_for(m::T, field::Symbol)::Vector{ValidationError} where {T<:AbstractModel}
  result = ValidationError[]
  for err in errors!!(m)
    err.field == field && push!(result, err)
  end

  result
end

"""
    errors_messages_for(m::T, field::Symbol)::Vector{String} where {T<:AbstractModel}

Vector of error messages corresponding to the validation errors of `m.field`.
"""
function errors_messages_for(m::T, field::Symbol)::Vector{String} where {T<:AbstractModel}
  result = String[]
  for err in errors_for(m, field)
    push!(result, err.error_message)
  end

  result
end

"""
    errors_to_string(m::T, field::Symbol, separator = "\n"; upper_case_first = false)::String where {T<:AbstractModel}

Concatenates the validation errors of `m.field` into a single string -- meant to be displayed easily to end users.
"""
function errors_to_string(m::T, field::Symbol, separator = "\n"; upper_case_first = false)::String where {T<:AbstractModel}
  join( map(x -> upper_case_first ? uppercasefirst(x) : x, errors_messages_for(m, field)), separator)
end

end