#=
function _unpack_local_solve!(m::GlobalOptimizer, opt::T) where T

    m._maximum_node_id = 0
    m._termination_status_code = MOI.get(opt, MOI.TerminationStatus())
    m._result_status_code = MOI.get(opt, MOI.PrimalStatus())
    m._feasible_solution_found = m._result_status_code == MOI.FEASIBLE_POINT

    if MOI.get(opt, MOI.ResultCount()) > 0

        objective_value = MOI.get(opt, MOI.ObjectiveValue())

        # corrects for standard printing multiplier
        if _optimization_sense(m._input_problem) == MOI.MAX_SENSE
            objective_value *= -1.0
        end

        m._global_lower_bound = objective_value
        m._global_upper_bound = objective_value
        m._objective_value = objective_value
        m._best_upper_value = objective_value
        m._solution_value = objective_value
    end

    map!(x -> MOI.get(opt, MOI.VariablePrimal(), x), m._solution, m._relaxed_variable_index)

    m._run_time = time() - m._start_time

    return nothing
end

function add_sv_or_aff_obj!(m::GlobalOptimizer, opt::T) where T
    if _objective_type(_input_problem(m)) == SINGLE_VARIABLE
        MOI.set(opt, MOI.ObjectiveFunction{SV}(), m._input_problem._objective_sv)
    elseif _objective_type(_input_problem(m)) == SCALAR_AFFINE
        MOI.set(opt, MOI.ObjectiveFunction{SAF}(), m._input_problem._objective)
    end
    return nothing
end
=#

add_nl_functions!(m::GlobalOptimizer{N,T,S}) where {N,T<:Real,S} = add_nonlinear_functions!(m, m._input_problem._nlp_data)
add_nl_functions!(m::GlobalOptimizer{N,T,S}, x::Nothing) where {N,T<:Real,S} = nothing
add_nl_functions!(m::GlobalOptimizer{N,T,S}, x::MOI.NLPBlockData) where {N,T<:Real,S} = add_nl_functions!(m, x.evaluator)
function add_nonlinear_functions!(m::GlobalOptimizer{N,T,S}, x::JuMP.NLPEvaluator) where {N,T<:Real,S}

    nlp_data = m._input_problem._nlp_data
    MOI.initialize(x, Symbol[:Grad, :ExprGraph])

    # set nlp data structure
    m._working_problem._nlp_data = nlp_data

    # add subexpressions (assumes they are already ordered by JuMP)
    # creates a dictionary that lists the subexpression sparsity
    # by search each node for variables dict[2] = [2,3] indicates
    # that subexpression 2 depends on variables 2 and 3
    # this is referenced when subexpressions are called by other
    # subexpressions or functions to determine overall sparsity
    # the sparsity of a function is the collection of indices
    # in all participating subexpressions and the function itself
    # it is necessary to define this as such to enable reverse
    # McCormick constraint propagation
    relax_evaluator = m._working_problem._relaxed_evaluator
    has_subexpressions = length(x.m.nlp_data.nlexpr) > 0
    dict_sparsity = Dict{Int64,Vector{Int64}}()
    if has_subexpressions
        for i = 1:length(x.subexpressions)
            subexpr = x.subexpressions[i]
            push!(relax_evaluator.subexpressions, NonlinearExpression!(subexpr, dict_sparsity, i,
                                                                      x.subexpression_linearity,
                                                                      m.relax_tag))
        end
    end

    # scrubs udf functions using Cassette to remove odd data structures...
    # alternatively convert udfs to JuMP scripts...
    m.presolve_scrubber_flag && Script.scrub!(m._working_problem._nlp_data)
    if m.presolve_to_JuMP_flag
        Script.udf_loader!(m)
    end

    parameter_values = copy(x.parameter_values)

    # add nonlinear objective
    if x.has_nlobj
        # TODO: Should add a constraint to nlp_data... or evaluator not objective
        m._working_problem._objective_nl = BufferedNonlinearFunction(x.objective, MOI.NLPBoundsPair(-Inf, Inf),
                                                                     dict_sparsity, x.subexpression_linearity,
                                                                     m.relax_tag)
    end

    # add nonlinear constraints
    constraint_bounds = m._working_problem._nlp_data.constraint_bounds
    for i = 1:length(x.constraints)
        constraint = x.constraints[i]
        bnds = constraint_bounds[i]
        push!(m._working_problem._nonlinear_constr, BufferedNonlinearFunction(constraint, bnds, dict_sparsity,
                                                                              x.subexpression_linearity,
                                                                              m.relax_tag))
    end

    m._input_problem._nonlinear_count = length(m._working_problem._nonlinear_constr)
    m._working_problem._nonlinear_count = length(m._working_problem._nonlinear_constr)

    return nothing
end

function add_nl_evaluator!(m::GlobalOptimizer{N,T,S}) where {N,T<:Real,S}
    add_nl_evaluator!(m, m._input_problem._nlp_data)
end
add_nl_evaluator!(m::GlobalOptimizer{N,T,S}, x::Nothing) where {N,T<:Real,S} = nothing
function add_nonlinear_evaluator!(m::GlobalOptimizer{N,T,S}, x::JuMP.NLPEvaluator) where {N,T<:Real,S}
    m._working_problem._relaxed_evaluator = Evaluator{N,T}()

    relax_evaluator = m._working_problem._relaxed_evaluator
    relax_evaluator.variable_count = length(m._working_problem._variable_info)
    relax_evaluator.user_operators = x.m.nlp_data.user_operators

    relax_evaluator.lower_variable_bounds = zeros(relax_evaluator.variable_count)
    relax_evaluator.upper_variable_bounds = zeros(relax_evaluator.variable_count)
    relax_evaluator.x                     = zeros(relax_evaluator.variable_count)
    relax_evaluator.num_mv_buffer         = zeros(relax_evaluator.variable_count)
    relax_evaluator.treat_x_as_number     = fill(false, relax_evaluator.variable_count)
    relax_evaluator.ctx                   = GuardCtx(metadata = GuardTracker(m.domain_violation_ϵ,
                                                                             m.domain_violation_guard_on))
    relax_evaluator.subgrad_tol           = m.subgrad_tol

    m._nonlinear_evaluator_created = true

    return nothing
end

function add_subexpr_buffers!(m::GlobalOptimizer{N,T,S}) where {N,T<:Real,S}
    relax_evaluator = m._working_problem._relaxed_evaluator
    relax_evaluator.subexpressions_eval = fill(false, length(relax_evaluator.subexpressions))

    return nothing
end

function add_variables(m::GlobalOptimizer, optimizer::T, variable_number::Int) where T

    variable_index = fill(VI(1), variable_number)
    for i = 1:variable_number
        @inbounds variable_index[i] = MOI.add_variable(optimizer)
        single_variable = SV(@inbounds variable_index[i])
        v_info = @inbounds m._working_problem._variable_info[i]
        if _is_fixed(v_info)
            MOI.add_constraint(optimizer, single_variable, _ET(vi))
        elseif _is_real_interval(vi)
            MOI.add_constraint(optimizer, single_variable, _IT(vi))
        elseif _is_greater_than(vi)
            MOI.add_constraint(optimizer, single_variable, _GT(vi))
        elseif _is_less_than(vi)
            MOI.add_constraint(optimizer, single_variable, _LT(vi))
        elseif _is_zero_one(vi)
            MOI.add_constraint(optimizer, single_variable, _ZO(vi))
        elseif _is_int_interval(vi)
            MOI.add_constraint(optimizer, single_variable, _Int(vi))
        end
    end

    return variable_index
end

"""
$(TYPEDSIGNATURES)

Loads variables, linear constraints, and empty storage for first nlp and
quadratic cut.
"""
function load_relaxed_problem!(m::GlobalOptimizer{N,T,S}) where {N,T<:AbstractFloat,S}

    opt = m.relaxed_optimizer

    # add variables and indices and constraints
    wp = m._working_problem
    branch_num = 0

    variable_count = wp._variable_num
    for i = 1:variable_count

        vinfo = wp._variable_info[i]
        is_branch = m._branch_variables[i]
        is_branch && (branch_num += 1)

        var_indx = MOI.add_variable(opt)
        var = SV(var_indx)
        push!(m._relaxed_variable_index, var_indx)

        if vinfo.is_integer
        elseif vinfo.is_fixed
            ci_et = MOI.add_constraint(opt, var, ET(vinfo.lower_bound))
            is_branch && push!(m._relaxed_variable_eq, (ci_sv_et, branch_num))
        else
            if vinfo.has_lower_bound
                ci_gt = MOI.add_constraint(opt, var, GT(vinfo.lower_bound))
                is_branch && push!(m._relaxed_variable_gt, (ci_gt, branch_num))
            end
            if vinfo.has_upper_bound
                ci_lt = MOI.add_constraint(opt, var, LT(vinfo.upper_bound))
                is_branch && push!(m._relaxed_variable_lt, (ci_lt, branch_num))
            end
        end
    end

    # set node index to single variable constraint index maps
    m._branch_variable_num = branch_num
    m._node_to_sv_leq_ci = fill(CI{SV,LT}(-1), branch_num)
    m._node_to_sv_geq_ci = fill(CI{SV,GT}(-1), branch_num)
    for v in m._relaxed_variable_lt
        m._node_to_sv_leq_ci[v[2]] = v[1]
    end
    for v in m._relaxed_variable_gt
        m._node_to_sv_geq_ci[v[2]] = v[1]
    end

    # add linear constraints (ASSUME NOT BRIDGED... TODO: FIX LATER)
    _add_linear_constraints!(opt, m._input_problem)
    MOI.set(opt, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return
end

function _mod_vinfo!(wp, ip, ci::CI{SV,S}) where S
    sv = MOI.get(ip, MOI.ConstraintFunction(), ci)
    sv_set = MOI.get(ip, MOI.ConstraintSet(), ci)
    wp._variable_info[sv.value] = VariableInfo(wp._variable_info[sv.value], sv_set)
    return nothing
end

function _add_decision_variables!(wp, ip::InputModel{T}) where {T<:Real}
    imodel = ip._input_model
    ip_variable_num = MOI.get(imodel, MOI.NumberOfVariables())
    wp._variable_num = ip_variable_num
    wp._variable_info = fill(VariableInfo{T}(), ip_variable_num)
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, LT{T}}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, GT{T}}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, ET{T}}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, ZO}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, MOI.Integer}()))
    any(isempty, wp._variable_info) && error("Variable bounds lead to infeasible problem.") # TODO: Message + Model Issue Return Code
    return nothing
end

function _add_constraint!(wp, ip::InputModel{T}) where {T<:Real}
    imodel = ip._input_model
    ip_variable_num = MOI.get(imodel, MOI.NumberOfVariables())
    wp._variable_num = ip_variable_num
    wp._variable_info = fill(VariableInfo{T}(), ip_variable_num)
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, LT{T}}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, GT{T}}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, ET{T}}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, ZO}()))
    foreach(x -> _mod_vinfo!(wp, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SV, MOI.Integer}()))
    any(isempty, wp._variable_info) && error("Variable bounds lead to infeasible problem.") # TODO: Message + Model Issue Return Code
    return nothing
end

for Q in (LT,GT)
    @eval function _add_constraint!(wp::ParsedProblem, ip::InputModel{T}, ci::CI{SAF{T},$Q{T}}) where {T<:Real}
        imodel = ip._input_model
        f = MOI.get(imodel, MOI.ConstraintFunction(), ci)
        s = MOI.get(imodel, MOI.ConstraintSet(), ci)
        push!(wp._saf_leq, AffineFunctionIneq(f, s))
        return nothing
    end
    @eval function _add_constraint!(wp::ParsedProblem, ip::InputModel{T}, ci::CI{SQF{T},$Q{T}}) where {T<:Real}
        imodel = ip._input_model
        f = MOI.get(imodel, MOI.ConstraintFunction(), ci)
        s = MOI.get(imodel, MOI.ConstraintSet(), ci)
        push!(wp._sqf_leq, BufferedQuadraticIneq(f, s))
        return nothing
    end
end
function _add_constraint!(wp::ParsedProblem, ip::InputModel{T}, ci::CI{SAF{T},ET{T}}) where {T<:Real}
    imodel = ip._input_model
    f = MOI.get(imodel, MOI.ConstraintFunction(), ci)
    s = MOI.get(imodel, MOI.ConstraintSet(), ci)
    push!(wp._saf_eq, AffineFunctionEq(f, s))
    return nothing
end
function _add_constraint!(wp::ParsedProblem, ip::InputModel{T}, ci::CI{SQF{T},ET{T}}) where {T<:Real}
    imodel = ip._input_model
    f = MOI.get(imodel, MOI.ConstraintFunction(), ci)
    s = MOI.get(imodel, MOI.ConstraintSet(), ci)
    push!(wp._sqf_eq, BufferedQuadraticEq(f, s))
    return nothing
end

function _add_constraint!(wp::ParsedProblem, ip::InputModel{T}, ci::CI{VECVAR,SOC_CONE}) where {T<:Real}
    imodel = ip._input_model
    f = MOI.get(imodel, MOI.ConstraintFunction(), ci)
    s = MOI.get(imodel, MOI.ConstraintSet(), ci)
    first_variable_loc = f.variables[1].value
    prior_lbnd = wp._variable_info[first_variable_loc].lower_bound
    wp._variable_info[first_variable_loc].lower_bound = max(prior_lbnd, zero(T))
    push!(wp._conic_second_order, BufferedSOC(f, s))
    return nothing
end

function _add_linear_constraints!(m::S, ip::InputModel{T}) where {S,T<:Real}
    imodel = ip._input_model
    foreach(x -> _add_constraint!(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SAF{T}, LT{T}}()))
    foreach(x -> _add_constraint!(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SAF{T}, GT{T}}()))
    foreach(x -> _add_constraint!(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SAF{T}, ET{T}}()))
    return nothing
end

function _add_quadratic_constraints!(m::S, ip::InputModel{T}) where {S,T<:Real}
    imodel = ip._input_model
    foreach(x -> _add_constraint!(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SQF{T}, LT{T}}()))
    foreach(x -> _add_constraint!(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SQF{T}, GT{T}}()))
    foreach(x -> _add_constraint!(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{SQF{T}, ET{T}}()))
    return nothing
end

function _add_conic_constraints!(m::S, ip::InputModel{T}) where {S,T<:Real}
    imodel = ip._input_model
    foreach(x -> MOI.add_constraint(m, imodel, x), MOI.get(imodel, MOI.ListOfConstraintIndices{VECVAR,SOC_CONE}()))
    return nothing
end

#=
Adds objective, converting problem to MinSensae
=#
_add_objective(d::Nothing, m::S, ip::InputModel{T}) where {S,T<:Real} = false
function _add_objective(d, m::S, ip::InputModel{T}) where {S,T<:Real}
    if d.has_objective
        # TODO: other manipulations of nonlinear objective...
        imodel = ip._input_model
        vi = VI(MOI.get(imodel, MOI.NumberOfVariables()) + 1)
        push!(m._variable_info, VariableInfo{T}())
        m._objective = SAF{T}(SAT{T}[SAT{T}(one(T), vi)], zero(T))
        return true
    end
    return false
end
function _add_objective!(m::S, ip::InputModel{T}) where {S,T<:Real}
    nl_obj_set = _add_objective(ip._nlp_data, m, ip)
    if !nl_obj_set
        saf = MOI.get(ip._input_model, MOI.ObjectiveFunction{SAF{T}}())
        m._objective = copy(saf)
        #if MOI.get(ip._input_model, MOI.OptimizationSense(1)) == MOI.MAX_SENSE # TODO: Fix me!
        #    m._objective = MOIU.operate(-, T, m._objective)
        #end
    end
    m._objective_parsed = AffineFunction(copy(m._objective), m._objective.constant)
    m._optimization_sense = MOI.MIN_SENSE
    return nothing
end

"""
$(TYPEDSIGNATURES)

Creates an initial node with initial box constraints and adds it to the stack.
"""
function create_initial_node!(m::GlobalOptimizer{N,T,S}) where {N,T<:AbstractFloat,S}
    push!(m._stack, NodeBB{N,T}(ntuple(i -> _lower_bound(BranchVar, m, i), N),
                                ntuple(i -> _upper_bound(BranchVar, m, i), N),
                                -Inf, Inf, 1, 1, 0, BD_NONE))
    m._node_count = 1
    m._maximum_node_id += 1

    return nothing
end
function presolve_global!(t::ExtensionType, m::GlobalOptimizer{N,T,S}) where {N,T<:AbstractFloat,S}

    wp = _working_problem(m)
    ip = _input_problem(m)

    _add_decision_variables!(wp, ip)      # copy constraints with storage
    _add_linear_constraints!(wp, ip)
    _add_quadratic_constraints!(wp, ip)
    _add_conic_constraints!(wp, ip)

    _add_objective!(wp, ip)               # set objective function
    _label_branch_variables!(m)           # labels default variables to branch on

    add_nl_evaluator!(m)
    add_nl_functions!(m)
    add_subexpression_buffers!(m)

    load_relaxed_problem!(m)
    create_initial_node!(m)

    branch_variable_num = m._branch_variable_num

    m._current_xref             = fill(zero(T), branch_variable_num)
    m._candidate_xref           = fill(zero(T), branch_variable_num)
    m._current_objective_xref   = fill(zero(T), branch_variable_num)
    m._prior_objective_xref     = fill(zero(T), branch_variable_num)
    m._lower_lvd                = fill(zero(T), branch_variable_num)
    m._lower_uvd                = fill(zero(T), branch_variable_num)

    # populate in full space until local MOI nlp solves support constraint deletion
    # uses input model for local nlp solves... may adjust this if a convincing reason
    # to use a reformulated upper problem presents itself
    m._lower_result_count_max  = 100
    m._lower_result_count      = 0
    m._lower_solution          = Vector{T}[]
    for j = 1:_lower_result_count_max
        push!(m._lower_solution, zeros(m._working_problem._variable_num))
    end

    m._cut_solution        = zeros(T, m._working_problem._variable_num)
    m._solution            = zeros(T, m._working_problem._variable_num)
    m._upper_solution      = zeros(T, m._working_problem._variable_num)
    m._upper_variables     = fill(VI(-1), m._working_problem._variable_num)

    # add storage for fbbt
    m._lower_fbbt_buffer   = zeros(T, m._working_problem._variable_num)
    m._upper_fbbt_buffer   = zeros(T, m._working_problem._variable_num)

    # add storage for obbt ( perform obbt on all relaxed variables, potentially)
    m._obbt_working_lower_index = fill(false, branch_variable_num)
    m._obbt_working_upper_index = fill(false, branch_variable_num)
    m._old_low_index            = fill(false, branch_variable_num)
    m._old_upp_index            = fill(false, branch_variable_num)
    m._new_low_index            = fill(false, branch_variable_num)
    m._new_upp_index            = fill(false, branch_variable_num)
    m._lower_indx_diff          = fill(false, branch_variable_num)
    m._upper_indx_diff          = fill(false, branch_variable_num)
    m._obbt_variable_num        = branch_variable_num

    # set subgradient refinement flag
    wp._relaxed_evaluator.is_post = m.subgrad_tighten
    wp._relaxed_evaluator.subgrad_tighten = m.subgrad_tighten
    wp._relaxed_evaluator.reverse_subgrad_tighten =  m.reverse_subgrad_tighten

    m._presolve_time = time() - m._parse_time

    return nothing
end


"""
euler = [ψ, θ, ϕ]  # yaw, pitch, roll
"""
function distribution_info(manoeuvre::Symbol)
    p_min, p_max = nothing, nothing
    v_min, v_max = nothing, nothing
    euler_min, euler_max = nothing, nothing
    ω_min, ω_max = nothing, nothing
    if manoeuvre == :hovering
        p_min, p_max = [-1, -1, -11.0], [1, 1, -9.0]
        v_min, v_max = [-1, -1, -1.0], [1, 1, 1.0]
        euler_min, euler_max = [deg2rad(-5), deg2rad(-5), deg2rad(-5)], [deg2rad(5), deg2rad(5), deg2rad(5)]
        ω_min, ω_max = [deg2rad(-5), deg2rad(-5), deg2rad(-5)], [deg2rad(5), deg2rad(5), deg2rad(5)]
    elseif manoeuvre == :forward
        p_min, p_max = [-1, -1, -11.0], [1, 1, -9.0]
        v_min, v_max = [3, -1, -1.0], [7, 1, 1.0]
        euler_min, euler_max = [deg2rad(-2), deg2rad(-10), deg2rad(-2)], [deg2rad(2), deg2rad(-5), deg2rad(2)]
        ω_min, ω_max = [deg2rad(-2), deg2rad(-2), deg2rad(-2)], [deg2rad(2), deg2rad(2), deg2rad(2)]
    elseif manoeuvre == :debug
        p_min, p_max = [0, 0, -10.0], [0, 0, -10.0]
        v_min, v_max = zeros(3), zeros(3)
        euler_min, euler_max = zeros(3), zeros(3)
        ω_min, ω_max = zeros(3), zeros(3)
    else
        error("Invalid manoeuvre")
    end
    min_nt = (;
              p=p_min,
              v=v_min,
              euler=euler_min,
              ω=ω_min,
             )
    max_nt = (;
              p=p_max,
              v=v_max,
              euler=euler_max,
              ω=ω_max,
             )
    min_nt, max_nt
end

"""
    run_multiple_sim()

A function for running multiple simulation.
This is used for 2nd-year report.

# Notes
- collector = collect (sequential computing)
- collector = Transducers.tcollect (parallel computing)
- manoeuvre = :hovering or :forward (:debug for debugging)
- `manoeuvres` is an array of manoeuvres.
"""
function run_multiple_sim(
        N::Int,
        manoeuvres::AbstractArray{Symbol},
        methods::AbstractArray{Symbol},
        _faults,
        θs_array,  # path parameter
        t0::Real,
        tf::Real,
        h_threshold::Union{Real, Nothing},  # m (nothing: no constraint)
        actual_time_limit::Union{Real, Nothing},  # s
        N_thread::Int,
        collector,
        will_plot::Bool,
        seed::Int,
    )
    println("# of simulation cases: $(N)")
    println("manoeuvres: $(manoeuvres)")
    println("methods: $(methods)")
    if collector == tcollect
        println("Parallel computing...")
        # GR has concurrency issues
        will_plot == true ? error("plotting figures not supported in tcollect") : nothing
    elseif collector == collect
        println("Sequential computing...")
    else
        error("Invalid collector")
    end
    Random.seed!(seed)
    _dir_log = joinpath("data", "N_$(N)_seed_$(seed)")
    _dir_figure = joinpath("figures", "N_$(N)_seed_$(seed)")
    multicopter = LeeHexacopter()  # dummy
    trajs_des = θs_array |> Map(θs -> Bezier(θs, t0, tf)) |> collect
    # run sim and save fig
    for manoeuvre in manoeuvres
        x0s = 1:N |> Map(i -> FTCTests.sample(multicopter, distribution_info(manoeuvre)...)) |> collect
        for method in methods
            dir_log = joinpath(joinpath(_dir_log, String(manoeuvre)), String(method))
            dir_figure = will_plot ? joinpath(joinpath(_dir_figure, String(manoeuvre)), String(method)) : nothing
            if method == :adaptive  # method `:adaptive` does not require FDI information; not affected by FDI delay time constant `τ`.
                τs = [0.0]
            elseif method == :adaptive2optim
                τs = [0.0, 0.1]
            else
                error("Invalid method")
            end
            for τ in τs
                case_numbers_partition = 1:N |> Partition(N_thread; flush=true) |> Map(copy) |> collect
                for case_numbers in case_numbers_partition
                    @time _ = zip(case_numbers,
                                x0s[case_numbers],
                                _faults[case_numbers],
                                trajs_des[case_numbers],
                               ) |>
                    MapSplat((i, x0, _fault, traj_des) -> FTCTests.run_sim(method, x0, multicopter,
                                                                           FaultSet(_fault...),
                                                                           DelayFDI(τ),
                                                                           traj_des,
                                                                           dir_log,
                                                                           i;
                                                                           dir_figure=dir_figure,
                                                                           t0=t0, tf=tf,
                                                                           h_threshold=h_threshold,
                                                                           actual_time_limit=actual_time_limit,
                                                                          )) |> collector
                end
            end
        end
    end
    nothing
end


module Utils

using PyCall, DataFrames, KernelDensity, Statistics, StatsPlots, PlotlyBase

export corner, load_pickle, sub_dict

include("load.jl")
include("plots.jl")

function __init__()
    py"""
    import numpy as np
    import pickle

    def load_pickle(fpath):
        with open(fpath, "rb") as f:
            data = pickle.load(f)
        return data
    """
end
load_pickle = py"load_pickle"

end # module


using Test
using ProBeTune
using Statistics
using DiffEqFlux

# test samplers
@testset "Samplers" begin
    @testset "Continuos sampler" begin
        f(rng, t_span, n) = t -> rand(rng, 1)

        s = ContinousInputSampler(f, (0, 10))
        @test s.tspan == (0, 10)

        A = [f(0.0) for f in s[1:10]]
        resample!(s)
        B = [f(0.0) for f in s[1:10]]
        @test A !== B

        reset!(s)
        C = [f(0.0) for f in s[1:10]]
        @test A == C
    end

    @testset "Constant sampler" begin
        f(rng, n) =rand(rng, 5)

        s = ConstantInputSampler(f)

        A = s[1:10]
        resample!(s)
        B = s[1:10]
        @test A !== B

        reset!(s)
        C = s[1:10]
        @test A == C
    end
end

# Test a simple example
@testset "simple_example" begin
    dim_sys = 10
    diffusive_nl_example = PBTExample.create_diffusive_nl_example(dim_sys, 2, 0.:0.1:10., 10);

    p_initial = ones(2 * 10 + dim_sys)

    d_init, p_tuned, dists = behavioural_distance(diffusive_nl_example, p_initial; abstol=1e-2, reltol=1e-2)

    @test typeof(p_tuned) == typeof(p_initial)
    @test p_initial != p_tuned # test if the optimizer actually changes the parameters, this way we can also indirectly check if AD works

    res_10 = pbt_tuning(diffusive_nl_example, p_tuned; abstol=1e-4, reltol=1e-4, optimizer = ADAM(0.5), optimizer_options = (:maxiters => 50,))

    @test res_10.retcode == :Default || res_10.retcode == :Success

    d_end, p_tuned, dists = behavioural_distance(diffusive_nl_example, res_10.u; abstol=1e-2, reltol=1e-2)

    @test d_init > d_end # Testing if the tuning results in a smaller behavioural distance
end


using DataArrays, DataFrames, FixedEffectModels, Base.Test

df = DataFrame(v1 = @data(["ok", NA, "ok", "first"]), v2 = @data([1, 3, 2, 1]), v3 = @data([NA, 1, 1, NA]))

@test group(df[[:v1]]).pool == [1, 2]
@test group(df[[:v1]]).refs == [2, 0, 2, 1]

@test group(df[[:v2]]) == pool(df[:v2])
@test group(df[[:v2]]) == pool(df[:v2])

@test group(df[[:v3]]).pool ==  [1]
@test group(df[[:v3]]).refs ==  [0, 1, 1, 0]

@test group(df).pool == [1]
@test group(df).refs == [0, 0, 1, 0]

# test different syntaxes
df = DataFrame(v1 = pool(collect(1:1000)), v2 = pool(fill(1, 1000)))
@test group(df, [:v1, :v2]) == collect(1:1000)
@test group(df, :v1, :v2) == collect(1:1000)
@test group(df[:v1], df[:v2]) == collect(1:1000)


import RData
import RDatasets


import Base: view

using Base: ScalarIndex, ViewIndex, Slice, @_inline_meta, @boundscheck,
            to_indices, compute_offset1, unsafe_length, _maybe_reshape_parent, index_ndims

struct Contiguous end
struct NonContiguous end

# Detect whether the view is contiguous or not
CuIndexStyle() = Contiguous()
CuIndexStyle(I...) = NonContiguous()
CuIndexStyle(i1::Colon, ::ScalarIndex...) = Contiguous()
CuIndexStyle(i1::AbstractUnitRange, ::ScalarIndex...) = Contiguous()
CuIndexStyle(i1::Colon, I...) = CuIndexStyle(I...)

cuviewlength() = ()
cuviewlength(::Real, I...) = (@_inline_meta; cuviewlength(I...)) # skip scalars
cuviewlength(i1::AbstractUnitRange, I...) = (@_inline_meta; (unsafe_length(i1), cuviewlength(I...)...))
cuviewlength(i1::AbstractUnitRange, ::ScalarIndex...) = (@_inline_meta; (unsafe_length(i1),))

view(A::CuArray, I::Vararg{Any,N}) where {N} = (@_inline_meta; _cuview(A, I, CuIndexStyle(I...)))

function _cuview(A, I, ::Contiguous)
    @_inline_meta
    J = to_indices(A, I)
    @boundscheck checkbounds(A, J...)
    _cuview(_maybe_reshape_parent(A, index_ndims(J...)), J, cuviewlength(J...))
end

# for contiguous views just return a new CuArray
_cuview(A::CuArray{T}, I::NTuple{N,ViewIndex}, dims::NTuple{M,Integer}) where {T,N,M} =
    CuArray{T,M}(A.buf, dims; offset=A.offset + compute_offset1(A, 1, I) * sizeof(T), own=A.own)

# fallback to SubArray when the view is not contiguous
_cuview(A, I, ::NonContiguous) where {N} = invoke(view, Tuple{AbstractArray, typeof(I).parameters...}, A, I...)


abstract type MultiFieldStyle end

struct ConsecutiveMultiFieldStyle <: MultiFieldStyle end

struct StridedMultiFieldStyle <: MultiFieldStyle end

"""
    struct MultiFieldFESpace{S<:MultiFieldStyle,B} <: FESpace
      spaces::Vector{<:SingleFieldFESpace}
      multi_field_style::S
      constraint_style::Val{B}
    end
"""
struct MultiFieldFESpace{MS<:MultiFieldStyle,CS<:ConstraintStyle,V} <: FESpace
  vector_type::Type{V}
  spaces::Vector{<:SingleFieldFESpace}
  multi_field_style::MS
  constraint_style::CS
  function MultiFieldFESpace(
    ::Type{V},
    spaces::Vector{<:SingleFieldFESpace},
    multi_field_style::MultiFieldStyle) where V
    @assert length(spaces) > 0

    MS = typeof(multi_field_style)
    if any( map(has_constraints,spaces) )
      constraint_style = Constrained()
    else
      constraint_style = UnConstrained()
    end
    CS = typeof(constraint_style)
    new{MS,CS,V}(V,spaces,multi_field_style,constraint_style)
  end
end

"""
    MultiFieldFESpace(spaces::Vector{<:SingleFieldFESpace})
"""
function MultiFieldFESpace(spaces::Vector{<:SingleFieldFESpace})
  Ts = map(get_dof_value_type,spaces)
  T = typeof(*(map(zero,Ts)...))
  MultiFieldFESpace(Vector{T},spaces,ConsecutiveMultiFieldStyle())
end

function MultiFieldFESpace(::Type{V},spaces::Vector{<:SingleFieldFESpace}) where V
  MultiFieldFESpace(V,spaces,ConsecutiveMultiFieldStyle())
end

MultiFieldStyle(::Type{MultiFieldFESpace{S,B,V}}) where {S,B,V} = S()
MultiFieldStyle(f::MultiFieldFESpace) = MultiFieldStyle(typeof(f))

# Implementation of FESpace

function FESpaces.get_triangulation(f::MultiFieldFESpace)
  s1 = first(f.spaces)
  trian = get_triangulation(s1)
  @check all(map(i->trian===get_triangulation(i),f.spaces))
  trian
end

function FESpaces.num_free_dofs(f::MultiFieldFESpace)
  n = 0
  for U in f.spaces
    n += num_free_dofs(U)
  end
  n
end

FESpaces.get_dof_value_type(f::MultiFieldFESpace{MS,CS,V}) where {MS,CS,V} = eltype(V)

FESpaces.get_vector_type(f::MultiFieldFESpace) = f.vector_type

FESpaces.ConstraintStyle(::Type{MultiFieldFESpace{S,B,V}}) where {S,B,V} = B()

function FESpaces.get_cell_shapefuns(f::MultiFieldFESpace)

  # Compute the cell axes
  blocks = map(get_cell_shapefuns,f.spaces)
  blocks_data = map(get_data,blocks)
  cell_axes_blocks = map(i->lazy_map(axes,i),blocks_data)
  cell_axes = lazy_map(_multifield_axes_dofs,cell_axes_blocks...)

  # Compute the underlying single-fields
  nfields = length(f.spaces)
  all_bases = map(1:nfields) do i
    bsize = (nfields,)
    dv = blocks[i]
    cell_basis = lazy_map(Fields.BlockFieldArrayCooMap(bsize,[(i,)]),cell_axes,get_data(dv))
    FEBasis(cell_basis,get_triangulation(dv),TestBasis(),DomainStyle(dv))
  end
  MultiFieldCellField(all_bases)
end

function _multifield_axes_dofs(axs...)
  rs = map(first,axs)
  (append_ranges([rs...]),)
end

function FESpaces.get_cell_shapefuns_trial(f::MultiFieldFESpace)
  blocks = get_cell_shapefuns(f).single_fields
  nfields = length(blocks)
  all_bases = map(1:nfields) do i
    dv = blocks[i]
    cell_basis = lazy_map(transpose,get_data(dv))
    FEBasis(cell_basis,get_triangulation(dv),TrialBasis(),DomainStyle(dv))
  end
  MultiFieldCellField(all_bases)
end

function FESpaces.FEFunction(fe::MultiFieldFESpace, free_values)
  blocks = map(1:length(fe.spaces)) do i
    free_values_i = restrict_to_field(fe,free_values,i)
    FEFunction(fe.spaces[i],free_values_i)
  end
  MultiFieldFEFunction(free_values,fe,blocks)
end

function FESpaces.EvaluationFunction(fe::MultiFieldFESpace, free_values)
  blocks = map(1:length(fe.spaces)) do i
    free_values_i = restrict_to_field(fe,free_values,i)
    EvaluationFunction(fe.spaces[i],free_values_i)
  end
  MultiFieldFEFunction(free_values,fe,blocks)
end

function CellData.CellField(fe::MultiFieldFESpace,cell_values)
  single_fields = map(1:length(fe.spaces)) do i
    cell_values_field = lazy_map(a->a[Block(i)],cell_values)
    CellField(fe.spaces[i],cell_values_field)
  end
  MultiFieldCellField(single_fields)
end

function CellData.CellField(
  fe::MultiFieldFESpace,cell_values::LazyArray{<:Fill{<:BlockArrayCooMap}})
  single_fields = map(1:length(fe.spaces)) do i
    ma = cell_values.maps.value
    cell_values_field = Fields._get_cell_block(ma,cell_values,Block(i))
    CellField(fe.spaces[i],cell_values_field)
  end
  MultiFieldCellField(single_fields)
end

"""
    restrict_to_field(f::MultiFieldFESpace,free_values::AbstractVector,field::Integer)
"""
function restrict_to_field(f::MultiFieldFESpace,free_values::AbstractVector,field::Integer)
  _restrict_to_field(f,MultiFieldStyle(f),free_values,field)
end

function  _restrict_to_field(f,::MultiFieldStyle,free_values,field)
  @notimplemented
end

function  _restrict_to_field(f,::ConsecutiveMultiFieldStyle,free_values,field)
  offsets = compute_field_offsets(f)
  U = f.spaces
  pini = offsets[field] + 1
  pend = offsets[field] + num_free_dofs(U[field])
  SubVector(free_values,pini,pend)
end

"""
    compute_field_offsets(f::MultiFieldFESpace)
"""
function compute_field_offsets(f::MultiFieldFESpace)
  @assert MultiFieldStyle(f) == ConsecutiveMultiFieldStyle()
  U = f.spaces
  n = length(U)
  offsets = zeros(Int,n)
  for i in 1:(n-1)
    Ui = U[i]
    offsets[i+1] = offsets[i] + num_free_dofs(Ui)
  end
  offsets
end

function FESpaces.get_cell_isconstrained(f::MultiFieldFESpace)
  data = map(get_cell_isconstrained,f.spaces)
  lazy_map( (args...) -> +(args...)>0,  data...)
end

function FESpaces.get_cell_constraints(f::MultiFieldFESpace)
  blocks = map(get_cell_constraints,f.spaces)
  blockids = [ (i,i) for i in 1:length(f.spaces)]
  nfields = length(f.spaces)
  bsize = (nfields,nfields)

  blocks_axes = map( i->lazy_map(axes,i), blocks)

  function multifield_axes_constraints(axs...)
    rs1 = map(j->j[1],axs)
    rs2 = map(j->j[2],axs)
    r1 = append_ranges([rs1...])
    r2 = append_ranges([rs2...])
    (r1,r2)
  end
  cell_axes = lazy_map(multifield_axes_constraints,blocks_axes...)
  lazy_map(BlockArrayCooMap(bsize,blockids),cell_axes,blocks...)
end

function FESpaces.get_cell_dof_ids(f::MultiFieldFESpace)
  get_cell_dof_ids(f,MultiFieldStyle(f))
end

function FESpaces.get_cell_dof_ids(f::MultiFieldFESpace,::MultiFieldStyle)
  @notimplemented
end

function FESpaces.get_cell_dof_ids(f::MultiFieldFESpace,::ConsecutiveMultiFieldStyle)
  offsets = compute_field_offsets(f)
  spaces = f.spaces
  function fun(i,space)
    cell_dofs = get_cell_dof_ids(space)
    if i == 1
      return cell_dofs
    end
    offset = Int32(offsets[i])
    o = Fill(offset,length(cell_dofs))
    lazy_map(Broadcasting(_sum_if_first_positive),cell_dofs,o)
  end
  blocks = [ fun(i,space) for (i,space) in enumerate(spaces) ]
  bids = [ (i,) for i in 1:length(spaces)]
  bsize = (length(f.spaces),)
  cell_axes_blocks = map(i->lazy_map(axes,i),blocks)
  cell_axes = lazy_map(_multifield_axes_dofs,cell_axes_blocks...)
  lazy_map(BlockArrayCooMap(bsize,bids),cell_axes,blocks...)
end

function _sum_if_first_positive(a,b)
  if a>0
    return a+b
  else
    return a
  end
end

# API for multi field case

"""
    num_fields(f::MultiFieldFESpace)
"""
function num_fields(f::MultiFieldFESpace)
  length(f.spaces)
end

Base.iterate(m::MultiFieldFESpace) = iterate(m.spaces)

Base.iterate(m::MultiFieldFESpace,state) = iterate(m.spaces,state)

Base.getindex(m::MultiFieldFESpace,field_id::Integer) = m.spaces[field_id]

# API for the ConsecutiveMultiFieldStyle
import Gridap.FESpaces: interpolate
import Gridap.FESpaces: interpolate_everywhere
import Gridap.FESpaces: interpolate_dirichlet

@deprecate(
  interpolate(fs::MultiFieldFESpace, object),
  interpolate(object, fs::MultiFieldFESpace)
)

@deprecate(
  interpolate_everywhere(fs::MultiFieldFESpace, object),
  interpolate_everywhere(object, fs::MultiFieldFESpace)
)

@deprecate(
  interpolate_dirichlet(fs::MultiFieldFESpace, object),
  interpolate_dirichlet(object, fs::MultiFieldFESpace)
)

"""
The resulting MultiFieldFEFunction is in the space (in particular it fulfills Dirichlet BCs
even in the case that the given cell field does not fulfill them)
"""
function FESpaces.interpolate(objects, fe::MultiFieldFESpace)
  free_values = zero_free_values(fe)
  blocks = SingleFieldFEFunction[]
  for (field, (U,object)) in enumerate(zip(fe.spaces,objects))
    free_values_i = restrict_to_field(fe,free_values,field)
    uhi = interpolate!(object, free_values_i,U)
    push!(blocks,uhi)
  end
  MultiFieldFEFunction(free_values,fe,blocks)
end

"""
like interpolate, but also compute new degrees of freedom for the dirichlet component.
The resulting MultiFieldFEFunction does not necessary belongs to the underlying space
"""
function FESpaces.interpolate_everywhere(objects, fe::MultiFieldFESpace)
  free_values = zero_free_values(fe)
  blocks = SingleFieldFEFunction[]
  for (field, (U,object)) in enumerate(zip(fe.spaces,objects))
    free_values_i = restrict_to_field(fe,free_values,field)
    dirichlet_values_i = zero_dirichlet_values(U)
    uhi = interpolate_everywhere!(object, free_values_i,dirichlet_values_i,U)
    push!(blocks,uhi)
  end
  MultiFieldFEFunction(free_values,fe,blocks)
end

"""
"""
function FESpaces.interpolate_dirichlet(objects, fe::MultiFieldFESpace)
  free_values = zero_free_values(fe)
  blocks = SingleFieldFEFunction[]
  for (field, (U,object)) in enumerate(zip(fe.spaces,objects))
    free_values_i = restrict_to_field(fe,free_values,field)
    dirichlet_values_i = zero_dirichlet_values(U)
    uhi = interpolate_dirichlet!(object, free_values_i,dirichlet_values_i,U)
    push!(blocks,uhi)
  end
  MultiFieldFEFunction(free_values,fe,blocks)
end


using Images, GeometryTypes, GLVisualize, Reactive, GLWindow, Colors
using GLAbstraction, GLFW
import GLAbstraction: imagespace, singlepressed
import GLVisualize: moving_average

if !isdefined(:runtests)
    window = glscreen()
end

color_a = Signal(RGBA{Float32}(1,0,0,1))
iconsize = 54
color_v, color_s = widget(color_a, window, area=(iconsize, iconsize))
edit_screen = Screen(window, area = map(window.area) do a
    SimpleRectangle(0, 0, a.w, iconsize)
end)
paint_screen = Screen(window, area = map(window.area) do a
    SimpleRectangle(0, iconsize, a.w, a.h-iconsize)
end)
GLVisualize.add_screen(paint_screen)

using Plots; glvisualize(size = widths(paint_screen))

plt = plot(rand(100));
gui() # tell Plots.jl to plot this in a window

# Plots creates subscreens in which it plots.
# We need to recover that screen
paint_screen = plt.o.children[1]

# create a clear canvas button (unchecked seems vaguely like a good symbol for an empty canvas)
buttobj, button_s = GLVisualize.button(loadasset("unchecked.png"), edit_screen)

# create a slider for the linewidth
slider_w, slider_s = GLVisualize.slider(
    linspace(0.5f0, 20f0, 100), edit_screen,
    thickness = 2f0,
    slider_length = 4 * iconsize,
    icon_size = Signal(iconsize)
)

# view and position them with layout!
_view(
    layout!(SimpleRectangle{Float32}(0, 0, iconsize, iconsize), color_v),
    edit_screen, camera=:fixed_pixel
)
_view(
    layout!(SimpleRectangle{Float32}(iconsize+2, 0, iconsize, iconsize), buttobj),
    edit_screen, camera=:fixed_pixel
)
# translate slider
set_arg!(slider_w, :model, translationmatrix(Vec3f0(iconsize*2+4, 0, 0)))

_view(
    slider_w,
    edit_screen, camera=:fixed_pixel
)

const linebuffer = Signal(fill(Point2f0(NaN), 4))
const colorbuffer = Signal(fill(value(color_s), 4))
const lineobj = visualize(
    linebuffer, :lines, color = colorbuffer,
    thickness = slider_s
)
_view(lineobj, paint_screen, camera=:perspective)

@materialize mouseposition, mouse_buttons_pressed, mouseinside = paint_screen.inputs

camera = paint_screen.cameras[:perspective]

const history = Point2f0[] # preallocate history for moving average

s = map(mouseposition, mouse_buttons_pressed, init=nothing) do mp, mbp
    l0, c0 = map(value, (linebuffer, colorbuffer))
    if singlepressed(mbp, GLFW.MOUSE_BUTTON_LEFT) && value(mouseinside)
        p = imagespace(mp, camera)
        keep, p = moving_average(p, 1.5f0, history)
        if keep
            push!(linebuffer, push!(l0, p))
            push!(colorbuffer, push!(c0, value(color_s)))
        end
    else
        if !isnan(last(l0)) # only push one NaN to seperate
            empty!(history) # reset
            push!(linebuffer, push!(l0, Point2f0(NaN)))
            push!(colorbuffer, push!(c0, value(color_s)))
        end
    end
    nothing
end
# preserve signals, so that it doesn't get garbage collected.
preserve(s)

# reset buffers
s2 = map(button_s) do clicked
    if clicked
        push!(linebuffer, fill(Point2f0(NaN), 4))
        push!(colorbuffer, fill(value(color_s), 4))
    end
    nothing
end
preserve(s2)

if !isdefined(:runtests)
    renderloop(window)
end


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

"""
    ValueAccessor{S<:AbstractValueShape}

A value accessor provides a means to access a value with a given shape
stored in a flat real-valued data vector with a given offset position.

Constructor:

    ValueAccessor{S}(shape::S, offset::Int)

The offset is relative to the first index of a flat data array, so if
the value is stored at the beginning of the array, the offset will be zero.

An `ValueAccessor` can be used to index into a given flat data array.

Example:

```julia
acc = ValueAccessor(ArrayShape{Real}(2,3), 2)
valshape(acc) == ArrayShape{Real,2}((2, 3))
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
data[acc] == [3 5 7; 4 6 8]
```
"""
struct ValueAccessor{S<:AbstractValueShape}
    shape::S
    offset::Int
    len::Int

    ValueAccessor{S}(shape::S, offset::Int) where {S<:AbstractValueShape} =
        new{S}(shape, offset, totalndof(shape))
end

export ValueAccessor

ValueAccessor(shape::S, offset::Int) where {S<:AbstractValueShape} = ValueAccessor{S}(shape, offset)

# Value accessors behave as scalars under broadcasting:
@inline Base.Broadcast.broadcastable(shape::ValueAccessor) = Ref(shape)

nonabstract_eltype(va::ValueAccessor) = nonabstract_eltype(va.shape)

Base.size(va::ValueAccessor) = size(va.shape)
Base.length(va::ValueAccessor) = length(va.shape)

valshape(va::ValueAccessor) = va.shape

# Would this be useful?
# AbstractValueShape(va::ValueAccessor) = valshape(va)
# Base.convert(::Type{AbstractValueShape}, va::ValueAccessor) = AbstractValueShape(va)

function view_range end

@inline function view_range(idxs::AbstractUnitRange{<:Integer}, va::ValueAccessor)
    from = first(idxs) + va.offset
    to = from + va.len - 1
    from:to
end

function view_idxs end

@inline view_idxs(idxs::AbstractUnitRange{<:Integer}, va::ValueAccessor) = view_range(idxs, va)

Base.@propagate_inbounds function Base.view(
    data::AbstractMatrix, va_row::ValueAccessor, va_col::ValueAccessor
)
    view(data, view_idxs(axes(data, 1), va_row), view_idxs(axes(data, 2), va_col))
end


using NESSie.Format
using NESSie.Format: readoff_nodes, readoff_elements

testfiles = ((mktemp()..., (0,0)), # empty file
             (mktemp()..., (3,2))) # dummy file
write(testfiles[2][2], """
OFF
3 2 5
 1.0000000000e+00     0.0000000000e+00     0.0000000000e+00
 0.0000000000e+00     1.0000000000e+00     0.0000000000e+00
 0.0000000000e+00     0.0000000000e+00     1.0000000000e+00
3 0 1 2
3 2 0 1
""")

@testset "readoff" begin
    try
        for (fname, fh, len) in testfiles, fn in ("single", "bulk"), T in testtypes
            seekstart(fh)
            nodes = (); elements = ()
            if fn == "single"
                # check single methods
                readline(fh)
                readline(fh)
                nodes = readoff_nodes(fh, len[1], T)
                elements = readoff_elements(fh, len[2], nodes)
            elseif fn == "bulk"
                # check bulk method
                model = readoff(fh, T)
                nodes, elements = (model.nodes, model.elements)
            end
            # check return types
            @test typeof(nodes) == Vector{Vector{T}}
            @test typeof(elements) == Vector{Triangle{T}}

            # check lengths
            @test length(nodes) == len[1]
            @test length(elements) == len[2]

            # check content
            if fname == testfiles[2][1]
                @test nodes[1] == T[1, 0, 0]
                @test nodes[2] == T[0, 1, 0]
                @test nodes[3] == T[0, 0, 1]
                @test elements[1].v1 === nodes[1]
                @test elements[1].v2 === nodes[2]
                @test elements[1].v3 === nodes[3]
                @test elements[2].v1 === nodes[3]
                @test elements[2].v2 === nodes[1]
                @test elements[2].v3 === nodes[2]
            end
        end
    finally
        [begin close(fh); rm(fn); end for (fn, fh, _) in testfiles]
    end
end


using Kosmos
using Test

@testset "Kosmos.jl" begin

    include("clock.jl")
    include("sysgeneration.jl")

end


# Apply dimensionless periodic boundary conditions
# Alex Tam, 12/10/2020

"Apply dimensionless periodic boundary conditions to segments and motors"
function periodic(pos1, pos2)
    translations = Vector{Vector{Int}}(undef, 5); # Pre-allocate vector of translation vectors
    translations[1] = [0,0]; # Ensure that the identity translation occurs first
    translations[2] = [floor(pos1[1]), floor(pos1[2])]; # Store 1st possible combination
    translations[3] = [floor(pos1[1]), floor(pos2[2])]; # Store 2nd possible combination
    translations[4] = [floor(pos2[1]), floor(pos1[2])]; # Store 3rd possible combination
    translations[5] = [floor(pos2[1]), floor(pos2[2])]; # Store 4th possible combination
    translations = unique(translations); # Delete repeated translations
    return translations
end

"Update all filament segment translations"
function segment_translations(s, af)
    for f in af
        for seg in f.segments
            seg.t = periodic(s.an[f.index][seg.index+1], s.an[f.index][seg.index]); # Apply periodic BCs
        end
    end
    return af
end


using Go
using Base.Test

function random_position(seed, board_size, n_moves)
    srand(seed)
    p = Position{board_size}()
    for i in 1:n_moves
        for t in (black, white)
            ok = false
            while !ok
                try
                    p = move!(Position(p), t, rand(1:board_size), rand(1:board_size))
                    ok = true
                catch e
                    if !isa(e, IllegalMove)
                        rethrow()
                    end
                end
            end
        end
    end
    p
end

function random_expression(n)
    @assert n > 7
    e = rand(UInt8, n)
    e[1:4] = map(UInt8, collect("goxp"))
    e[5] = 0x00
    e[6:7] = reinterpret(UInt8, UInt16[n])
    e
end

function random_population(n, m)
    [random_expression(n) for i in 1:m]
end

function compare(path, objs...)
    print(path)
    s = join([sprint(print, x) for x in objs], "\n") * "\n"
    if !exists(path)
        print(" (w)")
        open(path, "w") do io
            print(io, s)
        end
    end
    @test s == open(readall, path, "r")
    println(" ok")
end

include("position.jl")
include("expression.jl")
include("evolution.jl")


module SquareSymmetries

export unit, rotate90, rotate180, rotate270, flipdiag, flipadiag, flipx, flipy, symmetries

include("symmetry.jl")

end


using Test

import PySCF: get_4idx

@testset "Indexing functions" begin
   @test get_4idx(1,2,3,4) == 38
end

include("test_scf.jl")
include("test_mp2.jl")
include("test_ccsd.jl")
include("test_properties.jl")


using Brillouin
using Test

# defines `test_show` to test `show` methods with good error diffs, using DeepDiffs.jl
include("testutils_show.jl")

# ---------------------------------------------------------------------------------------- #
# core API/utilities
include("kpaths.jl")
include("wignerseitz.jl")
include("kpaths_spglib.jl")

# ---------------------------------------------------------------------------------------- #
# optional plot utilities
include("plotlyjs.jl")


using Random
using Distributed

struct SerialIterator
    len::Int
    get_example::Function
    batchsize::Int
    indices::Vector
    function SerialIterator(dataset::Dataset, batchsize::Int; shuffle=true)
        indices=Vector(1:dataset.len)
        if shuffle
            shuffle!(indices)
        end
        _get_example = i -> get_example(dataset, i)
        new(length(dataset), _get_example, batchsize, indices)
    end
end

function Base.iterate(diter::SerialIterator, state=(1, 0))
    idx_start, count = state
    if idx_start + diter.batchsize > diter.len
        return nothing
    else
        indices = diter.indices[idx_start:idx_start+diter.batchsize-1]
        element = diter.get_example.(indices)
        return (element, (idx_start + diter.batchsize, count + 1))
    end
end


# dependencies
using MPI
using Compat
using Printf
using LinearAlgebra: BlasFloat, BlasReal
using DistributedArrays, Distributed
using DistributedArrays: DArray, defaultdist

# enicode encoded array to input ccall
f_pchar(scope::Char) = transcode(UInt8, string(scope))
f_pstring(scope::String) = transcode(UInt8, string(scope))

# path to scalapack dynamic library
const libscalapack = "/usr/local/Cellar/scalapack/2.0.2_15/lib/libscalapack.dylib"
# depend on envionment
# const ScaInt = Int32
const ScaInt = Int64
const zero = convert(ScaInt, 0)
const one = convert(ScaInt, 1)

# array descriptor index
struct descriptor_index
    # params
    dtype::ScaInt
    ctxt::ScaInt
    m::ScaInt; n::ScaInt;
    mb::ScaInt; nb::ScaInt;
    rsrc::ScaInt; csrc::ScaInt;
    lld::ScaInt;
    # constructor
    descriptor_index() = new(1, 2, 3, 4, 5, 6, 7, 8, 9)
end
const desc_idx = descriptor_index()


module ConstrainedFESpaces

using Gridap
using Gridap.Helpers
using Gridap.CellValuesGallery

export ConstrainedFESpace

import Gridap: num_free_dofs
import Gridap: num_diri_dofs
import Gridap: diri_tags
import Gridap: apply_constraints
import Gridap: apply_constraints_rows
import Gridap: apply_constraints_cols
import Gridap: celldofids
import Gridap: interpolate_values
import Gridap: interpolate_diri_values
import Gridap: CellField
import Gridap: CellFieldForEval
import Gridap: CellBasis
import Gridap: Triangulation

using Base: @propagate_inbounds
import Base: size
import Base: IndexStyle
import Base: getindex

"""
A FESpace that it is constructed from another one plus some constraints
on the free values.
For the moment, we can only fix some free dofs. In the future, we will add
also linear constraints between free dofs.
"""
struct ConstrainedFESpace{D,Z,T,E} <: FESpace{D,Z,T}
  fespace::FESpace{D,Z,T}
  fixeddofs::Vector{Int}
  fixedvals::Vector{E}
  nfree::Int
  celldofs::IndexCellVector{Int}
  is_fixed::Vector{Bool}
  is_free::Vector{Bool}
  dof_to_new_dof::Vector{Int}
end

function ConstrainedFESpace(
  fespace::FESpace{D,Z,T},
  fixeddofs::Vector{Int},
  fixedvals::Vector{E}=zeros(dof_type(T),length(fixeddofs))) where {D,Z,T,E}

  @assert E == dof_type(T)

  celldofs, nfree, is_fixed, is_free, dof_to_new_dof =
    _setup_celldofs(fespace,fixeddofs)
  ConstrainedFESpace{D,Z,T,E}(
    fespace,fixeddofs,fixedvals,nfree,celldofs,
    is_fixed,is_free,dof_to_new_dof)
end

num_free_dofs(this::ConstrainedFESpace) = this.nfree

num_diri_dofs(this::ConstrainedFESpace) = num_diri_dofs(this.fespace)

diri_tags(this::ConstrainedFESpace) = diri_tags(this.fespace)

apply_constraints(
  this::ConstrainedFESpace,
  cellvec::CellVector,
  cellids::CellNumber) = apply_constraints(this.fespace,cellvec,cellids)

apply_constraints_rows(
  this::ConstrainedFESpace,
  cellmat::CellMatrix,
  cellids::CellNumber) = apply_constraints_rows(this.fespace,cellmat,cellids)

apply_constraints_cols(
  this::ConstrainedFESpace,
  cellmat::CellMatrix,
  cellids::CellNumber) = apply_constraints_cols(this.fespace,cellmat,cellids)

function celldofids(this::ConstrainedFESpace)
  this.celldofs
end

function interpolate_values(this::ConstrainedFESpace,fun::Function)
  freevals, dirivals = interpolate_values(this.fespace,fun)
  (freevals[this.is_free], dirivals)
end

function interpolate_values(this::ConstrainedFESpace{D,Z,T},val::T) where {D,Z,T}
  freevals, dirivals = interpolate_values(this.fespace,val)
  (freevals[this.is_free], dirivals)
end

function interpolate_diri_values(this::ConstrainedFESpace, funs::Vector{<:Function})
  interpolate_diri_values(this.fespace,funs)
end

function interpolate_diri_values(this::ConstrainedFESpace{D,Z,T}, vals::Vector{T}) where {D,Z,T}
  interpolate_diri_values(this.fespace,vals)
end

function CellField(
  this::ConstrainedFESpace{D,Z,T},
  free_dofs::AbstractVector{E},
  diri_dofs::AbstractVector{E}) where {D,Z,T,E}

  ndiri = length(diri_dofs)
  v = VectorOfTwoParts(this.dof_to_new_dof,free_dofs,this.fixedvals,ndiri)

  CellField(this.fespace,v,diri_dofs)
end

function CellFieldForEval(
  this::ConstrainedFESpace{D,Z,T},
  free_dofs::AbstractVector{E},
  diri_dofs::AbstractVector{E}) where {D,Z,T,E}

  ndiri = length(diri_dofs)
  v = VectorOfTwoParts(this.dof_to_new_dof,free_dofs,this.fixedvals,ndiri)

  CellFieldForEval(this.fespace,v,diri_dofs)
end

function CellBasis(this::ConstrainedFESpace{D,Z,T}) where {D,Z,T}
  CellBasis(this.fespace)
end

function Triangulation(this::ConstrainedFESpace{D,Z}) where {Z,D}
  Triangulation(this.fespace)
end

# Helpers

struct VectorOfTwoParts{T,VT1<:AbstractVector{T},VT2<:AbstractVector{T},I} <: AbstractVector{T}
  dof_to_xdof::Vector{I}
  pdof_to_val::VT1
  ndof_to_val::VT2
  offset::I
end

size(v::VectorOfTwoParts) = (length(v.dof_to_xdof),)

IndexStyle(::Type{VectorOfTwoParts{T,I}}) where {T,I} = IndexLinear()

@propagate_inbounds function getindex(v::VectorOfTwoParts,i::Integer)
  @inbounds xdof = v.dof_to_xdof[i]
  if xdof > 0
    @inbounds return v.pdof_to_val[xdof]
  elseif xdof< 0
    @inbounds return v.ndof_to_val[-xdof-v.offset]
  end
end

function _setup_celldofs(fespace,fixeddofs)

  celldofs = celldofids(fespace)
  nfree = num_free_dofs(fespace)
  ndiri = num_diri_dofs(fespace)

  nfixed = length(fixeddofs)

  nfree_new = nfree - nfixed

  dof_to_new_dof = zeros(Int,nfree)
  is_fixed = fill(false,nfree)
  is_fixed[fixeddofs] .= true
  is_free = is_fixed.==false

  dof_to_new_dof[is_free] .= 1:nfree_new
  dof_to_new_dof[is_fixed] .= (-(ndiri+1)):-1:(-(ndiri+nfixed))

  celldofs_new = CellVectorFromLocalToGlobalPosAndNeg(
    celldofs,dof_to_new_dof,collect(-1:-1:-ndiri))

  (celldofs_new, nfree_new, is_fixed, is_free, dof_to_new_dof)
end

end # module


"""
    DynamicBin{TPS<:Tuple}
    DynamicBin(size::Int, types::Type...) -> DynamicBin{Tuple{types...}}

A bin that can push! items into. It contains a vector of vector, with each sub-vector has specific types.
"""
struct DynamicBin{TPS<:Tuple} <: AbstractBin
    binsize::Int
    data::Vector{Vector}
end
DynamicBin(size::Int, types::Type...) = DynamicBin{Tuple{types...}}(size, [tp[] for tp in types])
function Base.push!(bin::DynamicBin, item)
    for (ii, vec) in zip(item, bin.data)
        push!(vec, ii)
    end
    bin
end

Base.length(bin::DynamicBin) = bin.data[1] |> length
fitted(bin::DynamicBin) = length(bin)%bin.binsize == 0

print_lastbin(bin::DynamicBin) = print_lastbin(stdout, bin)
function print_lastbin(io::IO, bin::DynamicBin)  # only show the mean of last batch
    if length(bin) < bin.binsize
        print(io, "No filled bin | N = $(length(bin))")
    else
        means = [mean(v[end-bin.binsize+1:end]) for v in bin.data]
        print(io, join([(@sprintf " %1.3e " m) for m in means], " "), " | N = $(length(bin))")
    end
    means
end


import LazySets.require

function __init__()
    @require JuMP = "4076af6c-e467-56ae-b986-b466b2749572" include(raw"initialization/jump.jl")
    @require MathematicalSystems = "d14a8603-c872-5ed3-9ece-53e0e82e39da" include(raw"initialization/mathematical_systems.jl")
    # This packages are already required in LazySets (i think)
    # @require Polyhedra = "67491407-f73d-577b-9b50-8179a7c68029" ""
    # @require CDDLib = "3391f64e-dcde-5f30-b752-e11513730f60"
    # @require Optim = "429524aa-4258-5aef-a3af-852621145aeb"
end


struct DecisionIndex <: MOI.AbstractConstraintAttribute end
MOI.is_set_by_optimize(::DecisionIndex) = true

struct DecisionCoefficientChange{T} <: MOI.AbstractFunctionModification
    decision::MOI.VariableIndex
    new_coefficient::T
end

struct DecisionMultirowChange{T} <: MOI.AbstractFunctionModification
    decision::MOI.VariableIndex
    new_coefficients::Vector{Tuple{Int64, T}}
end

struct DecisionStateChange <: MOI.AbstractFunctionModification
    index::Int
    new_state::DecisionState
end

struct KnownValuesChange <: MOI.AbstractFunctionModification end

const DecisionModification = Union{DecisionCoefficientChange, DecisionStateChange, KnownValuesChange}
const VectorDecisionModification = Union{DecisionMultirowChange, DecisionStateChange, KnownValuesChange}


"""
    recorder(index<:AbstractRange, value::Array{T,1}, step_info::Mvector{2})

- Holds array of type {T} over the range indicated by index.
- Step holds two numbers: (i,j) where j = index[i]
- Arrays are initialised as undef{T}
"""

struct recorder{T, U<:Integer, R<:AbstractRange}
    # holds a set of timepoints, and associated data of type T. Also a name. everything is in arrays so quicker to assign and allows for immutability of this struct
    index::R
    value::Array{T,1}
    current_step::MArray{Tuple{2},U,1,2}   # statically sized array of size 1, with mutable entries
end
recorder(index::T, value) where T <: AbstractRange = recorder(index,Array{typeof(value)}(undef, length(index)), MVector(1,index[1]))
# recorder(r::recorder, rg::AbstractRange) = recorder(r.index[findall(x -> x ∈ rg, r.index)], r.value[findall(x -> x ∈ rg, r.index)])
recorder(r::recorder, rg::AbstractRange) = recorder(rg, r.value[findall(x -> x ∈ rg, r.index)], MVector(1,rg[1]))

@recipe f(::Type{recorder}, r::recorder) = (r.index, r.value)

"""
    make_records_template(l::lossInfo, which::NamedTuple)
e.g.

    make_records_template(l, NamedTuple{(:grad_norm, :params)}(1:4, 5:7))

records grad_norm at timesteps 1:4 and params at timesteps 5:7
#### Available things to record:
    :grad_norm, :loss, :training_loss, :test_loss, :local_task_difficulty
    :grad, :update, :sys_noise, :intr_noise, :params
"""
function make_records_template(l::lossInfo, which::NamedTuple)
    fields = Array{recorder,1}(undef,length(keys(which)))
    weights_template = Tracker.data.(params(get_model(l)))
    for (i,name) in enumerate(keys(which))
         if name ∈ (:grad, :test_grad, :update, :sys_noise, :intr_noise, :params, :full_training_grad)
            # make an array of weights templates for these
            fields[i] = recorder(which[name], deepcopy(weights_template))
        elseif name ∈ (:grad_norm, :test_grad_norm, :loss, :training_loss,
                       :test_loss, :local_task_difficulty, :update_norm,
                       :training_test_grad_correlation, :full_training_loss)
            # make an array of floats for these
            fields[i] = recorder(which[name], 0.)
        end
    end
    return NamedTuple{keys(which)}(fields)
end

function make_some_model_templates(l::lossInfo, num_holders)
    # preallocate arrays for: update term, noise term,
    m = get_model(l)
    template = Tracker.data.(params(m))
    r = recorder(1:num_holders, template)
    [r.value[i] = deepcopy(template) for i in r.index]
    for i in r.index #zero the nested array
        [fill!(el,0) for el in r.value[i]]
    end
    return r
end

function make_temp_storage(l::lossInfo ,u::updateInfo, tr::trainingInfo, rec )
    store = Dict{Symbol,Any}
    store = Dict((:lossInfo, :updateInfo, :trainingInfo) .=> (l,u,tr))
    store[:params] = params(get_model(l))
    return store
end

"""
    set_not_updated(store, recording_template)
makes store[:not_updated];  a set of names that are not updated by do_updates(), but need to be updated for add_records().
"""
function set_not_updated!(store, rec)
    # must come immediately after initialise_updates()
    # the names of the things we have to update:
    store[:not_updated] = setdiff(keys(rec), keys(store))
    # to check = reduce(∪, get_storage_dependencies.(store[:not_updated])...)
    to_check = get_storage_dependencies(store[:not_updated])
    store[:am_i_updated] = Dict(to_check .=> false)
    return store
end

"""
    add_records(rec,store, data, t)
within the training loop at time t. fill rec given values in store
data is only used for calculating local task difficulty
"""
function add_records!(rec, store, t::Integer)
    li = store[:lossInfo]
    tr = store[:trainingInfo]
    [store[:am_i_updated][name] = false for name in keys(store[:am_i_updated])]

    for name in keys(rec)
            # my_rec = rec[name]
            if t == rec[name].current_step[2]
                # println(name, t)
                store = fill_record!(store,rec[name],name)
                rec[name].current_step[1] += 1
                if length(rec[name].index) >= rec[name].current_step[1]
                    rec[name].current_step[2] = rec[name].index[rec[name].current_step[1]]
                end
            end
    end

    return rec, store
end
"""
check whether to fill record at this timestep
"""
function fill_record!(store, my_rec,name)
    if name ∈ store[:not_updated] && store[:am_i_updated][name] == false
    # update store values if not already done
        # store = @eval $(Symbol("get_",name,"!"))(store)
        # e.g. get_grad_norm!(store)
        # store = eval(:($(Symbol("get_",name,"!"))(store))) eval is global scope so this doesn't work
        store = getfield(WeightDrifter, Symbol("get_$(name)!"))(store[:updateInfo], store)
        # store = f(store)
        store[:am_i_updated][name] = true
    end
    if name ∈ (:grad, :test_grad, :full_training_grad)
        my_rec.value[my_rec.current_step[1]] = Tracker.data.(store[name][p] for p in store[:params])
    elseif name == :params
        my_rec.value[my_rec.current_step[1]] =  Tracker.data.(store[name])
    else
        my_rec.value[my_rec.current_step[1]] =  Tracker.data(store[name])
    end
    return store
end


module SIMD

# Define an abstract type to parameterize the generated vector types
abstract AbstractVec{N,T}

Base.length{N,T}(::AbstractVec{N,T}) = N
Base.eltype{N,T}(::AbstractVec{N,T}) = T

# Create a vector type
function mkvectype{T}(typename::Symbol, N::Integer, ::Type{T})
    implname = symbol(typename, "_impl")
    @eval begin
        nbits = 8*$N*sizeof($T)
        bitstype nbits $implname
        immutable $typename <: AbstractVec{$N,$T}
            elts::$implname
            $typename(elts::$implname) = new(elts)
        end
        Base.length(::Type{$typename}) = $N
        Base.eltype(::Type{$typename}) = $T
    end
end

# Return a vector type, creating it if necessary
export Vec
@generated function Vec{N,T}(::Type{Val{N}}, ::Type{T})
    typename = symbol("Vec", N, "x", T)
    mkvectype(typename, N, T)
    typename
end
Vec{T}(N::Integer, ::Type{T}) = Vec(Val{N}, T)

# Convert Julia types to LLVM types
llvmtype(::Type{Int8}) = "i8"
llvmtype(::Type{Int16}) = "i16"
llvmtype(::Type{Int32}) = "i32"
llvmtype(::Type{Int64}) = "i64"
llvmtype(::Type{Int128}) = "i128"

llvmtype(::Type{UInt8}) = "i8"
llvmtype(::Type{UInt16}) = "i16"
llvmtype(::Type{UInt32}) = "i32"
llvmtype(::Type{UInt64}) = "i64"
llvmtype(::Type{UInt128}) = "i128"

llvmtype(::Type{Float16}) = "half"
llvmtype(::Type{Float32}) = "float"
llvmtype(::Type{Float64}) = "double"

# Type-dependent optimization flags
fastflags{T<:Signed}(::Type{T}) = "nsw"
fastflags{T<:Unsigned}(::Type{T}) = "nuw"
fastflags{T<:AbstractFloat}(::Type{T}) = "fast"

# Type-dependent LLVM intrinsics
llvmins{T<:Integer}(::Type{Val{:+}}, ::Type{T}) = "add"
llvmins{T<:Integer}(::Type{Val{:-}}, ::Type{T}) = "sub"
llvmins{T<:Integer}(::Type{Val{:*}}, ::Type{T}) = "mul"
llvmins{T<:Signed}(::Type{Val{:div}}, ::Type{T}) = "sdiv"
llvmins{T<:Signed}(::Type{Val{:rem}}, ::Type{T}) = "srem"
llvmins{T<:Unsigned}(::Type{Val{:div}}, ::Type{T}) = "udiv"
llvmins{T<:Unsigned}(::Type{Val{:rem}}, ::Type{T}) = "urem"

llvmins{T<:AbstractFloat}(::Type{Val{:+}}, ::Type{T}) = "fadd"
llvmins{T<:AbstractFloat}(::Type{Val{:-}}, ::Type{T}) = "fsub"
llvmins{T<:AbstractFloat}(::Type{Val{:*}}, ::Type{T}) = "fmul"
llvmins{T<:AbstractFloat}(::Type{Val{:/}}, ::Type{T}) = "fdiv"
llvmins{T<:AbstractFloat}(::Type{Val{:rem}}, ::Type{T}) = "frem"

llvmins{T<:AbstractFloat}(::Type{Val{:^}}, ::Type{T}) = "@llvm.pow"
llvmins{T<:AbstractFloat}(::Type{Val{:abs}}, ::Type{T}) = "@llvm.fabs"
llvmins{T<:AbstractFloat}(::Type{Val{:muladd}}, ::Type{T}) = "@llvm.fmuladd"
llvmins{T<:AbstractFloat}(::Type{Val{:sqrt}}, ::Type{T}) = "@llvm.fsqrt"

# Function wrappers
@generated function llvmwrap{Op,N,T}(::Type{Val{Op}}, v1::AbstractVec{N,T})
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ins = llvmins(Val{Op}, T)
    flags = fastflags(T)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    if ins[1] == '@'
        suffix = ".v$(N)f$(8*sizeof(T))"
        push!(decls, "declare <$N x $typ> $ins$suffix(<$N x $typ>)")
        push!(instrs, "%res = call <$N x $typ> $ins$suffix(<$N x $typ> %arg1)")
    else
        push!(instrs, "%res = $ins $flags <$N x $typ> zeroinitializer, %arg1")
    end
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{$etyp}, v1.elts))
    end
end

@generated function llvmwrap{Op,N,T}(::Type{Val{Op}},
        v1::AbstractVec{N,T}, v2::AbstractVec{N,T})
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ins = llvmins(Val{Op}, T)
    flags = fastflags(T)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    push!(instrs, "%arg2 = bitcast $jtyp %1 to <$N x $typ>")
    if ins[1] == '@'
        suffix = ".v$(N)f$(8*sizeof(T))"
        push!(decls,
            "declare <$N x $typ> $ins$suffix(<$N x $typ>, <$N x $typ>)")
        push!(instrs,
            "%res = call <$N x $typ> $ins$suffix(<$N x $typ> %arg1, " *
                "<$N x $typ> %arg2)")
    else
        push!(instrs, "%res = $ins $flags <$N x $typ> %arg1, %arg2")
    end
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{$etyp, $etyp}, v1.elts, v2.elts))
    end
end

@generated function llvmwrap{Op,N,T}(::Type{Val{Op}},
        v1::AbstractVec{N,T}, v2::AbstractVec{N,T}, v3::AbstractVec{N,T})
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ins = llvmins(Val{Op}, T)
    flags = fastflags(T)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    push!(instrs, "%arg2 = bitcast $jtyp %1 to <$N x $typ>")
    push!(instrs, "%arg3 = bitcast $jtyp %2 to <$N x $typ>")
    if ins[1] == '@'
        suffix = ".v$(N)f$(8*sizeof(T))"
        push!(decls,
            "declare <$N x $typ> $ins$suffix(<$N x $typ>, <$N x $typ>, " *
                "<$N x $typ>)")
        push!(instrs,
            "%res = call <$N x $typ> $ins$suffix(<$N x $typ> %arg1, " *
                "<$N x $typ> %arg2, <$N x $typ> %arg3)")
    else
        push!(instrs, "%res = $ins $flags <$N x $typ> %arg1, %arg2, %arg3")
    end
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{$etyp, $etyp, $etyp}, v1.elts, v2.elts, v3.elts))
    end
end

# Arithmetic functions

for op in (:+, :-, :abs, :sqrt)
    @eval begin
        Base.$op{N,T}(v1::AbstractVec{N,T}) =
            llvmwrap(Val{$(QuoteNode(op))}, v1)
    end
end

for op in (:+, :-, :*, :/, :div, :rem, :^)
    @eval begin
        Base.$op{N,T}(v1::AbstractVec{N,T}, v2::AbstractVec{N,T}) =
            llvmwrap(Val{$(QuoteNode(op))}, v1, v2)
    end
end

for op in (:muladd,)
    @eval begin
        Base.$op{N,T}(v1::AbstractVec{N,T}, v2::AbstractVec{N,T},
                v3::AbstractVec{N,T}) =
            llvmwrap(Val{$(QuoteNode(op))}, v1, v2, v3)
    end
end

# Load and store functions

function scalar2vector(vec, siz, typ, sca)
    instrs = []
    accum(nam, i) = i<0 ? "undef" : i==siz-1 ? nam : "$(nam)_$i"
    for i in 0:siz-1
        push!(instrs,
            "$(accum(vec,i)) = " *
                "insertelement <$siz x $typ> $(accum(vec,i-1)), " *
                "$typ $sca, i32 $i")
    end
    instrs
end

function array2vector(vec, siz, typ, arr, tmp=arr)
    instrs = []
    accum(nam, i) = i<0 ? "undef" : i==siz-1 ? nam : "$(nam)_$i"
    for i in 0:siz-1
        push!(instrs, "$(tmp)_$i = extractvalue [$siz x $typ] $arr, $i")
        push!(instrs,
            "$(accum(vec,i)) = " *
                "insertelement <$siz x $typ> $(accum(vec,i-1)), " *
                "$typ $(tmp)_$i, i32 $i")
    end
    instrs
end

function vector2array(arr, siz, typ, vec, tmp=vec)
    instrs = []
    accum(nam, i) = i<0 ? "undef" : i==siz-1 ? nam : "$(nam)_$i"
    for i in 0:siz-1
        push!(instrs, "$(tmp)_$i = extractelement <$siz x $typ> $vec, i32 $i")
        push!(instrs,
            "$(accum(arr,i)) = "*
                "insertvalue [$siz x $typ] $(accum(arr,i-1)), " *
                "$typ $(tmp)_$i, $i")
    end
    instrs
end

@generated function Base.convert{V<:AbstractVec}(::Type{V}, x::Number)
    N = length(V)
    T = eltype(V)
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    decls = []
    instrs = []
    append!(instrs, scalar2vector("%res", N, typ, "%0"))
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = Vec(N,T)
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{$T}, $T(x)))
    end
end

@generated function Base.convert{V<:AbstractVec}(::Type{V}, xs::Tuple)
    N = length(V)
    T = eltype(V)
    @assert nfields(xs) == N
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    decls = []
    instrs = []
    append!(instrs, array2vector("%res", N, typ, "%0", "%resarr"))
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = Vec(N,T)
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{NTuple{$N,$T}}, NTuple{$N,$T}(xs)))
    end
end

@generated function Base.convert{N,T}(::Type{NTuple{N,T}}, v1::AbstractVec{N,T})
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    append!(instrs, vector2array("%res", N, typ, "%arg1"))
    push!(instrs, "ret [$N x $typ] %res")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            NTuple{$N,$T}, Tuple{$etyp}, v1.elts)
    end
end

export setindex
@generated function setindex{N,T,I}(v1::AbstractVec{N,T}, ::Type{Val{I}}, x)
    @assert isa(I, Integer)
    @assert 1 <= I <= N
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ityp = llvmtype(Int)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    push!(instrs,
        "%res = insertelement <$N x $typ> %arg1, $typ %1, $ityp $(I-1)")
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{$etyp, T}, v1.elts, T(x)))
    end
end

export setindex
@generated function setindex{N,T}(v1::AbstractVec{N,T}, i::Integer, x)
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ityp = llvmtype(Int)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    push!(instrs, "%res = insertelement <$N x $typ> %arg1, $typ %2, $ityp %1")
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        #TODO @boundscheck @assert 1 <= i <= N
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{$etyp, Int, T}, v1.elts, Int(i)-1, T(x)))
    end
end

@generated function Base.getindex{N,T,I}(v1::AbstractVec{N,T}, ::Type{Val{I}})
    @assert isa(I, Integer)
    @assert 1 <= I <= N
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ityp = llvmtype(Int)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    push!(instrs, "%res = extractelement <$N x $typ> %arg1, $ityp $(I-1)")
    push!(instrs, "ret $typ %res")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            T, Tuple{$etyp}, v1.elts)
    end
end

@generated function Base.getindex{N,T}(v1::AbstractVec{N,T}, i::Integer)
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    ityp = llvmtype(Int)
    decls = []
    instrs = []
    push!(instrs, "%arg1 = bitcast $jtyp %0 to <$N x $typ>")
    push!(instrs, "%res = extractelement <$N x $typ> %arg1, $ityp %1")
    push!(instrs, "ret $typ %res")
    vtyp = v1
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        #TODO @boundscheck @assert 1 <= i <= N
        Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            T, Tuple{$etyp, Int}, v1.elts, Int(i)-1)
    end
end

export vload, vloada
@generated function vload{V<:AbstractVec, T, Aligned}(::Type{V}, ptr::Ptr{T},
        ::Type{Val{Aligned}} = Val{false})
    @assert isa(Aligned, Bool)
    N = length(V)
    @assert T === eltype(V)
    jtyp = "i$(8*N*sizeof(T))"
    typ = llvmtype(T)
    decls = []
    instrs = []
    if Aligned
        align = N * sizeof(T)
    else
        align = sizeof(T)   # This is overly optimistic
    end
    flags = ", align $align"
    push!(instrs, "%ptr = bitcast $typ* %0 to <$N x $typ>*")
    push!(instrs, "%res = load <$N x $typ>, <$N x $typ>* %ptr$flags")
    push!(instrs, "%resbits = bitcast <$N x $typ> %res to $jtyp")
    push!(instrs, "ret $jtyp %resbits")
    # push!(instrs, "%ptr = bitcast $typ* %0 to $jtyp*")
    # push!(instrs, "%res = load $jtyp, $jtyp* %ptr$flags")
    # push!(instrs, "ret $jtyp %res")
    vtyp = Vec(N,T)
    etyp = fieldtype(vtyp, 1)
    quote
        # $(Expr(:meta, :inline))
        $vtyp(Base.llvmcall($((join(decls, "\n"), join(instrs, "\n"))),
            $etyp, Tuple{Ptr{T}}, ptr))
    end
end

vloada{V<:AbstractVec}(::Type{V}, ptr::Ptr) = vload(V, ptr, Val{true})

@inline function vload{V<:AbstractVec, Aligned}(::Type{V}, arr::Vector,
        i::Integer, ::Type{Val{Aligned}} = Val{false})
    #TODO @boundscheck @assert 1 <= i < length(arr) - length(V)
    vload(V, pointer(arr, i), Val{Aligned})
end
vloada{V<:AbstractVec}(::Type{V}, arr::Vector, i::Integer) =
    vload(V, arr, i, Val{true})

end


module GaussianRV
using FFTW

include("fft_sample.jl")
export sample_field

end # module


using Test
using ThreeBodyTB
using Suppressor

#include("../includes_laguerre.jl")
#include("../Ewald.jl")

function testU()

    @testset "testing U" begin

#        Atomdata.atoms["X"].energy_offset = 0.0
#        Atomdata.atoms["Xa"].energy_offset = 0.0
        @suppress begin

            units_old = ThreeBodyTB.set_units()
            ThreeBodyTB.set_units(both="atomic")

            cscl = makecrys([1.0 0 0; 0 1.0 0; 0 0 1.0]*10.0, [0 0 0; 0.5 0.5 0.5], ["X", "Xa"], units="Bohr");
            coefXX = ThreeBodyTB.CalcTB.make_coefs(Set((:X, :X)),2, fillzeros=true)
            coefXXa = ThreeBodyTB.CalcTB.make_coefs(Set((:X, :Xa)),2, fillzeros=true)
            coefXaXa = ThreeBodyTB.CalcTB.make_coefs(Set((:Xa, :Xa)),2, fillzeros=true)

            database = Dict()
            database[(:X, :X)] = coefXX
            database[(:X, :Xa)] = coefXXa
            database[(:Xa, :X)] = coefXXa
            database[(:Xa, :Xa)] = coefXaXa
            database["scf"] = true
            database["SCF"] = true

            tbc = ThreeBodyTB.CalcTB.calc_tb_fast(cscl, database, use_threebody=false, use_threebody_onsite=false)
            tbc.gamma = ThreeBodyTB.Ewald.electrostatics_getgamma(cscl, onlyU=true) #only use onsite U, no electrostatics

            @test size(tbc.gamma) == (2,2)
            @test tbc.gamma[1,1] == 0.5
            @test tbc.gamma[2,2] == 0.5
            @test tbc.gamma[1,2] == 0.0

            tbc.scf = true

            energy, efermi, e_den, dq, error_flag = ThreeBodyTB.SCF.scf_energy(tbc, mix=0.1, grid=[1 1 1], conv_thr = 1e-8, iters=1000)
            println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")

            #        energy, efermi, e_den, dq = SCF.scf_energy(tbc, mix=0.5, grid=[1 1 1], conv_thr = 1e-8, mixing_mode=:pulay, iters=1000)

            #        println("energy $energy")
            #        println("efermi $efermi")
            #        println("dq $dq")

            @test abs(energy - 0.5) ≤ 1e-6
            @test abs(efermi - 1.5) ≤ 1e-5
            @test abs(dq[1] - 1.0) ≤ 1e-5
            @test abs(dq[2] + 1.0) ≤ 1e-5
            @test abs(e_den[1] - 1.0) ≤ 1e-5
            @test abs(e_den[2] - 0.0) ≤ 1e-5

            cscl2 = makecrys([1.0 0 0; 0 1.0 0; 0 0 1.0]*10.0, [0 0 0; 0.5 0.5 0.5], ["X", "X"], units="Bohr");
            tbc2 = ThreeBodyTB.CalcTB.calc_tb_fast(cscl2, database, use_threebody=false, use_threebody_onsite=false)
            tbc2.gamma = ThreeBodyTB.Ewald.electrostatics_getgamma(cscl, onlyU=true) #only use onsite U, no electrostatics
            tbc2.scf = true
            energy, efermi, e_den, dq, error_flag = ThreeBodyTB.SCF.scf_energy(tbc2, mix=0.75, grid=[1 1 1], conv_thr = 1e-8)
            @test abs(energy - -0.011283791670955126) ≤ 1e-6

            ThreeBodyTB.set_units(energy = units_old[1], length=units_old[2])

        end
    end
end

function test_nacl()

    @testset "testing nacl" begin
        @suppress begin
            units_old = ThreeBodyTB.set_units()
            ThreeBodyTB.set_units(both="atomic")

            cscl = makecrys([1.0 0 0; 0 1.0 0; 0 0 1.0]*19.5509319833, [0 0 0; 0.5 0.5 0.5], ["Na", "Cl"], units="Bohr");

            #no tb params
            coefXX = ThreeBodyTB.CalcTB.make_coefs(Set(("Na", "Na")),2, fillzeros=true)
            coefXXa = ThreeBodyTB.CalcTB.make_coefs(Set(("Na", "Cl")),2, fillzeros=true)
            coefXaXa = ThreeBodyTB.CalcTB.make_coefs(Set(("Cl", "Cl")),2, fillzeros=true)

            database = Dict()
            database[(:Na, :Na)] = coefXX
            database[(:Na, :Cl)] = coefXXa
            database[(:Cl, :Na)] = coefXXa
            database[(:Cl, :Cl)] = coefXaXa
            database["scf"] = true
            database["SCF"] = true

            tbc = ThreeBodyTB.CalcTB.calc_tb_fast(cscl, database, use_threebody=false, use_threebody_onsite=false)

            tbc.scf = true

            energy, efermi, e_den, dq, error_flag = ThreeBodyTB.SCF.scf_energy(tbc, mix=0.005, grid=[1 1 1], conv_thr = 1e-8, smearing = 0.005, iters = 1000)

            #        println("energy $energy")
            #        println("efermi $efermi")
            #        println("e_den $e_den")
            #        println("dq $dq")

            tbc_noscf = ThreeBodyTB.CalcTB.calc_tb_fast(cscl, database, use_threebody=false, use_threebody_onsite=false)
            tbc_noscf.scf = false
            energy_no, efermi_no, e_den_no, dq_no, error_flag = ThreeBodyTB.SCF.scf_energy(tbc_noscf, mix=0.005, grid=[1 1 1], conv_thr = 1e-8, smearing = 0.005, iters = 1000)

            #        println("energy_no $energy_no")
            #        println("efermi_no $efermi_no")
            #        println("e_den_no $e_den_no")
            #        println("dq_no $dq_no")

            #        println("the actual DFT atomization  energy of this configuration is -0.13722807000000614")
            #        println("energy_scf is $energy")
            #        println("energy_noscf is $energy_no")

            @test abs(-0.13722807000000614 - energy) < 5e-2

            ThreeBodyTB.set_units(energy = units_old[1], length=units_old[2])

        end
    end
end

function test_nacl2()

    @testset "testing nacl2" begin
        @suppress begin
            units_old = ThreeBodyTB.set_units()
            ThreeBodyTB.set_units(both="atomic")

            rs = makecrys([1.0 1.0 0; 1.0 0 1.0; 0 1.0 1.0]*10.6062839325, [0 0 0; 0.5 0.5 0.5], ["Na", "Cl"], units="Bohr");

            #no tb params
            coefXX = ThreeBodyTB.CalcTB.make_coefs(Set(("Na", "Na")),2, fillzeros=true)
            coefXXa = ThreeBodyTB.CalcTB.make_coefs(Set(("Na", "Cl")),2, fillzeros=true)
            coefXaXa = ThreeBodyTB.CalcTB.make_coefs(Set(("Cl", "Cl")),2, fillzeros=true)

            database = Dict()
            database[(:Na, :Na)] = coefXX
            database[(:Na, :Cl)] = coefXXa
            database[(:Cl, :Na)] = coefXXa
            database[(:Cl, :Cl)] = coefXaXa
            database["scf"] = true
            database["SCF"] = true

            tbc = ThreeBodyTB.CalcTB.calc_tb_fast(rs, database, use_threebody=false, use_threebody_onsite=false)

            tbc.scf = true

            energy, efermi, e_den, dq, error_flag = ThreeBodyTB.SCF.scf_energy(tbc, mix=0.005, grid=[1 1 1], conv_thr = 1e-8, smearing = 0.01, iters = 1000)

            #        println("energy $energy")
            #        println("efermi $efermi")
            #        println("e_den $e_den")
            #        println("dq $dq")

            tbc_noscf = ThreeBodyTB.CalcTB.calc_tb_fast(rs, database, use_threebody=false, use_threebody_onsite=false)
            tbc_noscf.scf = false
            energy_no, efermi_no, e_den_no, dq_no, error_flag = ThreeBodyTB.SCF.scf_energy(tbc_noscf, mix=0.5, grid=[1 1 1], conv_thr = 1e-8, smearing = 0.01, iters = 1000) #, mixing_mode=:simple)

            #        println("energy_no $energy_no")
            #        println("efermi_no $efermi_no")
            #        println("e_den_no $e_den_no")
            #        println("dq_no $dq_no")

            #        println("the actual DFT atomization energy of this configuration is -0.23582715999999948")
            #        println("energy_scf is $energy")
            #        println("energy_noscf is $energy_no")

            @test abs(-0.23582715999999948 - energy) < 6e-2

            ThreeBodyTB.set_units(energy = units_old[1], length=units_old[2])

        end
    end
end

testU()
#println("sleep ")
#sleep(3)
test_nacl()
#println("sleep ")
#sleep(3)
test_nacl2()


"""
Submodule of `SirenSeq`.
Standard musical scales are defined here.
"""
module Scales

## a scale is a function that takes (ocv::Int, val::Int) as input and outputs a midi pitch value {0..127}
## `ocv` is an octave offset while `val` is the note interval on the scale
## scales can be any length between 1 and 11

export

## standard 7 note scales

aLyd,aMaj,aMix,aDor,aMin,aPhr,aLoc,
bLyd,bMaj,bMix,bDor,bMin,bPhr,bLoc,
cLyd,cMaj,cMix,cDor,cMin,cPhr,cLoc,
dLyd,dMaj,dMix,dDor,dMin,dPhr,dLoc,
eLyd,eMaj,eMix,eDor,eMin,ePhr,eLoc,
fLyd,fMaj,fMix,fDor,fMin,fPhr,fLoc,
gLyd,gMaj,gMix,gDor,gMin,gPhr,gLoc,

## a scale which ignores `ocv` and uses `val` as the midi pitch value
## useful for percussion tracks

noScale,

## returns the pitch value for a given scale and given (octave,interval) pair

getPitch

abstract Scale

type ChromaticMode <: Scale
	pmap::Dict{Int,Int}
	basev::Int
	name::AbstractString
end

type PercussionScale <: Scale
	name::AbstractString
end

function Base.isequal(x::ChromaticMode, y::ChromaticMode)
	if !isequal(x.pmap,y.pmap) ; return false ; end
	if !isequal(x.basev,y.basev) ; return false ; end
	if !isequal(x.name,y.name) ; return false ; end
	true
end

function Base.isequal(x::PercussionScale, y::PercussionScale)
	true
end

function Base.isequal(x::PercussionScale, y::ChromaticMode)
	false
end

function toPitch(ocv::Int, val::Int, scaleDict::Dict{Int,Int}, ofs::Int)
	ssz = length(scaleDict) ;  @assert  0 < ssz < 12
	while val < 1
		val += ssz
		ocv -= 1
	end
	while val > ssz
		val -= ssz
		ocv += 1
	end
	clamp(ofs+scaleDict[val]+60+12*(ocv-4),0,127)
end

## returns the pitch value for a given scale and given (octave,interval) pair
function getPitch(sca::ChromaticMode, ocv::Int, val::Int)
	toPitch(ocv,val,sca.pmap,sca.basev)
end

function getPitch(sca::PercussionScale, ocv::Int, val::Int)
	clamp(val,0,127)
end

const cLydDc = Dict{Int,Int}( 1=>0, 2=>2, 3=>4, 4=>6, 5=>7, 6=>9, 7=>11 )
const cMajDc = Dict{Int,Int}( 1=>0, 2=>2, 3=>4, 4=>5, 5=>7, 6=>9, 7=>11 )
const cMixDc = Dict{Int,Int}( 1=>0, 2=>2, 3=>4, 4=>5, 5=>7, 6=>9, 7=>10 )
const cDorDc = Dict{Int,Int}( 1=>0, 2=>2, 3=>4, 4=>5, 5=>7, 6=>8, 7=>10 )
const cMinDc = Dict{Int,Int}( 1=>0, 2=>2, 3=>3, 4=>5, 5=>7, 6=>8, 7=>10 )
const cPhrDc = Dict{Int,Int}( 1=>0, 2=>1, 3=>3, 4=>5, 5=>7, 6=>8, 7=>10 )
const cLocDc = Dict{Int,Int}( 1=>0, 2=>1, 3=>3, 4=>5, 5=>6, 6=>8, 7=>10 )

noScale = PercussionScale("perc")

aLyd = ChromaticMode(cLydDc,-3,"cLyd")
aMaj = ChromaticMode(cMajDc,-3,"cMaj")
aMix = ChromaticMode(cMixDc,-3,"cMix")
aDor = ChromaticMode(cDorDc,-3,"aDor")
aMin = ChromaticMode(cMinDc,-3,"aMin")
aPhr = ChromaticMode(cPhrDc,-3,"aPhr")
aLoc = ChromaticMode(cLocDc,-3,"aLoc")

bLyd = ChromaticMode(cLydDc,-1,"bLyd")
bMaj = ChromaticMode(cMajDc,-1,"bMaj")
bMix = ChromaticMode(cMixDc,-1,"bMix")
bDor = ChromaticMode(cDorDc,-1,"bDor")
bMin = ChromaticMode(cMinDc,-1,"bMin")
bPhr = ChromaticMode(cPhrDc,-1,"bPhr")
bLoc = ChromaticMode(cLocDc,-1,"bLoc")

cLyd = ChromaticMode(cLydDc,0,"cLyd")
cMaj = ChromaticMode(cMajDc,0,"cMaj")
cMix = ChromaticMode(cMixDc,0,"cMix")
cDor = ChromaticMode(cDorDc,0,"cDor")
cMin = ChromaticMode(cMinDc,0,"cMin")
cPhr = ChromaticMode(cPhrDc,0,"cPhr")
cLoc = ChromaticMode(cLocDc,0,"cLoc")

dLyd = ChromaticMode(cLydDc,2,"dLyd")
dMaj = ChromaticMode(cMajDc,2,"dMaj")
dMix = ChromaticMode(cMixDc,2,"dMix")
dDor = ChromaticMode(cDorDc,2,"dDor")
dMin = ChromaticMode(cMinDc,2,"dMin")
dPhr = ChromaticMode(cPhrDc,2,"dPhr")
dLoc = ChromaticMode(cLocDc,2,"dLoc")

eLyd = ChromaticMode(cLydDc,4,"eLyd")
eMaj = ChromaticMode(cMajDc,4,"eMaj")
eMix = ChromaticMode(cMixDc,4,"eMix")
eDor = ChromaticMode(cDorDc,4,"eDor")
eMin = ChromaticMode(cMinDc,4,"eMin")
ePhr = ChromaticMode(cPhrDc,4,"ePhr")
eLoc = ChromaticMode(cLocDc,4,"eLoc")

fLyd = ChromaticMode(cLydDc,6,"fLyd")
fMaj = ChromaticMode(cMajDc,6,"fMaj")
fMix = ChromaticMode(cMixDc,6,"fMix")
fDor = ChromaticMode(cDorDc,6,"fDor")
fMin = ChromaticMode(cMinDc,6,"fMin")
fPhr = ChromaticMode(cPhrDc,6,"fPhr")
fLoc = ChromaticMode(cLocDc,6,"fLoc")

gLyd = ChromaticMode(cLydDc,7,"gLyd")
gMaj = ChromaticMode(cMajDc,7,"gMaj")
gMix = ChromaticMode(cMixDc,7,"gMix")
gDor = ChromaticMode(cDorDc,7,"gDor")
gMin = ChromaticMode(cMinDc,7,"gMin")
gPhr = ChromaticMode(cPhrDc,7,"gPhr")
gLoc = ChromaticMode(cLocDc,7,"gLoc")

end