text:
using ArgParse, HDF5, JLD

include("./simulator.jl")

function check_missing(missingfile, topology, algorithm, topology_index, scenario_id, traffic_id, scale_id, option_num)
    targetfile = "/home/arrow-data-process/arrow-data/availability/$(topology)/jld/$(algorithm)/Bandwidth_Availability_$(topology_index)_$(scenario_id)_$(traffic_id)_$(scale_id)_$(option_num).jld"

    missingfile_list = readdlm(missingfile, header=false)
    println(missingfile_list)

    if in(targetfile, missingfile_list)
        printstyled("running missing file $(targetfile)\n", color=:blue)
        return true
    else
        printstyled("skipping file $(targetfile)\n", color=:blue)
        return false
    end
end

function main()
    s = ArgParseSettings(description = "arg parse here for parallel experimentation")
    @add_arg_table! s begin
        "--traffic"  # traffic ID
        "--scale", "-s"  # demand scale
        "--topology", "-t"  # topology name
        "--te", "-a"  # TE algorithm
        "--verbose", "-v"  # stdout of not
        "--parallel", "-p"  # parallel file location
        "--downscale", "-d"  # location
        "--singleplot", "-g"  # have only one folder in regular plot
        "--topoindex", "-i"  # index of IP topology
        "--cutoff", "-c"  # failure scenario cutoff
        "--tunnel", "-n"  # tunnel number per node pair
        "--scenarioID", "-o"  # generate new failure scenarios from weibull
        "--ticketsnum", "-k"  # number of lottery tickets
        "--largeticketsnum", "-j"  # number of lottery tickets in the largest tickets file
        "--abstractoptical", "-b"  # only run abstract optical layer (weibull or K scenarios) or not
        "--teavarbeta", "-x"  # target availability for teavar
        "--tunneltype", "-u"  # TE tunnel type
        "--missing", "-m"  # run for missing file or not
        "--simulation", "-l"  # run failure simulation after TE optimization or not
        "--failurefree", "-f"  # if running fully restorable TE without failure
        "--expandspectrum", "-e"  # expand fiber spectrum (e.g. to L band) for tunable restoration ratios
        "--scenariogeneration", "-r"  # -1 if do not genearte any tickets, 0 if generate tickets for all scenarios, or generate tickets for the r'th scenario
    end
    parsed_args = parse_args(s) # the result is a Dict{String,Any}

    AllTopologies = [parsed_args["topology"]]
    verbose = parse(Bool, parsed_args["verbose"])
    AllTopoIndex = [parsed_args["topoindex"]]
    scenario_cutoff = parse(Float64, parsed_args["cutoff"])
    scenario_id = parsed_args["scenarioID"]
    option_num = parse(Int64, parsed_args["ticketsnum"])
    large_option_num = parse(Int64, parsed_args["largeticketsnum"])
    abstractoptical = parse(Int64, parsed_args["abstractoptical"])
    singleplot = parse(Bool, parsed_args["singleplot"])
    beta = parse(Float64, parsed_args["teavarbeta"])
    tunnel_rounting = parse(Int64, parsed_args["tunneltype"])
    missingmode = parse(Bool, parsed_args["missing"])
    failure_simulation = parse(Bool, parsed_args["simulation"])
    failure_free = parse(Bool, parsed_args["failurefree"])
    expanded_spectrum = parse(Int64, parsed_args["expandspectrum"])
    scenario_generation_only = parse(Int64, parsed_args["scenariogeneration"])

    All_demand_upscale = [1]

    # randomized rounding gap (how much slack variable delta can go beyond naive restoration solution)
    option_gap = 0  # default sum relaxation without randomized rounding
    if AllTopologies[1] == "B4"
        option_gap = 0.95  # the sum relaxation for lottery selection, 0.95 for B4, 0.9 for IBM
    elseif AllTopologies[1] == "IBM"
        option_gap = 0.9  # the sum relaxation for lottery selection, 0.95 for B4, 0.9 for IBM
    end

    optical_rerouting_K = 3  # number of surrogate fiber paths for restoration on optical layer
    partial_load_full_tickets = true  # in the ticket scaling evaluation, we only generate a ticket file with large ticket number and partial load to get smaller tickets, otherwise, we need dedicated ticket file for each ticket number
    dirname = "./data/experiment/exp-$(string(today()))"
    dir = nextRun(dirname, singleplot)

    GRB_ENV = Gurobi.Env()

    if abstractoptical > 0
        ## only generating failure scenarios into files
        weibull_or_k = 1  # generate failure scenarios using weibull
        if abstractoptical == 2
            weibull_or_k = 0  # generate failure scenarios considering K simutaneous fiber cut
        end

        if option_num > 1
            ticket_or_not = 2  # ARROW, multiple tickets
        elseif option_num == 1
            ticket_or_not = 1  # ARROW-NAIVE, one single ticket
        else
            ticket_or_not = 0  # All other TEs
        end

        IPTopo, OpticalTopo, IPScenarios, OpticalScenarios = get_failure_scenarios(dir, AllTopologies[1], AllTopoIndex[1], verbose, scenario_cutoff, scenario_id, weibull_or_k, failure_free, expanded_spectrum)
        if scenario_generation_only >= 0
            ## parallel generating tickets for each scenarios in a scenario file
            abstract_optical_layer(GRB_ENV, IPTopo, OpticalTopo, IPScenarios, OpticalScenarios, scenario_generation_only, dir, AllTopologies[1], AllTopoIndex[1], verbose, scenario_cutoff, scenario_id, optical_rerouting_K, partial_load_full_tickets, option_num, large_option_num, option_gap, ticket_or_not, expanded_spectrum)
            printstyled("Abstracting $(AllTopologies[1]) optical layer with $(scenario_cutoff) cutoff and $(option_num) lottery ticket for scenario file ID $(scenario_id) index $(scenario_generation_only) completed!\n", color=:blue)
        end
    else
        ## run TE optimization and failure simulation based on failure scenarios parsed from files
        AllTraffic = [parse(Int64, parsed_args["traffic"])]
        scales = [parse(Float64, parsed_args["scale"])]
        AllAlgorithms = split(parsed_args["te"], ",")
        parallel_dir = parsed_args["parallel"]
        All_demand_downscale = [parse(Float64, parsed_args["downscale"])]
        AllTunnelNum = [parsed_args["tunnel"]]

        if isfile("$(parallel_dir)/$(AllTopologies[1])/parallel_setup.txt") == false
            mkdir("$(parallel_dir)/$(AllTopologies[1])")
        end

        open("$(parallel_dir)/$(AllTopologies[1])/parallel_setup.txt", "w+") do io
            writedlm(io, ("AllAlgorithms", AllAlgorithms))
            writedlm(io, ("AllTopoIndex", AllTopoIndex))
            writedlm(io, ("AllTunnelNum", AllTunnelNum))
            writedlm(io, ("All_demand_upscale", All_demand_upscale))
            writedlm(io, ("All_demand_downscale", All_demand_downscale))
            writedlm(io, ("AllTraffic", AllTraffic))
            writedlm(io, ("scenario_cutoff", scenario_cutoff))
            writedlm(io, ("beta", beta))
            writedlm(io, ("scales", scales))
            writedlm(io, ("option_num", option_num))
            writedlm(io, ("single experiment results", dir))
            writedlm(io, ("scenario_id", scenario_id))
            writedlm(io, ("tunnel_rounting", tunnel_rounting))
        end

        ## here we assume all single run, no serial otherwise error
        topology = AllTopologies[1]
        algorithm = AllAlgorithms[1]
        topology_index = AllTopoIndex[1]
        scenario_id = scenario_id
        traffic_id = AllTraffic[1]
        scale_id = scales[1]
        option_num = option_num
        missingfile = "./data/topology/$(topology)/IP_topo_$(topology_index)/missing_data_$(algorithm)_availability.txt"

        accumulate_running = 0
        if missingmode
            if check_missing(missingfile, topology, algorithm, topology_index, scenario_id, traffic_id, scale_id, option_num)
                accumulate_running += 1
                println("Running simulation number $(accumulate_running)")
                simulator(GRB_ENV, dir, AllTopologies, AllTopoIndex, AllTunnelNum, AllAlgorithms, All_demand_upscale, All_demand_downscale, AllTraffic, scenario_cutoff, beta, scales, optical_rerouting_K, partial_load_full_tickets, option_num, large_option_num, option_gap, verbose, parallel_dir, singleplot, scenario_id, tunnel_rounting, failure_simulation, failure_free, expanded_spectrum)
                printstyled("Find parallel run results of traffic-$(parsed_args["traffic"]) scale-$(parsed_args["scale"]) this parallel run in $(parallel_dir)\n", color=:blue)
                printstyled("Find single run results of traffic-$(parsed_args["traffic"]) scale-$(parsed_args["scale"]) this parallel run in $(dir)\n", color=:blue)
            else
                printstyled("Skip traffic-$(parsed_args["traffic"]) scale-$(parsed_args["scale"]) this parallel run in $(parallel_dir)\n", color=:green)
            end
        else
            @time simulator(GRB_ENV, dir, AllTopologies, AllTopoIndex, AllTunnelNum, AllAlgorithms, All_demand_upscale, All_demand_downscale, AllTraffic, scenario_cutoff, beta, scales, optical_rerouting_K, partial_load_full_tickets, option_num, large_option_num, option_gap, verbose, parallel_dir, singleplot, scenario_id, tunnel_rounting, failure_simulation, failure_free, expanded_spectrum)
            printstyled("Find parallel run results of traffic-$(parsed_args["traffic"]) scale-$(parsed_args["scale"]) this parallel run in $(parallel_dir)\n", color=:blue)
            printstyled("Find single run results of traffic-$(parsed_args["traffic"]) scale-$(parsed_args["scale"]) this parallel run in $(dir)\n", color=:blue)
        end
    end
end

main()

max_stars_repo_name: hipersys-team/arrow

max_issues_repo_name: arrow-bow/arrow

max_forks_repo_name: arrow-bow/arrow


text:
# specialize on the X gate
function subspace_mul!(st::AbstractVector, U::Val{:X_test}, loc::Int)
    n = log2dim(st)

    if loc == 1
        @inbounds @batch for k in 0:2:((1<<n)-1)
            idx_1 = k
            idx_2 = k | 1
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    elseif loc == 2
        @inbounds @batch for k in 0:4:((1<<n)-1)
            idx_1 = k
            idx_2 = k | 0b10
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b01
            idx_2 = k | 0b11
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    elseif loc == 3
        @inbounds @batch per=thread for k in 0:8:((1<<n)-1)
            idx_1 = k
            idx_2 = k | 0b100
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b001
            idx_2 = k | 0b101
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b010
            idx_2 = k | 0b110
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b011
            idx_2 = k | 0b111
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    elseif loc == 4
        @inbounds @batch for k in 0:16:((1<<n)-1)
            idx_1 = k
            idx_2 = k | 0b1000
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0001
            idx_2 = k | 0b1001
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0010
            idx_2 = k | 0b1010
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0011
            idx_2 = k | 0b1011
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0100
            idx_2 = k | 0b1100
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0101
            idx_2 = k | 0b1101
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0110
            idx_2 = k | 0b1110
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | 0b0111
            idx_2 = k | 0b1111
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    else
        # subspace_mul_X_test_large_loc_1!(st, n, loc)
        # subspace_mul_X_test_large_loc_2!(st, n, loc)
        # subspace_mul_X_test_large_loc_3!(st, n, loc)
        subspace_mul_X_test_large_loc_4!(st, n, loc)
    end
end

@inline function subspace_mul_X_test_large_loc_1!(st::AbstractVector, n::Int, loc::Int)
    # the original loops for large loc (loc≥5)
    loc_bit = 1 << (loc-1)
    @inbounds @batch for k_highbits in 0 : (1<<loc) : ((1<<n)-1)
        for k in k_highbits : 8 : ((k_highbits | loc_bit) - 1)
            subspace_mul_X_test_large_loc_kernel!(st, k, loc_bit)
        end
    end
end

@inline function subspace_mul_X_test_large_loc_2!(st::AbstractVector, n::Int, loc::Int)
    # for loc≥5.
    # better load balance but (a little bit?) larger kernel for each thread,
    # since we need to recover k from k_continuous.

    loc_bit = 1 << (loc-1)
    mask_highbits = -1 << loc
    mask_lowbits = ((1<<loc) - 1) & (-1 << 4)
    @inbounds @batch for k_continuous in 0 : 16 : ((1<<n)-1)
        k_highbits = k_continuous & mask_highbits
        k_lowbits = (k_continuous & mask_lowbits) >> 1
        k = k_highbits | k_lowbits
        subspace_mul_X_test_large_loc_kernel!(st, k, loc_bit)
    end
end

@inline function subspace_mul_X_test_large_loc_3!(st::AbstractVector, n::Int, loc::Int)
    # for loc≥5.
    # very poor performance when the number of threads is small,
    # probably due to bad cache locality

    n_minus_loc = n - loc
    if n_minus_loc == 0
        loc_bit = 1<<(n-1)
        @inbounds @batch for k in 0 : (loc_bit - 1)
            idx_1 = k
            idx_2 = k | (1<<(n-1))
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    elseif n_minus_loc == 1
        loc_bit = 1<<(n-2)
        mask_1 = 1<<(n-1)
        @inbounds @batch for k in 0 : (loc_bit - 1)
            idx_1 = k
            idx_2 = k | loc_bit
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_1
            idx_2 = k | (mask_1 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    elseif n_minus_loc == 2
        loc_bit = 1<<(n-3)
        mask_01 =            1<<(n-2)
        mask_10 = 1<<(n-1)
        mask_11 = 1<<(n-1) | 1<<(n-2)
        @inbounds @batch for k in 0 : (loc_bit - 1)
            idx_1 = k
            idx_2 = k | loc_bit
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_01
            idx_2 = k | (mask_01 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_10
            idx_2 = k | (mask_10 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_11
            idx_2 = k | (mask_11 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    elseif n_minus_loc == 3
        loc_bit = 1<<(n-4)
        mask_001 =                       1<<(n-3)
        mask_010 =            1<<(n-2)
        mask_011 =            1<<(n-2) | 1<<(n-3)
        mask_100 = 1<<(n-1)
        mask_101 = 1<<(n-1)            | 1<<(n-3)
        mask_110 = 1<<(n-1) | 1<<(n-2)
        mask_111 = 1<<(n-1) | 1<<(n-2) | 1<<(n-3)
        @inbounds @batch for k in 0 : (loc_bit - 1)
            idx_1 = k
            idx_2 = k | loc_bit
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_001
            idx_2 = k | (mask_001 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_010
            idx_2 = k | (mask_010 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_011
            idx_2 = k | (mask_011 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_100
            idx_2 = k | (mask_100 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_101
            idx_2 = k | (mask_101 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_110
            idx_2 = k | (mask_110 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
            idx_1 = k | mask_111
            idx_2 = k | (mask_111 | loc_bit)
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    else
        subspace_mul_X_test_large_loc_1!(st, n, loc)
    end
end

@inline function subspace_mul_X_test_large_loc_4!(st::AbstractVector, n::Int, loc::Int)
    # for loc≥5.
    # Features of this method:
    # (1) The number of iterations of the outer loop matches the number of threads,
    #     thereby fixing the highest bits of k in each thread, striving for good cache locality
    # (2) Compared to subspace_mul_X_test_large_loc_2!, this method only requires
    #     one operation of "recover k from k_continuous" in each thread

    tot_threads = Threads.nthreads()
    tot_threads == 1 && return subspace_mul_X_test_large_loc_1!(st, n, loc)

    num_highbits = Base.min(log2i(tot_threads), n-1)
    loc < n-num_highbits && return subspace_mul_X_test_large_loc_2!(st, n, loc)

    num_threads = 1 << num_highbits
    mask_highbits = -1 << loc
    mask_lowbits = (1<<loc) - 1

    @inbounds @batch for k_continuous in 0 : (1<<(n-num_highbits)) : ((num_threads-1)<<(n-num_highbits))
        k_highbits = k_continuous & mask_highbits
        k_lowbits = (k_continuous & mask_lowbits) >> 1
        k = k_highbits | k_lowbits
        j_max = (1 << (n-1-num_highbits)) - 1
        for j in k : (k | j_max)
            idx_1 = j
            idx_2 = j | (1<<(loc-1))
            subspace_mul_X_test_swap!(st, idx_1, idx_2)
        end
    end
end

@inline function subspace_mul_X_test_large_loc_kernel!(st::AbstractVector, k::Int, loc_bit::Int)
    idx_1 = k
    idx_2 = k | loc_bit
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b001
    idx_2 = k | (0b001 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b010
    idx_2 = k | (0b010 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b011
    idx_2 = k | (0b011 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b100
    idx_2 = k | (0b100 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b101
    idx_2 = k | (0b101 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b110
    idx_2 = k | (0b110 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
    idx_1 = k | 0b111
    idx_2 = k | (0b111 | loc_bit)
    subspace_mul_X_test_swap!(st, idx_1, idx_2)
end

@inline @inbounds function subspace_mul_X_test_swap!(st::AbstractVector, idx_1::Int, idx_2::Int)
    tmp = st[idx_1+1]
    st[idx_1+1] = st[idx_2+1]
    st[idx_2+1] = tmp
end

ext: jl


text:
function (bha::BHAJ)(segments, trajectory, wobrange, inclrange)

  traj = update_trajectory(bha::BHAJ, trajectory)

  tpdict = Dict{Symbol, Any}(
    :ratio          => bha.ratio,
    :medium         => bha.medium,
    :maxiters       => bha.maxiters,
    :dlerrormax     => bha.dlerrormax,
    :drerrormax     => bha.drerrormax,
    :segments       => update_segments(bha::BHAJ, segments),
    :tprecords      => bha.tprecords,
    :name           => bha.name,
    :pdir           => bha.pdir,
    :debuglevel     => bha.debuglevel,
    :wobrange       => update_wobrange(wobrange),
    :inclrange      => update_inclrange(inclrange),
    :welltrajectory => Dict{Symbol, Float64}(
      :heading  => traj.heading,
      :diameter => traj.diameter
    )
  )

  tp!(tpdict)
end

function update_wobrange(wobs)
  collect(wobs)
end

function update_inclrange(incls)
  collect(incls)
end

function update_trajectory(bha::BHAJ, traj)
  Trajectory(Float64(traj[1][1]), Float64(traj[1][2]))
end

function createsurveyrecord(::Type{Val{BHAtp.Bit}}, depth, incl, heading, holediameter)
  return BitSurvey(depth, incl, heading, holediameter)
end

function createsurveyrecord(::Type{Val{BHAtp.Rig}}, depth, incl, heading, holediameter)
  return BitSurvey(depth, incl, heading, holediameter)
end

function createsurveyrecord(depth, incl, heading, holediameter)
  return HoleSurvey(depth, incl, heading, holediameter)
end

function update_surveys(bha::BHAJ, incl::String, heading::Float64, holediameter::Float64)
  # Create survey records based an bha and trajectory data
  # Notice first record is top of string
  surveys = Vector{SurveyType}()
  depth = 0.0
  for i in length(bha.segments):-1:1
    seg = bha.segments[i]
    if typeof(seg) != BHAtp.Stabilizer
      if typeof(seg) in [BHAtp.Bit, BHAtp.Rig]
        push!(surveys, createsurveyrecord(Val{typeof(seg)}, depth, float(incl),
          heading, holediameter))
      else
        push!(surveys, createsurveyrecord(depth, float(incl), heading, holediameter))
      end
      depth += seg.length
    end
  end
  surveys
end

function create_segment(::Type{Val{:bit}},
  seg::Tuple{Symbol, Float64, Float64, Float64, Float64})
  Bit(seg...)
end

function create_segment(::Type{Val{:pipe}},
  seg::Tuple{Symbol, Float64, Float64, Float64, Float64})
  Pipe(seg...)
end

function create_segment(::Type{Val{:collar}},
  seg::Tuple{Symbol, Float64, Float64, Float64, Float64})
  Collar(seg...)
end

function create_segment(::Type{Val{:stabilizer}},
  seg::Tuple{Symbol, Float64, Float64, Float64, Float64})
  Stabilizer(seg...)
end

function update_segments(bha::BHAJ, segs)
  segments = Vector{SegmentType}()
  for i in 1:length(segs)

  # e.g. (:bit, :steel, 0.00, 2.75, 9.0, 0.2),
    push!(segments, create_segment(Val{segs[i][1]}, (segs[i][2],
      segs[i][3], segs[i][4], segs[i][5], segs[i][6])))

  end
  segments
end


text:
"""
    test_constraint_get_ConstraintIndex(model::MOI.ModelLike, config::Config)

Test getting constraints by name.
"""
function test_constraint_get_ConstraintIndex(model::MOI.ModelLike, ::Config)
    MOIU.loadfromstring!(
        model,
        """
    variables: x
    minobjective: 2.0x
    c1: 1.0 * x >= 1.0
    c2: 1.0 * x <= 2.0
""",
    )
    F = MOI.ScalarAffineFunction{Float64}
    @test MOI.get(model, MOI.ConstraintIndex, "c3") === nothing
    @test MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c1") ===
          nothing
    @test MOI.get(
        model,
        MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}},
        "c2",
    ) === nothing
    c1 = MOI.get(model, MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}}, "c1")
    @test MOI.get(model, MOI.ConstraintIndex, "c1") == c1
    @test MOI.is_valid(model, c1)
    c2 = MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c2")
    @test MOI.get(model, MOI.ConstraintIndex, "c2") == c2
    @test MOI.is_valid(model, c2)
    return
end

"""
    test_constraint_ScalarAffineFunction_LessThan(
        model::MOI.ModelLike,
        config::Config,
    )

Add an ScalarAffineFunction-in-LessThan constraint.
"""
function test_constraint_ScalarAffineFunction_LessThan(
    model::MOI.ModelLike,
    config::Config,
)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{Float64},
        MOI.LessThan{Float64},
    )
    x = MOI.add_variable(model)
    c = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 0.0),
        MOI.LessThan(1.0),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0),
    )
    _test_model_solution(
        model,
        config;
        objective_value = 0.5,
        variable_primal = [(x, 0.5)],
        constraint_primal = [(c, 1.0)],
        constraint_dual = [(c, -0.5)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ScalarAffineFunction_LessThan),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5]),
            MOI.FEASIBLE_POINT,
            (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) =>
                [-0.5],
        ),
    )
    return
end

"""
    test_constraint_ScalarAffineFunction_GreaterThan(
        model::MOI.ModelLike,
        config::Config,
    )

Add an ScalarAffineFunction-in-GreaterThan constraint.
"""
function test_constraint_ScalarAffineFunction_GreaterThan(
    model::MOI.ModelLike,
    config::Config,
)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{Float64},
        MOI.GreaterThan{Float64},
    )
    x = MOI.add_variable(model)
    c = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 0.0),
        MOI.GreaterThan(1.0),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0),
    )
    _test_model_solution(
        model,
        config;
        objective_value = 0.5,
        variable_primal = [(x, 0.5)],
        constraint_primal = [(c, 1.0)],
        constraint_dual = [(c, 0.5)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ScalarAffineFunction_GreaterThan),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5]),
            MOI.FEASIBLE_POINT,
            (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) =>
                [0.5],
        ),
    )
    return
end

"""
    test_constraint_ScalarAffineFunction_EqualTo(
        model::MOI.ModelLike,
        config::Config,
    )

Add an ScalarAffineFunction-in-EqualTo constraint.
"""
function test_constraint_ScalarAffineFunction_EqualTo(
    model::MOI.ModelLike,
    config::Config,
)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{Float64},
        MOI.EqualTo{Float64},
    )
    x = MOI.add_variable(model)
    c = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 0.0),
        MOI.EqualTo(1.0),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0),
    )
    _test_model_solution(
        model,
        config;
        objective_value = 0.5,
        variable_primal = [(x, 0.5)],
        constraint_primal = [(c, 1.0)],
        constraint_dual = [(c, 0.5)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ScalarAffineFunction_EqualTo),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5]),
            MOI.FEASIBLE_POINT,
            (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) =>
                [0.5],
        ),
    )
    return
end

"""
    test_constraint_ScalarAffineFunction_Interval(
        model::MOI.ModelLike,
        config::Config,
    )

Add an ScalarAffineFunction-in-Interval constraint.
"""
function test_constraint_ScalarAffineFunction_Interval(
    model::MOI.ModelLike,
    config::Config,
)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{Float64},
        MOI.Interval{Float64},
    )
    x = MOI.add_variable(model)
    c = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 0.0),
        MOI.Interval(1.0, 4.0),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(3.0, x)], 0.0),
    )
    _test_model_solution(
        model,
        config;
        objective_value = 6.0,
        variable_primal = [(x, 2.0)],
        constraint_primal = [(c, 4.0)],
        constraint_dual = [(c, -1.5)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ScalarAffineFunction_Interval),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [2.0]),
            MOI.FEASIBLE_POINT,
            (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) =>
                [-1.5],
        ),
    )
    return
end

"""
    test_constraint_ScalarAffineFunction_duplicate(
        model::MOI.ModelLike,
        config::Config,
    )

Add a `ScalarAffineFunction`-in-`LessThan` constraint with duplicate terms in
the function.

Taken from https://github.com/JuliaOpt/MathOptInterfaceMosek.jl/issues/41
"""
function test_constraint_ScalarAffineFunction_duplicate(
    model::MOI.ModelLike,
    config::Config,
)
    x = MOI.add_variable(model)
    objective_function =
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0)
    MOI.set(
        model,
        MOI.ObjectiveFunction{typeof(objective_function)}(),
        objective_function,
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 1.0], [x, x]), 0.0)
    c = MOI.add_constraint(model, f, MOI.LessThan(1.0))
    _test_model_solution(
        model,
        config;
        objective_value = 0.5,
        variable_primal = [(x, 0.5)],
        constraint_primal = [(c, 1.0)],
        constraint_dual = [(c, -0.5)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ScalarAffineFunction_duplicate),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5]),
            MOI.FEASIBLE_POINT,
            (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) =>
                [-0.5],
        ),
    )
    return
end

"""
    test_constraint_VectorAffineFunction_duplicate(
        model::MOI.ModelLike,
        config::Config,
    )

Add a `VectorAffineFunction`-in-`Nonpositives` constraint with duplicate terms
in the function.
"""
function test_constraint_VectorAffineFunction_duplicate(
    model::MOI.ModelLike,
    config::Config,
)
    x = MOI.add_variable(model)
    objective_function =
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0)
    MOI.set(
        model,
        MOI.ObjectiveFunction{typeof(objective_function)}(),
        objective_function,
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    f = MOI.VectorAffineFunction(
        MOI.VectorAffineTerm.(1, MOI.ScalarAffineTerm.([1.0, 1.0], [x, x])),
        [-1.0],
    )
    c = MOI.add_constraint(model, f, MOI.Nonpositives(1))
    _test_model_solution(
        model,
        config;
        objective_value = 0.5,
        variable_primal = [(x, 0.5)],
        constraint_primal = [(c, [0.0])],
        constraint_dual = [(c, [-0.5])],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_VectorAffineFunction_duplicate),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5]),
            MOI.FEASIBLE_POINT,
            (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) =>
                [[-0.5]],
        ),
    )
    return
end

"""
    test_constraint_qcp_duplicate_diagonal(model::MOI.ModelLike, config::Config)

Test a QCP problem with a duplicate diagonal term.

The problem is `max x + 2y | y + x^2 + x^2 <= 1, x >= 0.5, y >= 0.5`.
"""
function test_constraint_qcp_duplicate_diagonal(
    model::MOI.ModelLike,
    config::Config,
)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{Float64},
        MOI.LessThan{Float64},
    )
    x = MOI.add_variables(model, 2)
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
        MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], x), 0.0),
    )
    vc1 = MOI.add_constraint(model, x[1], MOI.GreaterThan(0.5))
    # We test this after the creation of every `VariableIndex` constraint
    # to ensure a good coverage of corner cases.
    @test vc1.value == x[1].value
    vc2 = MOI.add_constraint(model, x[2], MOI.GreaterThan(0.5))
    @test vc2.value == x[2].value
    MOI.add_constraint(
        model,
        MOI.ScalarQuadraticFunction(
            MOI.ScalarQuadraticTerm.([2.0, 2.0], [x[1], x[1]], [x[1], x[1]]),  # quad
            MOI.ScalarAffineTerm.([1.0], [x[2]]),  # affine terms
            0.0,  # constant
        ),
        MOI.LessThan(1.0),
    )
    _test_model_solution(
        model,
        config;
        objective_value = 1.5,
        variable_primal = [(x[1], 0.5), (x[2], 0.5)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_qcp_duplicate_diagonal),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5, 0.5]),
        ),
    )
    return
end

"""
    test_constraint_qcp_duplicate_off_diagonal(model::MOI.ModelLike, config::Config)

Test a QCP problem with a duplicate off-diagonal term.

The problem is
`max x + 2y | x^2 + 0.25y*x + 0.25x*y + 0.5x*y + y^2 <= 1, x >= 0.5, y >= 0.5`.
"""
function test_constraint_qcp_duplicate_off_diagonal(
    model::MOI.ModelLike,
    config::Config,
)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{Float64},
        MOI.LessThan{Float64},
    )
    x = MOI.add_variables(model, 2)
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
        MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], x), 0.0),
    )
    vc1 = MOI.add_constraint(model, x[1], MOI.GreaterThan{Float64}(0.5))
    @test vc1.value == x[1].value
    vc2 = MOI.add_constraint(model, x[2], MOI.GreaterThan{Float64}(0.5))
    @test vc2.value == x[2].value
    MOI.add_constraint(
        model,
        MOI.ScalarQuadraticFunction(
            MOI.ScalarQuadraticTerm.(
                [2.0, 0.25, 0.25, 0.5, 2.0],
                [x[1], x[1], x[2], x[1], x[2]],
                [x[1], x[2], x[1], x[2], x[2]],
            ),  # quad
            MOI.ScalarAffineTerm{Float64}[],  # affine terms
            0.0,  # constant
        ),
        MOI.LessThan(1.0),
    )
    _test_model_solution(
        model,
        config;
        objective_value = 0.5 + (√13 - 1) / 2,
        variable_primal = [(x[1], 0.5), (x[2], (√13 - 1) / 4)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_qcp_duplicate_off_diagonal),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [0.5, (√13 - 1) / 4]),
        ),
    )
    return
end

"""
    test_constraint_ZeroOne_bounds(model::MOI.ModelLike, config::Config)

Test a problem with a bounded ZeroOne variable.
"""
function test_constraint_ZeroOne_bounds(model::MOI.ModelLike, config::Config)
    @requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.ZeroOne)
    MOIU.loadfromstring!(
        model,
        """
    variables: x
    maxobjective: 2.0x
    x in ZeroOne()
    x >= 0.0
    x <= 1.0
""",
    )
    x = MOI.get(model, MOI.VariableIndex, "x")
    _test_model_solution(
        model,
        config;
        objective_value = 2.0,
        variable_primal = [(x, 1.0)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ZeroOne_bounds),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) ->
            MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])),
    )
    return
end

"""
    test_constraint_ZeroOne_bounds_2(model::MOI.ModelLike, config::Config)

Test a problem with a ZeroOne and binding fractional upper bound.
"""
function test_constraint_ZeroOne_bounds_2(model::MOI.ModelLike, config::Config)
    @requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.ZeroOne)
    MOIU.loadfromstring!(
        model,
        """
    variables: x
    maxobjective: 2.0x
    x in ZeroOne()
    x >= 0.0
    x <= 0.5
""",
    )
    x = MOI.get(model, MOI.VariableIndex, "x")
    _test_model_solution(
        model,
        config;
        objective_value = 0.0,
        variable_primal = [(x, 0.0)],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_ZeroOne_bounds_2),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) ->
            MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])),
    )
    return
end

"""
    test_constraint_ZeroOne_bounds_3(model::MOI.ModelLike, config::Config)

Test a problem with a ZeroOne and infeasible fractional bounds.
"""
function test_constraint_ZeroOne_bounds_3(model::MOI.ModelLike, config::Config)
    @requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.ZeroOne)
    @requires _supports(config, MOI.optimize!)
    MOIU.loadfromstring!(
        model,
        """
    variables: x
    maxobjective: 2.0x
    x in ZeroOne()
    x >= 0.2
    x <= 0.5
""",
    )
    x = MOI.get(model, MOI.VariableIndex, "x")
    MOI.optimize!(model)
    @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
    return
end

function setup_test(
    ::typeof(test_constraint_ZeroOne_bounds_3),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.INFEASIBLE),
    )
    return
end

"""
    test_constraint_PrimalStart_DualStart_SecondOrderCone(
        model::MOI.ModelLike,
        config::Config{T},
    ) where {T}

Test combining the [`MOI.VariablePrimalStart`](@ref),
[`MOI.ConstraintPrimalStart`](@ref) and [`MOI.ConstraintDualStart`](@ref)
attributes with a `MOI.VectorAffineFunction{T}`-in-`MOI.SecondOrderCone`.
"""
function test_constraint_PrimalStart_DualStart_SecondOrderCone(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.SecondOrderCone,
    )
    @requires _supports(config, MOI.optimize!)
    x = MOI.add_variable(model)
    o = one(T)
    c = MOI.add_constraint(
        model,
        MOIU.operate(vcat, T, x, o),
        MOI.SecondOrderCone(2),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), x)
    if MOI.supports(model, MOI.VariablePrimalStart(), MOI.VariableIndex)
        MOI.set(model, MOI.VariablePrimalStart(), x, T(2))
    end
    if MOI.supports(model, MOI.ConstraintPrimalStart(), typeof(c))
        MOI.set(model, MOI.ConstraintPrimalStart(), c, T[2, 2])
    end
    if MOI.supports(model, MOI.ConstraintDualStart(), typeof(c))
        MOI.set(model, MOI.ConstraintDualStart(), c, T[2, -2])
    end
    _test_model_solution(
        model,
        config;
        objective_value = o,
        variable_primal = [(x, o)],
        constraint_primal = [(c, [o, o])],
        constraint_dual = [(c, [o, -o])],
    )
    return
end

function setup_test(
    ::typeof(test_constraint_PrimalStart_DualStart_SecondOrderCone),
    model::MOIU.MockOptimizer,
    ::Config,
)
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            MOI.OPTIMAL,
            (MOI.FEASIBLE_POINT, [1.0]),
            MOI.FEASIBLE_POINT,
            (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) =>
                [[1.0, -1.0]],
        ),
    )
    return
end

"""
    test_constraint_ConstraintPrimalStart(
        model::MOI.ModelLike,
        ::Config{T},
    ) where {T}

Test the `ConstraintPrimalStart` attribute.
"""
function test_constraint_ConstraintPrimalStart(
    model::MOI.ModelLike,
    ::Config{T},
) where {T}
    @requires MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.Nonnegatives,
    )
    @requires MOI.supports(
        model,
        MOI.ConstraintPrimalStart(),
        MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.Nonnegatives},
    )
    x = MOI.add_variable(model)
    f = MOI.VectorAffineFunction(
        [MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(one(T), x))],
        [0.0],
    )
    c = MOI.add_constraint(model, f, MOI.Nonnegatives(1))
    @test MOI.get(model, MOI.ConstraintPrimalStart(), c) === nothing
    MOI.set(model, MOI.ConstraintPrimalStart(), c, [-one(T)])
    @test MOI.get(model, MOI.ConstraintPrimalStart(), c) == [-one(T)]
    MOI.set(model, MOI.ConstraintPrimalStart(), c, nothing)
    @test MOI.get(model, MOI.ConstraintPrimalStart(), c) === nothing
    return
end

"""
    test_constraint_ConstraintDualStart(
        model::MOI.ModelLike,
        ::Config{T},
    ) where {T}

Test the `ConstraintDualStart` attribute.
"""
function test_constraint_ConstraintDualStart(
    model::MOI.ModelLike,
    ::Config{T},
) where {T}
    @requires MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.Nonnegatives,
    )
    @requires MOI.supports(
        model,
        MOI.ConstraintDualStart(),
        MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.Nonnegatives},
    )
    x = MOI.add_variable(model)
    f = MOI.VectorAffineFunction(
        [MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(one(T), x))],
        [0.0],
    )
    c = MOI.add_constraint(model, f, MOI.Nonnegatives(1))
    @test MOI.get(model, MOI.ConstraintDualStart(), c) === nothing
    MOI.set(model, MOI.ConstraintDualStart(), c, [-one(T)])
    @test MOI.get(model, MOI.ConstraintDualStart(), c) == [-one(T)]
    MOI.set(model, MOI.ConstraintDualStart(), c, nothing)
    @test MOI.get(model, MOI.ConstraintDualStart(), c) === nothing
    return
end

"""
    test_constraint_Indicator_ConstraintName(
        model::MOI.ModelLike,
        ::Config{T},
    ) where {T}

Test ConstraintName for indicator sets.
"""
function test_constraint_Indicator_ConstraintName(
    model::MOI.ModelLike,
    ::Config{T},
) where {T}
    @requires(
        MOI.supports_constraint(
            model,
            MOI.VectorAffineFunction{T},
            MOI.Indicator{MOI.ACTIVATE_ON_ONE,MOI.GreaterThan{T}},
        ),
    )
    x = MOI.add_variables(model, 2)
    MOI.add_constraint(model, x[1], MOI.ZeroOne())
    f = MOI.VectorAffineFunction(
        [
            MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x[1])),
            MOI.VectorAffineTerm(2, MOI.ScalarAffineTerm(2.0, x[2])),
        ],
        [0.0, 0.0],
    )
    s = MOI.Indicator{MOI.ACTIVATE_ON_ONE}(MOI.GreaterThan(1.0))
    c = MOI.add_constraint(model, f, s)
    MOI.set(model, MOI.ConstraintName(), c, "my_indicator")
    @test MOI.get(model, MOI.ConstraintName(), c) == "my_indicator"
    return
end

"""
    test_constraint_Indicator_ACTIVATE_ON_ONE(
        model::MOI.ModelLike,
        ::Config{T},
    ) where {T}

Test ACTIVATE_ON_ONE for indicator sets.
"""
function test_constraint_Indicator_ACTIVATE_ON_ONE(
    model::MOI.ModelLike,
    ::Config{T},
) where {T}
    @requires(
        MOI.supports_constraint(
            model,
            MOI.VectorAffineFunction{T},
            MOI.Indicator{MOI.ACTIVATE_ON_ONE,MOI.GreaterThan{T}},
        ),
    )
    x = MOI.add_variables(model, 2)
    MOI.add_constraint(model, x[1], MOI.ZeroOne())
    f = MOI.VectorAffineFunction(
        [
            MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x[1])),
            MOI.VectorAffineTerm(2, MOI.ScalarAffineTerm(2.0, x[2])),
        ],
        [0.0, 0.0],
    )
    s = MOI.Indicator{MOI.ACTIVATE_ON_ONE}(MOI.GreaterThan(1.0))
    c = MOI.add_constraint(model, f, s)
    @test MOI.get(model, MOI.ConstraintSet(), c) == s
    @test isapprox(MOI.get(model, MOI.ConstraintFunction(), c), f)
    return
end

"""
    test_constraint_Indicator_ACTIVATE_ON_ZERO(
        model::MOI.ModelLike,
        ::Config{T},
    ) where {T}

Test ACTIVATE_ON_ZERO for indicator sets.
"""
function test_constraint_Indicator_ACTIVATE_ON_ZERO(
    model::MOI.ModelLike,
    ::Config{T},
) where {T}
    @requires(
        MOI.supports_constraint(
            model,
            MOI.VectorAffineFunction{T},
            MOI.Indicator{MOI.ACTIVATE_ON_ONE,MOI.GreaterThan{T}},
        ),
    )
    x = MOI.add_variables(model, 2)
    MOI.add_constraint(model, x[1], MOI.ZeroOne())
    f = MOI.VectorAffineFunction(
        [
            MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x[1])),
            MOI.VectorAffineTerm(2, MOI.ScalarAffineTerm(2.0, x[2])),
        ],
        [0.0, 0.0],
    )
    s = MOI.Indicator{MOI.ACTIVATE_ON_ZERO}(MOI.GreaterThan(1.0))
    c = MOI.add_constraint(model, f, s)
    @test MOI.get(model, MOI.ConstraintSet(), c) == s
    @test isapprox(MOI.get(model, MOI.ConstraintFunction(), c), f)
    return
end


text:
function xc_version()
    varray = zeros(Cint, 3)
    ccall((:xc_version, libxc), Cvoid, (Ptr{Cint}, Ptr{Cint}, Ptr{Cint}),
          pointer(varray, 1), pointer(varray, 2), pointer(varray, 3))
    VersionNumber(varray[1], varray[2], varray[3])
end

function xc_version_string()
    s = ccall( (:xc_version_string, libxc), Ptr{UInt8}, () )
    return unsafe_string(s)
end


text:
mutable struct Masterproblem
    model::JuMP.Model
    consref_offtime
    consref_onepatient
    convexitycons
    lambda
    closingtime
    I
    env

end

mutable struct PricingProblem
        intID::Int64
        model::JuMP.Model
        xvars; yvars; tvars; kvars; gvars
        V; D; D_v; J; J_d; I; Ts; Te; Tdelta
        patients

        PricingProblem() = new()
end

mutable struct Sets
        Vp; Dp; Dv; J; Jd; I; Pg;
        Sets() = new()
end

mutable struct Subproblems
        pricingproblems::Dict{Int128,PricingProblem}
        sets::Dict{Int64,Sets}

        Subproblems() = new(Dict{String,PricingProblem}(),Dict{Int128,Sets}())
end

function addsets!(subproblems,patient,V,D,D_v,J,J_d,I,Ts,Te)
        println("addsets! is deprecated ")
        curset = PPSets()
        curset.patient = patient #TODO is this correct?
        curset.V = V
        curset.D = D
        curset.D_v = D_v
        curset.J = J
        curset.J_d = J_d
        curset.I = I
        curset.Ts = Ts
        curset.Te = Te

        curset.hash = hashSets(curset)

        subproblems.sets[patient] = curset
        return curset
end

function addPricingProblem(subproblems,patientID,sub::JuMP.Model,xvars,yvars,tvars,kvars,gvars,patient,)
        curPP = PricingProblem()
        curPP.intID = patientID
        curPP.model = sub
        curPP.xvars = xvars
        curPP.yvars = yvars
        curPP.tvars = tvars
        curPP.kvars = kvars
        curPP.gvars = gvars
        curPP.patients = [patient]

        subproblems.pricingproblems[patientID] = curPP
end

function getsubproblem(subproblems,patient)
    return subproblems.models[patient], subproblems.xvars[patient], subproblems.yvars[patient], subproblems.tvars[patient]
end


text:
import Pkg
using Test

function solver_test(name::Symbol)
    if string(name) in keys(Pkg.installed())
        ok = false
        try
            @eval import $name
            ok = true
        catch e
            @warn("The solver $name cannot be imported, run `] build $name`.")
        end
        if ok
            @testset "$name" begin
                include("$(lowercase(string(name)))_tests.jl")
            end
        end
    else
        @warn("The solver $name is not installed, run `] add $name`.")
    end
end

# LP solvers
solver_test(:GLPK)

# SOCP solvers
solver_test(:ECOS)

# SDP solvers (SOC is reformulated into SDP)
solver_test(:CSDP)
solver_test(:SDPA)

# SDP+SOC solvers
solver_test(:Mosek)
solver_test(:SeDuMi)
#solver_test(:SCS)


text:
module NewtonMethod

function newton_solver!(R, f!, ∂f∂p!, p, num_iters = 3, α = 1.0)
    # Array to store errors
    ε = zeros(num_iters);

    # Newton iteration loop
    for i in 1:num_iters
        # (Generalize to Jac-vec matrix-free method such as Krylov subspace for large problems.)

        # Compute residuals
        R    = f!(R, p)

        # Compute Jacobian
        ∂R∂p = ∂f∂p!(R, p)

        # Compute Newton step
        Δp   = ∂R∂p \ -R

        # Update state with relaxation factor
        p    = newton_update!(p, reshape(Δp, size(p)), α)

        # Error processing
        ε[i] = maximum(abs.(Δp))
        println("Newton step error L² norm: $(ε[i])")
    end
    R, p, ε
end

max_stars_repo_name: GodotMisogi/AdjointCFD

max_issues_repo_name: GodotMisogi/AdjointCFD

max_forks_repo_name: GodotMisogi/AdjointCFD


text:
using ForwardDiff, StaticArrays, NeighbourLists
using Test, LinearAlgebra, Printf

# uncomment for testing from editor/file
# include("test_aux.jl")

let rcut = 3.0

fn, fn_d = gen_fnbody(rcut)

function naive_n_body(X::Vector{SVec{T}}, f, M, rcut) where {T}
   N = length(X)
   E = 0.0
   cnt = 0
   start = CartesianIndex(ntuple(_->1, M))
   stop = CartesianIndex(ntuple(_->N, M))
   for j in CartesianIndices(start, stop)
      s = zeros(T, (M*(M-1))÷2); n = 0
      for a = 1:M-1, b = a+1:M
         n += 1
         s[n] = norm(X[j[a]] - X[j[b]])
      end
      if 0 < minimum(s) && maximum(s) <= rcut
         # each of these terms occur factorial(M) times, i.e. for
         # every permutation of the indices!
         E += f(s) / factorial(M)
      end
   end
   return E
end

vecs(V::Vector{T}) where {T} = reinterpret(SVector{3,T}, V, (length(V) ÷ 3,))
mat(V::Vector{SVector{3,T}}) where {T} = reinterpret(T, V, (3, length(V)))

# NOT NEEDED - WE DO FINITE-DIFFERENCE TESTS INSTEAD!
# function grad_naive_n_body(X, f, M, rcut)
#    x = mat(X)[:]
#    g = ForwardDiff.gradient( y -> naive_n_body(vecs(y), f, M, rcut),  x)
#    return vecs(g)
# end

println("--------------------------------------")
println("    Testing NBodyIterator")
println("--------------------------------------")
println("Check that the energy is consistent with a naive implementation")
MM = [2,2,3,3,3,4,4,5]  # body orders
println("   N     Nat    =>   |Emr-Enaive|")
for M in MM
   # create a not-too-large copper cell
   X, C, _ = rand_config(2)
   nat = length(X)

   # assemble energy via neighbourlist and map-reduce
   Emr = n_body(X, fn, M, rcut, C)
   # assemble energy naively
   Enaive = naive_n_body(X, fn, M, rcut)

   println("   $M      $nat    =>   $(abs(Emr - Enaive))")
   @test Emr ≈ Enaive
end

println("--------------------------------------")
println("Finite-difference tests")
MM = [2,3,4,5]  # body orders
for M in MM
   println(" [M = $M]")
   X, C, pbc = rand_config(2)
   if M > 3
      pbc = [false, false, false]
   end
   @show pbc
   nat = length(X)
   dE = grad_n_body(X, fn_d, M, rcut, C, pbc)
   dE = mat(dE)[:]
   E = n_body(X, fn, M, rcut, C, pbc)
   @printf("    h    | error \n")
   @printf("---------|----------- \n")
   x = mat(X)[:]
   errors = []
   for p = 2:11
      h = 0.1^p
      dEh = copy(dE)
      for n = 1:length(dE)
         x[n] += h
         dEh[n] = (n_body(vecs(x), fn, M, rcut, C, pbc) - E) / h
         x[n] -= h
      end
      push!(errors, vecnorm(dE - dEh, Inf))
      @printf(" %1.1e | %4.2e  \n", h, errors[end])
   end
   @test minimum(errors) <= 1e-3 * maximum(errors)
end

end # let block


text:
using NamedTupleTools
using Test

ntproto1 = namedtuple(:a, :b, :c, :d)
ntproto2 = namedtuple(:a, :b)

@test ntproto1 == NamedTuple{(:a, :b, :c, :d),T} where T<:Tuple

nt1 = ntproto1(1, 2, 3, 4)
nt2 = ntproto2("one", "two")

@test isprototype(ntproto1) === true
@test isprototype(nt1) === false

@test nt2 === (a = "one", b = "two")
@test ntproto2(nt1) === (a = 1, b = 2)

@test delete(ntproto1, :a) === NamedTuple{(:b, :c, :d),T} where T<:Tuple
@test delete(ntproto1, :a, :c) === NamedTuple{(:b, :d),T} where T<:Tuple
@test delete(ntproto1, (:b, :c)) === NamedTuple{(:a, :d),T} where T<:Tuple

@test merge(nt1, nt2) === (a = "one", b  = "two", c = 3, d = 4)

struct MyStruct
    tally::Int
    team::String
end

mystruct = MyStruct(5, "hometeam")
mynamedtuple = ntfromstruct(mystruct)

@test mynamedtuple == (tally = 5, team = "hometeam")

nt = (tally=5, team="hometeam")
ntstruct = structfromnt(MyStruct, nt)

@test ntstruct == mystruct

dict = Dict(:a=>1, :b=>2//11, :c=>"three")
nt = NamedTuple(dict)

@test Dict(nt) == dict

vec_pair = [:a => 1, :b => 2]
nt = (a = 1, b = 2)
@test namedtuple(vec_pair) == nt


text:
__precompile__()
module FastTransforms

using ToeplitzMatrices, HierarchicalMatrices, LowRankApprox, ProgressMeter, Compat,
        AbstractFFTs, SpecialFunctions

if VERSION < v"0.7-"
    using Base.FFTW
    import Base.FFTW: r2rFFTWPlan, unsafe_execute!, fftwSingle, fftwDouble, fftwNumber
    import Base.FFTW: libfftw, libfftwf, PlanPtr, r2rFFTWPlan, plan_r2r!,
                        REDFT00, REDFT01, REDFT10, REDFT11,
                        RODFT00, RODFT01, RODFT10, RODFT11
    const LAmul! = Base.A_mul_B!
    import Base: Factorization
    rmul!(A::AbstractArray, c::Number) = scale!(A,c)
    lmul!(c::Number, A::AbstractArray) = scale!(c,A)
    lmul!(A::AbstractArray, B::AbstractArray) = mul!(A,B)
    rmul!(A::AbstractArray, B::AbstractArray) = mul!(A,B)
    const floatmin = realmin
else
    using FFTW, LinearAlgebra, DSP
    import FFTW: r2rFFTWPlan, unsafe_execute!, fftwSingle, fftwDouble, fftwNumber
    import FFTW: libfftw3, libfftw3f, PlanPtr, r2rFFTWPlan, plan_r2r!,
                    REDFT00, REDFT01, REDFT10, REDFT11,
                    RODFT00, RODFT01, RODFT10, RODFT11
    const LAmul! = LinearAlgebra.mul!
    const libfftw = libfftw3
    const libfftwf = libfftw3f
    import LinearAlgebra: Factorization
    flipdim(A,d) = reverse(A; dims=d)
end

import Base: *, \, inv, size, view
import Base: getindex, setindex!, length
import Compat.LinearAlgebra: BlasFloat, BlasInt
import HierarchicalMatrices: HierarchicalMatrix, unsafe_broadcasttimes!
import HierarchicalMatrices: mul!, At_mul_B!, Ac_mul_B!
import HierarchicalMatrices: ThreadSafeVector, threadsafezeros
import LowRankApprox: ColPerm
import AbstractFFTs: Plan
import Compat: range, transpose, adjoint, axes

export cjt, icjt, jjt, plan_cjt, plan_icjt
export leg2cheb, cheb2leg, leg2chebu, ultra2ultra, jac2jac, plan_jac2jac
export normleg2cheb, cheb2normleg, normleg12cheb2, cheb22normleg1
export plan_leg2cheb, plan_cheb2leg
export plan_normleg2cheb, plan_cheb2normleg
export plan_normleg12cheb2, plan_cheb22normleg1

export gaunt

export nufft, nufft1, nufft2, nufft3, inufft1, inufft2
export plan_nufft, plan_nufft1, plan_nufft2, plan_nufft3, plan_inufft1, plan_inufft2

export paduatransform, ipaduatransform, paduatransform!, ipaduatransform!, paduapoints
export plan_paduatransform!, plan_ipaduatransform!

export SlowSphericalHarmonicPlan, FastSphericalHarmonicPlan, ThinSphericalHarmonicPlan
export sph2fourier, fourier2sph, plan_sph2fourier
export sphones, sphzeros, sphrand, sphrandn, sphevaluate

export SlowTriangularHarmonicPlan
export tri2cheb, cheb2tri, plan_tri2cheb
export triones, trizeros, trirand, trirandn, trievaluate

# Other module methods and constants:
#export ChebyshevJacobiPlan, jac2cheb, cheb2jac
#export sqrtpi, pochhammer, stirlingseries, stirlingremainder, Aratio, Cratio, Anαβ
#export Cnmαβ, Cnαβ, Cnmλ, Cnλ, Λ, absf, findmindices!
#export plan_clenshawcurtis, clenshawcurtisnodes, clenshawcurtisweights
#export plan_fejer1, fejernodes1, fejerweights1
#export plan_fejer2, fejernodes2, fejerweights2
#export RecurrencePlan, forward_recurrence!, backward_recurrence

include("stepthreading.jl")
include("fftBigFloat.jl")
include("specialfunctions.jl")
include("chebyshevtransform.jl")
include("clenshawcurtis.jl")
include("fejer.jl")
include("recurrence.jl")
include("PaduaTransform.jl")

abstract type FastTransformPlan{D,T} end

include("ChebyshevJacobiPlan.jl")
include("jac2cheb.jl")
include("cheb2jac.jl")

include("ChebyshevUltrasphericalPlan.jl")
include("ultra2cheb.jl")
include("cheb2ultra.jl")
include("nufft.jl")
include("inufft.jl")

include("cjt.jl")

include("toeplitzhankel.jl")

#leg2cheb(x...)=th_leg2cheb(x...)
#cheb2leg(x...)=th_cheb2leg(x...)
leg2chebu(x...) = th_leg2chebu(x...)
ultra2ultra(x...) = th_ultra2ultra(x...)
jac2jac(x...) = th_jac2jac(x...)
plan_jac2jac(x...) = th_jac2jacplan(x...)
plan_ultra2ultra(x...) = th_ultra2ultraplan(x...)

include("hierarchical.jl")
include("SphericalHarmonics/SphericalHarmonics.jl")
include("TriangularHarmonics/TriangularHarmonics.jl")

include("gaunt.jl")

include("precompile.jl")
_precompile_()

end # module


text:
using BenchmarkTools
using GeoPhyInv
using Test


text:
@testset "model.atom" begin

@testset "atom" begin
    @test atomnumber(:Fe) == 26

    sdfa = SDFileAtom(:Fe, 2, 1, nothing, [1.0, 2.0, 0.0], :anticlockwise)
    @test sdfa.symbol === :Fe
    @test atomnumber(sdfa) == 26
    @test sdfa.charge == 2
    @test sdfa.multiplicity == 1
    @test sdfa.coords == [1.0, 2.0, 0.0]
    @test sdfa.stereo === :anticlockwise

    smia = SmilesAtom(:C, 0, 1, 13.0, false, :clockwise)
    @test smia.symbol === :C
    @test atomnumber(smia) == 6
    @test smia.charge === 0
    @test smia.multiplicity == 1
    @test !smia.isaromatic
    @test smia.stereo === :clockwise
end

end # model.atom


text:
# ;; -*- mode: Scheme; tab-width: 2; -*- ;;
#
# ;; {{{ Data types
#
# (foreign-declare
# 	"#include <callback.h>\n"
# 	"#include <locale.h>\n"
# 	"#include <im.h>\n"
# 	"#include <im_binfile.h>\n"
# 	"#include <iup.h>\n"
# 	"#include <iupim.h>\n"
# 	"typedef struct Iclass_ Iclass;\n"
# 	"struct Ihandle_ { char sig[4]; Iclass *iclass; /* ... */ } ;\n"
# 	"extern char *iupClassCallbackGetFormat(Iclass *iclass, const char *name);\n")
#
# (define *ihandle-tag* "Ihandle")
# (define ihandle? (cut tagged-pointer? <> *ihandle-tag*))
#
# (define (ihandle->pointer nonnull?)
# 	(if nonnull?
# 		(lambda (handle)
# 			(ensure ihandle? handle)
# 			handle)
# 		(lambda (handle)
# 			(ensure (disjoin not ihandle?) handle)
# 			handle)))
#
# (define (pointer->ihandle nonnull?)
# 	(if nonnull?
# 		(lambda (handle)
# 			(ensure pointer? handle)
# 			(tag-pointer handle *ihandle-tag*))
# 		(lambda (handle)
# 			(and handle (tag-pointer handle *ihandle-tag*)))))
#
# # (define (ihandle-list->pointer-vector lst)
# # 	(let ([ptrs (make-pointer-vector (add1 (length lst)) #f)])
# # 		(do-ec (:list handle (index i) lst)
# # 			(begin
# # 				(ensure ihandle? handle)
# # 				(pointer-vector-set! ptrs i handle)))
# # 		ptrs))
#
# (define (istatus->integer status)
# 	(case status
# 		[(error)                 +1]
# 		[(opened invalid ignore) -1]
# 		[(default)               -2]
# 		[(close #f)              -3]
# 		[(continue)              -4]
# 		[else                    (if (integer? status) status 0)]))
#
# (define (integer->istatus status)
# 	(case status
# 		[(+1) 'error]
# 		[( 0) #t]
# 		[(-1) 'ignore]
# 		[(-2) 'default]
# 		[(-3) #f]
# 		[(-4) 'continue]
# 		[else status]))
#
# (define (iname->string default-case)
# 	(let ([change-case
# 	       (case default-case
# 	       	 [(upcase)   string-upcase]
# 	       	 [(downcase) string-downcase]
# 	       	 [else       (error 'iname->string "unsupported default case" default-case)])])
# 		(lambda (name)
# 			(cond
# 				[(or (not name) (string? name))
# 				 name]
# 				[(symbol? name)
# 				 (change-case (string-translate (symbol->string name) #\- #\_))]
# 				[else
# 				 (error 'iname->string "bad name" name)]))))
#
# (define (string->iname default-case)
# 	(let ([specials
# 	       (irregex
# 	       	 (case default-case
# 	       	 	 [(upcase)   "[-a-z]"]
# 	       	 	 [(downcase) "[-A-Z]"]
# 	       	 	 [else       (error 'string->iname "unsupported default case" default-case)]))])
# 		(lambda (name)
# 			(cond
# 				[(or (not name) (irregex-search specials name))
# 				 name]
# 				[else
# 				 (string->symbol (string-downcase (string-translate name #\_ #\-)))]))))
#
# (include "iup-types.scm")
#
# ;; }}}
#
# ;; {{{ Support macros and functions
#
# (define-syntax :children
# 	(syntax-rules ()
# 		[(:children cc child handle)
# 		 (:do cc ([child (child-ref handle 0)]) child ((sibling child)))]))
#
# (define-syntax optional-args
# 	(syntax-rules ()
# 		[(optional-args [name default] ...)
# 		 (lambda (args) (let-optionals args ([name default] ...) (list name ...)))]))
#
# (define ((make-constructor-procedure proc #!key [apply-args values]) . args)
# 	(let more ([keys '()] [key-args '()] [pos-args '()] [rest args])
# 		(cond
# 			[(null? rest)
# 			 (let ([handle (apply proc (apply-args (reverse! pos-args)))])
# 			 	 (do-ec (:parallel (:list key keys) (:list arg key-args))
# 			 	 	 ((if (procedure? arg) callback-set! attribute-set!) handle key arg))
# 			 	 handle)]
# 			[(keyword? (car rest))
# 			 (more
# 			 	 (cons (car rest) keys) (cons (cadr rest) key-args) pos-args
# 			 	 (cddr rest))]
# 			[else
# 			 (more
# 			 	 keys key-args (cons (car rest) pos-args)
# 			 	 (cdr rest))])))
#
# ;; }}}
#
# ;; {{{ System functions
#
# (define iup-version
# 	(foreign-lambda c-string "IupVersion"))
function version()
	ccall((:IupVersion, "libiup"), AbstractString, ())
end

# (define load/led
# 	(letrec ([load/raw (foreign-lambda c-string "IupLoad" c-string)])
# 		(lambda (file)
# 			(and-let* ([status (load/raw file)])
# 				(error 'load/led status))
	# 			(void)))
# 	(letrec ([load/raw (foreign-lambda c-string "IupLoad" c-string)])
# 		(lambda (file)
# 			(and-let* ([status (load/raw file)])
# 				(error 'load/led status))
# 			(void))))

function load_led(filename::AbstractString)
	ccall((:IupLoad, "libiup"), AbstractString, (AbstractString,), filename)
end

# ;; }}}
#
# ;; {{{ Attribute functions

# (define attribute-set!
#   (letrec ([set/string! (foreign-safe-lambda void "IupStoreAttribute" ihandle iname/upcase c-string)]
#            [set/handle! (foreign-safe-lambda void "IupSetAttributeHandle" ihandle iname/upcase ihandle)])
#     (lambda (handle name value)
#     	(cond
#     		[(or (not value) (string? value))
#          (set/string! handle name value)]
#         [(ihandle? value)
#          (set/handle! handle name value)]
#         [(boolean? value)
#          (set/string! handle name (if value "YES" "NO"))]
#         [else
#          (set/string! handle name (->string value))]))))

# name is supposed to be of type iname/upcase - however, that seems to be an alias for c-string
function attribute_set(handle::IHandle, name::AbstractString, value::AbstractString)
	ccall((:IupStoreAttribute), Void, (IHandle, IName, AbstractString), handle, name, value)
end

function attribute_set(handle::IHandle, name::AbstractString)
	attribute_set(handle, name, "")
end

function attribute_set(handle::IHandle, name::AbstractString, value::IHandle)
	ccall((:IupSetAttributeHandle), Void, (IHandle, IName, IHandle), handle, name, value)
end

function attribute_set(handle::IHandle, name::AbstractString, value::Boolean)
	if value
		attribute_set(handle, name, "YES")
	else
		attribute_set(handle, name, "NO")
	end
end

function attribute_set(handle::IHandle, name::AbstractString, value)
	attribute_set(handle, name, string(value))
end

#(define attribute-reset!
# 	(foreign-safe-lambda void "IupResetAttribute" ihandle iname/upcase))

function attribute_reset(handle::IHandle, name::AbstractString)
	ccall((:IupResetAttribute, "libiup"), Void, (IHandle, AbstractString), handle, name)
end

(define attribute
  (getter-with-setter
  	(foreign-safe-lambda c-string "IupGetAttribute" ihandle iname/upcase)
  	attribute-set!))

(define attributes
	(foreign-primitive scheme-object ([ihandle handle])
		"int n = IupGetAllAttributes(handle, NULL, 0);"
		"if (n > 0) {"
		"  char **buf = (char **) alloca(n * sizeof(char *));"
		"  if (IupGetAllAttributes(handle, buf, n) == n) {"
		"    int i, m = C_SIZEOF_LIST(n);"
		"    for (i = 0; i < n; ++i) m += C_SIZEOF_STRING(strlen(buf[i]));"
		"    C_word *mrk = C_alloc(m), lst = C_SCHEME_END_OF_LIST;"
		"    for (i = n-1; i >= 0; --i) lst = C_pair(&mrk, C_string2(&mrk, buf[i]), lst);"
		"    C_return(lst);"
		"  }"
		"}"
		"C_return(C_SCHEME_END_OF_LIST);"))

(define handle-name-set!
	(letrec ([handle-set! (foreign-lambda ihandle "IupSetHandle" iname/downcase ihandle)])
		(lambda (handle name)
			(handle-set! (or name (handle-name handle)) (and name handle)))))

(define handle-name
  (getter-with-setter
  	(foreign-lambda iname/downcase "IupGetName" nonnull-ihandle)
  	handle-name-set!))

(define handle-ref
	(foreign-lambda ihandle "IupGetHandle" iname/downcase))

;; }}}

;; {{{ Event functions

(define main-loop
	(letrec ([loop (foreign-safe-lambda istatus "IupMainLoop")])
		(lambda ()
			(let ([status (loop)])
				(case status
					[(#t) (void)]
					[else (error 'main-loop (format "error in IUP main loop (~s)" status))])))))

(define main-loop-step
  (letrec ([loop-step (foreign-safe-lambda istatus "IupLoopStep")]
           [loop-step/wait (foreign-safe-lambda istatus "IupLoopStepWait")])
    (lambda (poll?)
      (let ([status ((if poll? loop-step loop-step/wait))])
        (case status
          [(error) (error 'main-loop-step "error in IUP main loop")]
          [else    status])))))

(define main-loop-level
	(foreign-lambda int "IupMainLoopLevel"))

(define main-loop-exit
	(foreign-lambda void "IupExitLoop"))

(define main-loop-flush
	(foreign-safe-lambda void "IupFlush"))

(define make-wrapper
	(let ([make-wrapper/data
				 (foreign-lambda* c-pointer ([c-pointer data])
					 "C_return(alloc_callback(&callback_entry, data));")])
		(lambda (id)
			(make-wrapper/data (address->pointer id)))))

(define wrapper-id
	(let ([wrapper-data
				 (foreign-lambda* c-pointer ([c-pointer proc])
					 "C_return((proc && is_callback(proc) ? callback_data(proc) : NULL));")])
		(lambda (proc)
			(cond
			 [(wrapper-data proc) => pointer->address]
			 [else #f]))))

(define wrapper-destroy!
	(foreign-lambda void "free_callback" c-pointer))

(define-values (registry-ref registry-add! registry-remove! registry-clear!)
	(let ([registry (make-hash-table = number-hash)]
				[id-range (fxshr most-positive-fixnum 1)])
		(values
		 (lambda (id)
			 (cdr (hash-table-ref registry id)))
		 (lambda (handle data)
			 (let retry ()
				 (let ([id (fxior (fxshl (random id-range) 1) 1)])
					 (if (hash-table-exists? registry id)
							 (retry)
							 (let ([proc (make-wrapper id)])
								 (hash-table-set!
									registry id
									(cons proc data))
								 (hash-table-update!/default
									registry (pointer->address handle)
									(cut cons id <>) '())
								 proc)))))
		 (lambda (handle proc)
			 (cond
				[(wrapper-id proc)
				 => (lambda (id)
							(hash-table-update!/default
							 registry (pointer->address handle)
							 (cut delete id <> =) '())
							(hash-table-delete! registry id)
							(wrapper-destroy! proc))]))
		 (lambda (handle)
			 (let* ([key (pointer->address handle)]
							[ids (hash-table-ref/default registry key '())])
				 (hash-table-delete! registry key)
				 (do-ec (:list id ids)
					 (let ([proc (car (hash-table-ref registry id))])
						 (hash-table-delete! registry id)
						 (wrapper-destroy! proc))))))))

(define-external (callback_entry [c-pointer cid] [c-pointer frame]) void
	(define frame-start/ubyte!
		(foreign-lambda* void ([c-pointer frame]) "va_start_uchar((va_alist)frame);"))
	(define frame-start/int!
		(foreign-lambda* void ([c-pointer frame]) "va_start_int((va_alist)frame);"))
	(define frame-start/float!
		(foreign-lambda* void ([c-pointer frame]) "va_start_float((va_alist)frame);"))
	(define frame-start/double!
		(foreign-lambda* void ([c-pointer frame]) "va_start_double((va_alist)frame);"))
	(define frame-start/pointer!
		(foreign-lambda* void ([c-pointer frame]) "va_start_ptr((va_alist)frame, void *);"))

	(define frame-arg/ubyte!
		(foreign-lambda* unsigned-byte ([c-pointer frame]) "C_return(va_arg_uchar((va_alist)frame));"))
	(define frame-arg/int!
		(foreign-lambda* int ([c-pointer frame]) "C_return(va_arg_int((va_alist)frame));"))
	(define frame-arg/float!
		(foreign-lambda* float ([c-pointer frame]) "C_return(va_arg_float((va_alist)frame));"))
	(define frame-arg/double!
		(foreign-lambda* double ([c-pointer frame]) "C_return(va_arg_double((va_alist)frame));"))
	(define frame-arg/string!
		(foreign-lambda* c-string ([c-pointer frame]) "C_return(va_arg_ptr((va_alist)frame, char *));"))
	(define frame-arg/pointer!
		(foreign-lambda* c-pointer ([c-pointer frame]) "C_return(va_arg_ptr((va_alist)frame, void *));"))
	(define frame-arg/handle!
		(foreign-lambda* ihandle ([c-pointer frame]) "C_return(va_arg_ptr((va_alist)frame, Ihandle *));"))

	(define frame-return/ubyte!
		(foreign-lambda* void ([c-pointer frame] [unsigned-byte ret]) "va_return_uchar((va_alist)frame, ret);"))
	;(define frame-return/int!
	;	(foreign-lambda* void ([c-pointer frame] [int ret]) "va_return_int((va_alist)frame, ret);"))
	(define frame-return/status!
		(foreign-lambda* void ([c-pointer frame] [istatus ret]) "va_return_int((va_alist)frame, ret);"))
	(define frame-return/float!
		(foreign-lambda* void ([c-pointer frame] [float ret]) "va_return_float((va_alist)frame, ret);"))
	(define frame-return/double!
		(foreign-lambda* void ([c-pointer frame] [double ret]) "va_return_double((va_alist)frame, ret);"))
	(define frame-return/pointer!
		(foreign-lambda* void ([c-pointer frame] [c-pointer ret]) "va_return_ptr((va_alist)frame, void *, ret);"))
	(define frame-return/handle!
		(foreign-lambda* void ([c-pointer frame] [ihandle ret]) "va_return_ptr((va_alist)frame, Ihandle *, ret);"))

	(call-with-current-continuation
	 (lambda (return)
		 (let ([sig "i"] [ret? #f])
			 (dynamic-wind
				 void
				 (lambda ()
					 (let* ([data (registry-ref (pointer->address cid))]
									[proc (cdr data)])
						 (set! sig (car data))
						 (case (string-ref sig 0)
							 [(#\b)         (frame-start/ubyte! frame)]
							 [(#\i)         (frame-start/int! frame)]
							 [(#\f)         (frame-start/float! frame)]
							 [(#\d)         (frame-start/double! frame)]
							 [(#\v #\C #\h) (frame-start/pointer! frame)])
						 (let ([args (list-ec (:string chr "h" (string-drop sig 1))
													 (case chr
														 [(#\b)     (frame-arg/ubyte! frame)]
														 [(#\i)     (frame-arg/int! frame)]
														 [(#\f)     (frame-arg/float! frame)]
														 [(#\d)     (frame-arg/double! frame)]
														 [(#\s)     (frame-arg/string! frame)]
														 [(#\v #\C) (frame-arg/pointer! frame)]
														 [(#\h)     (frame-arg/handle! frame)]))])
							 (handle-exceptions exn
								 (print-error-message exn (current-error-port) "Error: in callback")
								 (let ([ret (apply proc args)])
									 (case (string-ref sig 0)
										 [(#\b)     (frame-return/ubyte! frame ret)]
										 [(#\i)     (frame-return/status! frame ret)]
										 [(#\f)     (frame-return/float! frame ret)]
										 [(#\d)     (frame-return/double! frame ret)]
										 [(#\v #\C) (frame-return/pointer! frame ret)]
										 [(#\h)     (frame-return/handle! frame ret)])
									 (set! ret? #t))))))
				 (lambda ()
					 (unless ret?
						 (case (string-ref sig 0)
							 [(#\b)         (frame-return/ubyte! frame 0)]
							 [(#\i)         (frame-return/status! frame 'continue)]
							 [(#\f)         (frame-return/float! frame 0.0)]
							 [(#\d)         (frame-return/double! frame 0.0)]
							 [(#\v #\C #\h) (frame-return/pointer! frame #f)]))
					 (return (void))))))))

(define-values (callback-set! callback)
	(letrec ([signature/raw
						(foreign-lambda* c-string ([nonnull-ihandle handle] [iname/upcase name])
							"C_return(iupClassCallbackGetFormat(handle->iclass, name));")]
					 [set/pointer!
					  (foreign-lambda c-pointer "IupSetCallback" nonnull-ihandle iname/upcase c-pointer)]
					 [get/pointer
					  (foreign-lambda c-pointer "IupGetCallback" nonnull-ihandle iname/upcase)]
					 [sigils
					  (irregex "([bifdsvCh]*)(?:=([bifdvCh]))?")]
					 [callback-set!
					  (lambda (handle name proc)
					  	(let* ([sig
					  	        (cond
												[(irregex-match sigils (or (signature/raw handle name) ""))
												 => (lambda (groups)
															(string-append
																(or (irregex-match-substring groups 2) "i")
																(irregex-match-substring groups 1)))]
												[else
												 (error 'callback-set! "callback has bad signature" handle name)])]
					  			   [new
					  	        (cond
					  	        	[(or (not proc) (pointer? proc)) proc]
					  	        	[else (registry-add! handle (cons sig proc))])])
								(cond
								 [(set/pointer! handle name new) =>
									(cut registry-remove! handle <>)])))]
					 [callback
					  (lambda (handle name)
					  	(let ([proc (get/pointer handle name)])
					  		(cond
					  			[(wrapper-id proc)
									 => (lambda (id)
												(cdr (registry-ref id)))]
					  			[else proc])))])
		(values
			callback-set!
			(getter-with-setter callback callback-set!))))

;; }}}

;; {{{ Layout functions

(define create
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupCreate" iname/downcase)))

(define destroy!
  (letrec ([collect-handles
            (lambda (handle acc)
							(cons
							 handle
							 (fold-ec acc (:children child handle)
								 child collect-handles)))]
           [handle-destroy!
            (foreign-safe-lambda void "IupDestroy" nonnull-ihandle)])
    (lambda (handle)
			(let ([handles (collect-handles handle '())])
				(handle-destroy! handle)
				(main-loop-flush)
				(for-each registry-clear! handles)))))

(define map-peer!
	(letrec ([map-peer/raw! (foreign-safe-lambda istatus "IupMap" nonnull-ihandle)])
		(lambda (handle)
			(let ([status (map-peer/raw! handle)])
				(case status
					[(#t) (void)]
					[else (error 'map-peer! (format "failed to map peer (~s)" status) handle)])))))

(define unmap-peer!
	(foreign-safe-lambda void "IupUnmap" nonnull-ihandle))

(define class-name
	(foreign-lambda iname/downcase "IupGetClassName" nonnull-ihandle))

(define class-type
	(foreign-lambda iname/downcase "IupGetClassType" nonnull-ihandle))

(define save-attributes!
	(foreign-lambda void "IupSaveClassAttributes" nonnull-ihandle))

(define parent
	(foreign-lambda ihandle "IupGetParent" nonnull-ihandle))

(define parent-dialog
	(foreign-lambda ihandle "IupGetDialog" nonnull-ihandle))

(define sibling
	(foreign-lambda ihandle "IupGetBrother" nonnull-ihandle))

(define child-add!
  (letrec ([append! (foreign-safe-lambda ihandle "IupAppend" nonnull-ihandle nonnull-ihandle)]
           [insert! (foreign-safe-lambda ihandle "IupInsert" nonnull-ihandle nonnull-ihandle nonnull-ihandle)])
    (lambda (child container #!optional [anchor #f])
      (or (if anchor
              (insert! container anchor child)
              (append! container child))
					(error 'child-add! "failed to add child" child container anchor)))))

(define child-remove!
	(foreign-safe-lambda void "IupDetach" nonnull-ihandle))

(define child-move!
	(letrec ([move! (foreign-safe-lambda istatus "IupReparent" nonnull-ihandle nonnull-ihandle ihandle)])
		(lambda (child parent #!optional ref-child)
			(let ([status (move! child parent ref-child)])
				(case status
					[(#t) (void)]
					[else (error 'child-move! (format "failed to move child (~s)" status) child parent)])))))

(define child-ref
  (letrec ([ref/position (foreign-lambda ihandle "IupGetChild" nonnull-ihandle int)]
           [ref/name (foreign-lambda ihandle "IupGetDialogChild" nonnull-ihandle iname/upcase)])
    (lambda (container id)
      ((if (integer? id) ref/position ref/name) container id))))

(define child-pos
	(letrec ([pos/raw (foreign-lambda int "IupGetChildPos" nonnull-ihandle nonnull-ihandle)])
		(lambda (parent child)
			(let ([pos (pos/raw parent child)])
				(and (not (negative? pos)) pos)))))

(define child-count
	(foreign-lambda int "IupGetChildCount" nonnull-ihandle))

(define (children handle)
	(list-ec (:children child handle) child))

(define refresh
	(foreign-safe-lambda void "IupRefresh" nonnull-ihandle))

(define redraw
	(letrec ([update
	          (foreign-safe-lambda* void ([nonnull-ihandle handle] [bool children])
	          	"IupUpdate(handle); if (children) IupUpdateChildren(handle);")]
	         [update/sync
	          (foreign-safe-lambda void "IupRedraw" nonnull-ihandle bool)])
	  (lambda (handle #!key [children? #f] [sync? #f])
	  	((if sync? update/sync update) handle children?))))

(define child-x/y->pos
	(letrec ([x/y->pos/raw (foreign-lambda int "IupConvertXYToPos" nonnull-ihandle int int)])
		(lambda (parent x y)
			(let ([pos (x/y->pos/raw parent x y)])
				(and (not (negative? pos)) pos)))))

;; }}}

;; {{{ Dialog functions

(define show
  (letrec ([position
            (lambda (v)
              (case v
                [(center)           #xffff]
                [(start top left)   #xfffe]
                [(end bottom right) #xfffd]
                [(mouse)            #xfffc]
                [(parent-center)    #xfffa]
                [(current)          #xfffb]
                [else               v]))]
           [popup (foreign-safe-lambda istatus "IupPopup" nonnull-ihandle int int)]
           [show/x/y (foreign-safe-lambda istatus "IupShowXY" nonnull-ihandle int int)])
    (lambda (handle #!key [x 'current] [y 'current] [modal? #f])
      (let ([status ((if modal? popup show/x/y) handle (position x) (position y))])
        (case status
          [(error) (error 'show "failed to show" handle)]
          [else    status])))))

(define hide
	(letrec ([hide/raw (foreign-safe-lambda istatus "IupHide" nonnull-ihandle)])
		(lambda (handle)
			(let ([status (hide/raw handle)])
				(case status
					[(#t) (void)]
					[else (error 'hide (format "failed to hide (~s)" status) handle)])))))

;; }}}

;; {{{ Composition functions

(define dialog
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupDialog" ihandle)))

(define fill
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupFill")))

(define gridbox
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupGridBoxv((Ihandle **)handles));")
  	#:apply-args list))

(define hbox
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupHboxv((Ihandle **)handles));")
  	#:apply-args list))

(define vbox
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupVboxv((Ihandle **)handles));")
  	#:apply-args list))

(define zbox
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupZboxv((Ihandle **)handles));")
  	#:apply-args list))

(define cbox
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupCboxv((Ihandle **)handles));")
  	#:apply-args list))

(define sbox
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupSbox" ihandle)))

(define radio
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupRadio" ihandle)))

(define normalizer
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupNormalizerv((Ihandle **)handles));")
  	#:apply-args list))

(define backgroundbox
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupBackgroundBox" ihandle)))

(define detachbox
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupDetachBox" ihandle)))

(define expandbox
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupExpander" ihandle)))

(define scrollbox
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupScrollBox" ihandle)))

(define split
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupSplit" ihandle ihandle)))

;; }}}

;; {{{ Image resource functions

(define image/palette
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupImage" int int blob)))

(define image/rgb
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupImageRGB" int int blob)))

(define image/rgba
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupImageRGBA" int int blob)))

(define image/file
	(letrec ([load-image (foreign-lambda ihandle "IupLoadImage" c-string)])
		(make-constructor-procedure
			(lambda (file)
				(or (load-image file) (error 'image/file (attribute #f 'iupim-lasterror)))))))

(define image/blob
	(letrec ([load-image
						(foreign-lambda* ihandle ([scheme-pointer data] [int size])
							"Ihandle *image;"
							"imBinMemoryFileName file;"
							"int oldModule = imBinFileSetCurrentModule(IM_MEMFILE);"
							"file.buffer = data;"
							"file.size = size;"
							"file.reallocate = 0;"
							"image = IupLoadImage((const char *)&file);"
							"imBinFileSetCurrentModule(oldModule);"
							"C_return(image);")])
		(make-constructor-procedure
			(lambda (data)
				(or (load-image
						 data
						 (cond
							[(blob? data) (blob-size data)]
							[(string? data) (string-length data)]
							[else (error 'image/blob "unknown argument type")]))
						(error 'image/blob (attribute #f 'iupim-lasterror)))))))

(define image-save
	(letrec ([save-image (foreign-lambda bool "IupSaveImage" nonnull-ihandle c-string iname/upcase)])
		(lambda (handle file format)
			(unless (save-image handle file format)
				(error 'image-save (attribute #f 'iupim-lasterror))))))

;; }}}

;; {{{ Focus functions

(define current-focus
  (letrec ([focus (foreign-safe-lambda ihandle "IupGetFocus")]
           [focus-set! (foreign-safe-lambda ihandle "IupSetFocus" ihandle)]
           [current-focus
            (case-lambda
              [()       (focus)]
              [(handle) (focus-set! handle)])])
    (getter-with-setter current-focus current-focus)))

(define focus-next
	(letrec ([focus-next/raw (foreign-safe-lambda ihandle "IupNextField" ihandle)])
		(lambda (#!optional [handle (current-focus)])
			(focus-next/raw handle))))

(define focus-previous
	(letrec ([focus-previous/raw (foreign-safe-lambda ihandle "IupPreviousField" ihandle)])
		(lambda (#!optional [handle (current-focus)])
			(focus-previous/raw handle))))

;; }}}

;; {{{ Menu functions

(define menu
  (make-constructor-procedure
  	(foreign-lambda* nonnull-ihandle ([ihandle-list handles]) "C_return(IupMenuv((Ihandle **)handles));")
  	#:apply-args list))

(define menu-item
  (letrec ([action-item (foreign-lambda nonnull-ihandle "IupItem" c-string iname/upcase)]
           [submenu-item (foreign-lambda nonnull-ihandle "IupSubmenu" c-string ihandle)])
    (make-constructor-procedure
     (lambda (#!optional [title #f] [action/menu #f])
       ((if (ihandle? action/menu) submenu-item action-item) title action/menu)))))

(define menu-separator
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupSeparator")))

;; }}}

;; {{{ Miscellaneous resource functions

(define clipboard
	(make-constructor-procedure
		(foreign-lambda nonnull-ihandle "IupClipboard")))

(define timer
  (make-constructor-procedure
  	(foreign-lambda nonnull-ihandle "IupTimer")))

(define send-url
	(letrec ([send-url/raw (foreign-lambda int "IupHelp" c-string)])
		(lambda (url)
			(and-let* ([status (send-url/raw url)]
			           [(not (= status 1))])
			  (error 'send-url (format "failed to open URL (~s)" status) url))
			(void))))

;; }}}

;; {{{ The library watchdog

(define thread-watchdog
  (letrec ([open (foreign-lambda* istatus () "C_return(IupOpen(NULL, NULL));")]
					 [setlocale (foreign-lambda* void () "setlocale(LC_NUMERIC, \"C\");")]
           [open-imglib (foreign-lambda void "IupImageLibOpen")]
           [close (foreign-lambda void "IupClose")]
           [chicken-yield (foreign-value "&CHICKEN_yield" c-pointer)])
		(and-let* ([(let ([status (dynamic-wind void open setlocale)])
    			        (case status
										[(#t)     #t]
										[(ignore) #f]
										[else     (error 'iup (format "failed to initialize library (~s)" status))]))]
      	       [(open-imglib)]
               [watchdog (timer)])
      (set-finalizer!
       watchdog
       (lambda (watchdog)
         (destroy! watchdog)
         (close)))
      (callback-set! watchdog 'action-cb chicken-yield)
      (attribute-set! watchdog 'time 500)
      (attribute-set! watchdog 'run #t)
      watchdog)))

;; }}}


text:
import SIMD
using SIMD: ScalarTypes

(V::Type{<:AbstractVec{T,N}})(x::T) where {T <: ScalarTypes, N} = V(ntuple(_ -> x, N))
datatype(x::SIMD.Vec{N,T}) where {N,T} = T

# Scalar vector type
# Essentially equivalent to what SIMD.jl provides; we only reproduce it
# so that it fits into our `AbstractVec` framework.

struct SVec{T,N} <: AbstractVec{T,N}
  data::NTuple{N,VecElement{T}}
  SVec{T,N}(data::NTuple{N,VecElement{T}}) where {T,N} = new(data)
end

SVec(xs::NTuple{N,VecElement{T}}) where {T,N} = SVec{T,N}(xs)
SVec(xs::NTuple{N,T}) where {T,N} = SVec{T,N}(map(VecElement, xs))

SVec{T,N}(xs::NTuple{N,T}) where {T,N} = SVec{T,N}(map(VecElement, xs))

# SIMD vectors are buggy – https://github.com/JuliaLang/julia/issues/30056
# Flip this to test programs without using them.
vect(xs::T...) where {T <: ScalarTypes} = SVec(xs)
vect(xs::VecElement{T}...) where {T} = SVec(xs)
# vect(xs::T...) where {T} = Vec(xs)

data(xs::SVec) = getfield.(xs.data, :value)

Base.summary(::SVec) = "SVec"

Base.convert(::Type{SIMD.Vec}, x::SVec{T,N}) where {T,N} =
  SIMD.Vec{N,T}(x.data)

Base.convert(::Type{SVec}, x::SIMD.Vec{N,T}) where {T,N} =
  SVec{T,N}(x.elts)

simd_vec(x::SVec) = convert(SIMD.Vec, x)
simd_vec(x) = x
svec(x::SIMD.Vec) = convert(SVec, x)
svec(x) = x

Base.sum(x::SVec) = sum(convert(SIMD.Vec, x))

SVecOrVal{T} = Union{SVec{T},T}
VecOrVal{T} = Union{Vec{T},T}
vecconvert(T, x::SIMD.Vec) = convert(SIMD.Vec{length(x),T}, x)
vecconvert(T, x) = convert(T, x)

vecpromote(xs...) = map(x -> vecconvert(promote_type(map(datatype, xs)...), x), xs)

# Arithmetic operations (forward to SIMD.jl)

for op in :[+, -, *, /, div, rem, &, |, !,
            ==, !=, >, >=, <, <=].args
    @eval begin
        @spmd $op(xs::SVecOrVal{T}...) where T <: ScalarTypes =
          svec($op(map(simd_vec, xs)...))
        # TODO: remove this when we can compile the usual promotion mechanisms
        @spmd $op(xs::SVecOrVal{<:ScalarTypes}...) =
          svec($op(vecpromote(map(simd_vec, xs)...)...))
    end
end

# TODO figure out what we can remove

promote_rule(::Type{SVec{S,N}}, ::Type{T}) where {S,T<:ScalarTypes,N} = SVec{promote_type(T,S),N}
promote_rule(::Type{SVec{S,N}}, ::Type{SVec{T,N}}) where {S,T,N} = SVec{promote_type(T,S),N}
promote_rule(::Type{Vec{S,N}}, ::Type{SVec{T,N}}) where {S,T,N} = Vec{promote_type(T,S),N}

convert(::Type{SVec{T,N}}, x) where {T,N} = SVec{T,N}(x)
convert(::Type{SVec{T,N}}, xs::SVec{S,N}) where {T,S,N} = vect(T.(data(xs))...)

convert(::Type{Vec{T,N}}, xs::SVec{S,N}) where {T,S,N} = Vec(map(x -> VecElement(T(x.value)), xs.data))

import Base.(:)
(:)(start::SVec{T,N}, step::SVec{T,N}, stop::SVec{T,N}) where {T,N} = Vec(tuple(map(x -> x[1]:x[2]:x[3], zip(start,step,stop))...))
(:)(start::SVec{T,N}, stop::SVec{T,N}) where {T,N} = (:)(start, SVec{T,N}(1), stop)

(:)(start::T, stop::AbstractVec{S,N}) where {T,S,N} = (:)(promote(start, stop)...)
(:)(start::AbstractVec{T,N}, stop::S) where {T,S,N} = (:)(promote(start, stop)...)

@spmd (:)(start, step, stop) = start:step:stop
@spmd (:)(start, stop) = start:stop

using Base: not_int
@spmd not_int(xs) = spmd(__mask__, ~, xs)


text:
module TestStaking

  using Test, Dates
  using NumerAPI.Tournament

  public_id = ENV["NUMERAI_PUBLIC_ID"]
  secret_key = ENV["NUMERAI_SECRET_KEY"]

  username = ENV["TOURNAMENT_USERNAME"]
  model_id = ENV["TOURNAMENT_MODEL_ID"]

  tournament_api = TournamentAPI(public_id, secret_key)

  # @testset "test state_set" begin
  #   nmr = 0.01
  #   stake = stake_set(tournament_api, username, nmr)

  #   println(stake)

  # end

  @testset "test stake_get" begin
    stake = stake_get(tournament_api, username)

    @test stake === 0.01
  end

  @testset "test stake_drain" begin

  end

  @testset "test stake_decrease" begin

  end

  @testset "test stake_increase" begin

  end

end # module


text:
"""
Stubs contains stub implementations of certain parts of the client side of DandelionWebSockets, as
well as simple implementations of the server side of a WebSocket.
"""
module Stubs

using DandelionWebSockets
using DandelionWebSockets:
    HTTPAdapter, HTTPUpgradeResponse, HeaderList, Frame,
    OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE, Opcode
import DandelionWebSockets: dohandshake, tcpnodelay, on_text, on_binary, send_text, send_binary,
                            state_connecting, state_open, state_closing, state_closed
import Base: read, write, eof, close

using Base64
using SHA

export ScriptedServer, ScriptedClientHandler, InProcessHandshakeAdapter,
    ShortWait, WaitForOpen, WaitForClose, CloseConnection,
    SendTextFrame, waitforscriptdone, InProcessIO, InProcessIOPair

const DataChannel = Channel{Union{Vector{UInt8}, Nothing}}

"""
InProcessIO is a fake network socket that uses `Channel`s for asynchronous communication.
"""
mutable struct InProcessIO <: IO
    readbuffer::IOBuffer
    writechan::DataChannel
    readchan::DataChannel
    iseof::Threads.Atomic{Bool}

    InProcessIO(writechan::DataChannel, readchan::DataChannel) =
        new(IOBuffer(), writechan, readchan, Threads.Atomic{Bool}(false))
end

function eof(io::InProcessIO)
    iseofalready = io.iseof[]
    if iseofalready
        return true
    end

    # Read a byte and see if it makes us EOF
    try
        mark(io.readbuffer)
        _fetch(io)
        reset(io.readbuffer)
    catch ex
        if typeof(ex) != EOFError
            rethrow()
        end
    end
    io.iseof[]
end

function _fetch(io::InProcessIO)
    newdata = take!(io.readchan)
    if newdata == nothing
        Threads.atomic_or!(io.iseof, true)
        throw(EOFError())
    else
        write(io.readbuffer, newdata)
    end
end

function close(io::InProcessIO)
    put!(io.writechan, nothing)
end

function _write(io::InProcessIO, v::T) where T
    b = IOBuffer()
    write(b, v)
    data = take!(b)
    put!(io.writechan, data)
    yield()
end
write(io::InProcessIO, v::UInt8) = _write(io, v)
write(io::InProcessIO, v::UInt16) = _write(io, v)
write(io::InProcessIO, v::UInt64) = _write(io, v)
function write(io::InProcessIO, cs::Base.CodeUnits{UInt8, String})
    put!(io.writechan, Vector{UInt8}(cs))
    yield()
end
function write(io::InProcessIO, cs::Vector{UInt8})
    put!(io.writechan, Vector{UInt8}(cs))
    yield()
end

function _read(io::InProcessIO, ::Type{T}) where T
    m = mark(io.readbuffer)
    while true
        try
            return read(io.readbuffer, T)
        catch ex
            if typeof(ex) != EOFError
                rethrow()
            end
        end
        try
            _fetch(io)
            seek(io.readbuffer, m)
        catch ex
            rethrow()
        end
    end
end
read(io::InProcessIO, v::Type{UInt8}) = _read(io, v)
read(io::InProcessIO, v::Type{UInt16}) = _read(io, v)
read(io::InProcessIO, v::Type{UInt64}) = _read(io, v)
function read(io::InProcessIO, n::Integer)
    m = mark(io.readbuffer)
    while true
        if io.readbuffer.size - io.readbuffer.ptr + 1 >= n
            return read(io.readbuffer, n)
        else
            _fetch(io)
            seek(io.readbuffer, m)
        end
    end
end

"""
InProcessIOPair creates two endpoints, one for a client and one for a server. What is written on
one endpoint can be read on the other.
"""
struct InProcessIOPair
    endpoint1::InProcessIO
    endpoint2::InProcessIO

    function InProcessIOPair()
        c1 = DataChannel(Inf)
        c2 = DataChannel(Inf)
        new(InProcessIO(c1, c2), InProcessIO(c2, c1))
    end
end

"""
TestAction is an abstract type for different actions for a test client or server to take.
"""
abstract type TestAction end

struct ShortWait <: TestAction end
struct WaitForOpen <: TestAction end
struct WaitForClose <: TestAction end
struct CloseConnection <: TestAction end
struct SendTextFrame <: TestAction
    s::String
end

const ListOfActions = AbstractVector{TestAction}

mutable struct ConnectionStatistics
    texts_received::Int
    binaries_received::Int

    ConnectionStatistics() = new(0, 0)
end

receivedtext(s::ConnectionStatistics) = s.texts_received += 1
receivedbinary(s::ConnectionStatistics) = s.binaries_received += 1

"""
ScriptedServer is a stub of a WebSocket server, that follows a pre-defined list of actions.
"""
mutable struct ScriptedServer
    script::ListOfActions
    io::Union{IO, Nothing}
    chanclose::Channel{Nothing}
    chanopen::Channel{Nothing}
    chanscriptdone::Channel{Nothing}
    statistics::ConnectionStatistics

    # TODO Use a better IO object
    function ScriptedServer(script::ListOfActions)
        this = new(script, nothing, Channel{Nothing}(1), Channel{Nothing}(1), Channel{Nothing}(1), ConnectionStatistics())
        @async runscript(this)
        this
    end
end

function newconnection(s::ScriptedServer, io::IO)
    s.io = io
    notifyopen(s)
    @async serverreadloop(s)
end
waitforclose(s::ScriptedServer) = take!(s.chanclose)
notifyclosed(s::ScriptedServer) = put!(s.chanclose, nothing)
notifyopen(s::ScriptedServer) = put!(s.chanopen, nothing)
waitforopen(s::ScriptedServer) = take!(s.chanopen)
notifyscriptdone(s::ScriptedServer) = put!(s.chanscriptdone, nothing)
waitforscriptdone(s::ScriptedServer) = take!(s.chanscriptdone)
write(s::ScriptedServer, frame::Frame) = write(s.io, frame)

createserverframe(opcode::Opcode, data::AbstractVector{UInt8}) =
    Frame(true, opcode, false, length(data), 0, b"", data)
createtextframe(s::String) = createserverframe(OPCODE_TEXT, codeunits(s))

takeaction(s::ScriptedServer, ::WaitForOpen) = waitforopen(s)
takeaction(::ScriptedServer, ::ShortWait) = sleep(0.5)
takeaction(s::ScriptedServer, send::SendTextFrame) = write(s, createtextframe(send.s))
function takeaction(s::ScriptedServer, ::WaitForClose)
    waitforclose(s)
end
function takeaction(s::ScriptedServer, ::CloseConnection)
    close(s.io)
end

function runscript(s::ScriptedServer)
    for action in s.script
        #println("Server: $(action)")
        try
            takeaction(s, action)
        catch ex
            println("Server: Action $(action) threw an exception: $ex")
            break
        end
    end
    notifyscriptdone(s)
end

function serverreadloop(s::ScriptedServer)
    try
        while true
            frame = read(s.io, Frame)
            if frame.opcode == OPCODE_TEXT
                receivedtext(s.statistics)
            elseif frame.opcode == OPCODE_BINARY
                receivedbinary(s.statistics)
            end
        end
    catch ex
        println("serverreadloop: Exception $ex")
    end
end

# Override this to make tcpnodelay a no-op for our fake socket
tcpnodelay(::InProcessIO) = nothing

"""
ScriptedClientHandler is a WebSocket client implementation that follows a fixed script.
"""
mutable struct ScriptedClientHandler <: WebSocketHandler
    connection::Union{WebSocketConnection, Nothing}
    script::ListOfActions
    chanclose::Channel{Nothing}
    chanopen::Channel{Nothing}
    chanscriptdone::Channel{Nothing}
    statistics::ConnectionStatistics

    function ScriptedClientHandler(script::ListOfActions)
        this = new(nothing, script, Channel{Nothing}(1), Channel{Nothing}(1), Channel{Nothing}(1), ConnectionStatistics())
        @async runscript(this)
        this
    end
end

waitforclose(c::ScriptedClientHandler) = take!(c.chanclose)
notifyclosed(c::ScriptedClientHandler) = put!(c.chanclose, nothing)
notifyopen(c::ScriptedClientHandler) = put!(c.chanopen, nothing)
waitforopen(c::ScriptedClientHandler) = take!(c.chanopen)
notifyscriptdone(c::ScriptedClientHandler) = put!(c.chanscriptdone, nothing)
waitforscriptdone(c::ScriptedClientHandler) = take!(c.chanscriptdone)

takeaction(c::ScriptedClientHandler, ::WaitForOpen) = waitforopen(c)
takeaction(::ScriptedClientHandler, ::ShortWait) = sleep(0.5)
takeaction(c::ScriptedClientHandler, send::SendTextFrame) = send_text(c.connection, send.s)
takeaction(c::ScriptedClientHandler, ::WaitForClose) = waitforclose(c)
takeaction(c::ScriptedClientHandler, ::CloseConnection) = stop(c.connection)

function runscript(c::ScriptedClientHandler)
    for action in c.script
        #println("Client: $(action)")
        try
            takeaction(c, action)
        catch ex
            println("Client: Action $(action) threw an exception: $ex")
            break
        end
    end
    notifyscriptdone(c)
end

on_text(t::ScriptedClientHandler, ::String) = receivedtext(t.statistics)
on_binary(t::ScriptedClientHandler, ::AbstractVector{UInt8}) = receivedbinary(t.statistics)
state_closed(t::ScriptedClientHandler) = notifyclosed(t)
state_closing(t::ScriptedClientHandler) = nothing
state_connecting(t::ScriptedClientHandler, connection::WebSocketConnection) = t.connection = connection
state_open(t::ScriptedClientHandler) = notifyopen(t)

"""
InProcessHandshakeAdapter is a stub for an HTTP upgrade request.
"""
struct InProcessHandshakeAdapter <: HTTPAdapter
    server::ScriptedServer
end

function finduniqueheader(headers::HeaderList, name::String) :: String
    vs = [v for (k, v) in headers
          if lowercase(k) == lowercase(name)]

    if length(vs) > 1
        throw(DomainError("Found two occurrences of $name: $vs"))
    end
    if isempty(vs)
        throw(DomainError("Found no occurrences of $name"))
    end

    vs[1]
end

function dohandshake(adapter::InProcessHandshakeAdapter, uri::String, headers::HeaderList) :: HTTPUpgradeResponse
    iopair = InProcessIOPair()
    newconnection(adapter.server, iopair.endpoint1)

    key = finduniqueheader(headers, "Sec-WebSocket-Key")
    responseheaders = [
        "Connection" => "Upgrade",
        "Upgrade" => "websocket",
        "Sec-WebSocket-Accept" => base64encode(sha1(key * "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))]
    HTTPUpgradeResponse(iopair.endpoint2, 101, responseheaders, b"")
end

"""
Wait for both the server and the client to close their connections.
"""
function waitforscriptdone(server::ScriptedServer, clienthandler::ScriptedClientHandler)
    waitforscriptdone(server)
    waitforscriptdone(clienthandler)
end

end