abstract type CSVWriter end

write_csv_row!(io::IO, vec::AbstractVector) = writedlm(io, reshape(vec, 1, :), ",")
write_csv_row!(io::IO, array::AbstractArray) = write_csv_row!(io, vec(array))

function write_inputs_csv(writer::CSVWriter, dir::String, robot_name::String, samples::AbstractVector{<:Sample})
    open(joinpath(dir, "$(robot_name)_inputs.csv"), "w") do io
        header = make_header(writer)
        write_csv_row!(io, header)
        for sample in samples
            sampledata = make_sample_data(writer, sample)
            write_csv_row!(io, vcat(sampledata...))
        end
    end
end

function write_expected_result_csvs(writer::CSVWriter, dir::String, robot_name::String, sample::Sample)
    Jlib = velocity_jacobian(writer, sample)

    state = MechanismState(writer.mechanism)
    set_configuration!(state, sample.q)
    set_velocity!(state, sample.v)
    M = mass_matrix(state)
    τid = inverse_dynamics(state, sample.v̇)
    c = dynamics_bias(state)

    Mlib = (Jlib' \ M) * Jlib'
    # τidlib = Jlib' \ τid
    # τlib = Jlib' \ sample.τ
    # clib = Jlib' \ c
    _, _, v̇lib, τlib = make_sample_data(writer, sample)

    open(joinpath(dir, "$(robot_name)_inverse_dynamics_expected.csv"), "w") do io
        write_csv_row!(io, τlib)
    end

    open(joinpath(dir, "$(robot_name)_mass_matrix_expected.csv"), "w") do io
        write_csv_row!(io, Mlib)
    end

    open(joinpath(dir, "$(robot_name)_dynamics_expected.csv"), "w") do io
        write_csv_row!(io, v̇lib)
    end
end

function make_header(writer::CSVWriter)
    header = String[]
    append!(header, configuration_coordinate_names(writer))
    append!(header, tangential_coordinate_names(writer, "v"))
    append!(header, tangential_coordinate_names(writer, "vd"))
    append!(header, tangential_coordinate_names(writer, "tau"))
    header
end

function configuration_coordinate_names(joint::Joint{<:Any, <:OneDegreeOfFreedomFixedAxis}, ::CSVWriter)
    ["q_$(string(joint))"]
end

function tangential_coordinate_names(joint::Joint{<:Any, <:OneDegreeOfFreedomFixedAxis}, prefix::String, ::CSVWriter)
    ["$(prefix)_$(string(joint))"]
end

function velocity_jacobian(writer::CSVWriter, sample::Sample)
    ForwardDiff.jacobian(sample.v) do v
        T = eltype(v)
        sample_dual = Sample(copyto!(similar(sample.q, T), sample.q), v, copyto!(similar(sample.v̇, T), sample.v̇), copyto!(similar(sample.τ, T), sample.τ))
        _, v_lib, _, _ = make_sample_data(writer, sample_dual)
        v_lib
    end
end

# RigidBodyDynamics.jl
struct RigidBodyDynamicsJlWriter <:CSVWriter
    mechanism::Mechanism{Float64}
end

RigidBodyDynamicsJlWriter(mechanism::Mechanism, robot_name::String) = RigidBodyDynamicsJlWriter(mechanism)

shortlibname(::RigidBodyDynamicsJlWriter) = "rbdjl"

function configuration_coordinate_names(writer::RigidBodyDynamicsJlWriter)
    configuration_coordinate_names.(tree_joints(writer.mechanism), Ref(writer)) |> flatten |> collect
end

function tangential_coordinate_names(writer::RigidBodyDynamicsJlWriter, prefix::String)
    tangential_coordinate_names.(tree_joints(writer.mechanism), Ref(prefix), Ref(writer)) |> flatten |> collect
end

function make_sample_data(writer::RigidBodyDynamicsJlWriter, sample::Sample)
    (sample.q, sample.v, sample.v̇, sample.τ)
end

function configuration_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, ::RigidBodyDynamicsJlWriter)
    jointprefix = "q_$(string(joint))"
    [
        jointprefix * "_qs",
        jointprefix * "_qx",
        jointprefix * "_qy",
        jointprefix * "_qz",
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
    ]
end

function tangential_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, prefix::String, ::RigidBodyDynamicsJlWriter)
    jointprefix = "$(prefix)_$(string(joint))"
    [
        jointprefix * "_wx",
        jointprefix * "_wy",
        jointprefix * "_wz",
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
    ]
end

# RBDL
struct RBDLWriter{S<:StateCache} <: CSVWriter
    mechanism::Mechanism
    statecache::S
    joints::Vector{Joint{Float64}}
    q_permutation::Vector{Int}
end

const RBDL_JOINT_NAMES = Dict{String, Vector{String}}()
RBDL_JOINT_NAMES["iiwa"] = [
    "lbr_iiwa_joint_1",
    "lbr_iiwa_joint_2",
    "lbr_iiwa_joint_3",
    "lbr_iiwa_joint_4",
    "lbr_iiwa_joint_5",
    "lbr_iiwa_joint_6",
    "lbr_iiwa_joint_7"
]
RBDL_JOINT_NAMES["hyq"] = [
    "base_link_0_to_world",
    "lf_haa_joint",
    "lf_hfe_joint",
    "lf_kfe_joint",
    "lh_haa_joint",
    "lh_hfe_joint",
    "lh_kfe_joint",
    "rf_haa_joint",
    "rf_hfe_joint",
    "rf_kfe_joint",
    "rh_haa_joint",
    "rh_hfe_joint",
    "rh_kfe_joint"
]
RBDL_JOINT_NAMES["atlas"] = [
    "pelvis_to_world",
    "back_bkz",
    "back_bky",
    "back_bkx",
    "l_arm_shz",
    "l_arm_shx",
    "l_arm_ely",
    "l_arm_elx",
    "l_arm_uwy",
    "l_arm_mwx",
    "l_arm_lwy",
    "neck_ay",
    "r_arm_shz",
    "r_arm_shx",
    "r_arm_ely",
    "r_arm_elx",
    "r_arm_uwy",
    "r_arm_mwx",
    "r_arm_lwy",
    "l_leg_hpz",
    "l_leg_hpx",
    "l_leg_hpy",
    "l_leg_kny",
    "l_leg_aky",
    "l_leg_akx",
    "r_leg_hpz",
    "r_leg_hpx",
    "r_leg_hpy",
    "r_leg_kny",
    "r_leg_aky",
    "r_leg_akx"
]

function RBDLWriter(mechanism::Mechanism, robot_name::String)
    # q order is permuted so that quaternion scalar components come last.
    # https://bitbucket.org/rbdl/rbdl/src/0879ee8c548a07b8a9e1296bcbb5b006e1a098c7/src/Model.cc#lines-374:375
    joint_names = RBDL_JOINT_NAMES[robot_name]
    joints_rbdl = Vector{Joint{Float64}}(undef, length(joints(mechanism)))
    for i in eachindex(joints_rbdl)
        joints_rbdl[i] = findjoint(mechanism, joint_names[i])
    end
    @assert isempty(setdiff(joints_rbdl, tree_joints(mechanism)))
    state = MechanismState(mechanism)
    q_permutation = Int[]
    quatjoints = Joint[]
    for joint in joints_rbdl
        qrange = configuration_range(state, joint)
        vrange = velocity_range(state, joint)
        if joint_type(joint) isa OneDegreeOfFreedomFixedAxis
            append!(q_permutation, qrange)
        elseif joint_type(joint) isa QuaternionFloating
            push!(quatjoints, joint)
            append!(q_permutation, qrange[5 : 7])
            append!(q_permutation, qrange[2 : 4])
        else
            error("Joint type not supported")
        end
    end
    for joint in quatjoints
        push!(q_permutation, configuration_range(state, joint)[1])
    end
    RBDLWriter(mechanism, StateCache(mechanism), joints_rbdl, q_permutation)
end

shortlibname(::RBDLWriter) = "rbdl"

function configuration_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, ::RBDLWriter)
    # NOTE: still transformed by q_permutation after (easier that way)
    jointprefix = "q_$(string(joint))"
    [
        jointprefix * "_qs",
        jointprefix * "_qx",
        jointprefix * "_qy",
        jointprefix * "_qz",
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
    ]
end

function tangential_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, prefix::String, ::RBDLWriter)
    jointprefix = "$(prefix)_$(string(joint))"
    [
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
        jointprefix * "_wx",
        jointprefix * "_wy",
        jointprefix * "_wz"
    ]
end

function configuration_coordinate_names(writer::RBDLWriter)
    names = configuration_coordinate_names.(tree_joints(writer.mechanism), Ref(writer)) |> flatten |> collect
    names[writer.q_permutation]
end

function tangential_coordinate_names(writer::RBDLWriter, prefix::String)
    tangential_coordinate_names.(writer.joints, Ref(prefix), Ref(writer)) |> flatten |> collect
end

function make_sample_data(writer::RBDLWriter, sample::Sample{T}) where T
    mechanism = writer.mechanism
    state = writer.statecache[T]
    rootframe = root_frame(mechanism)
    set_configuration!(state, sample.q)
    set_velocity!(state, sample.v)

    # q
    q_rbdl = sample.q[writer.q_permutation]

    # v, v̇, τ
    v_rbdl = fill!(similar(velocity(state)), NaN)
    v̇_rbdl = fill!(similar(velocity(state)), NaN)
    τ_rbdl = fill!(similar(velocity(state)), NaN)
    vstart = 1
    for joint in writer.joints
        jointid = JointID(joint)
        vrange = velocity_range(state, jointid)
        if joint_type(joint) isa OneDegreeOfFreedomFixedAxis
            v_rbdl[vstart : vstart] .= sample.v[vrange]
            v̇_rbdl[vstart : vstart] .= sample.v̇[vrange]
            τ_rbdl[vstart : vstart] .= sample.τ[vrange]
        elseif joint_type(joint) isa QuaternionFloating
            # RBDL rewrites
            #   [world] -- floating joint -- [floating body]
            # as
            #   [world] -- translation xyz -- [null body] -- spherical -- [floating body]
            # The velocity for an RBDL translation xyz joint is just the time derivative of the position.
            # The velocity for an RBDL spherical joint is the angular velocity in body frame
            # (https://bitbucket.org/rbdl/rbdl/src/0879ee8c548a07b8a9e1296bcbb5b006e1a098c7/include/rbdl/Joint.h#lines-135:144).

            v̇joint = sample.v̇[jointid]
            τjoint = sample.τ[jointid]
            jointtf = joint_transform(state, joint)
            jointtwist = RigidBodyDynamics.twist(state, joint)
            jointaccel = SpatialAcceleration(jointtwist.body, jointtwist.base, jointtwist.frame, SVector{3}(v̇joint[1 : 3]), SVector{3}(v̇joint[4 : 6]))
            jointwrench = Wrench(frame_after(joint), SVector{3}(τjoint[1 : 3]), SVector{3}(τjoint[4 : 6]))

            body_origin = Point3D(jointtwist.body, zero(T), zero(T), zero(T))
            body_origin_velocity = transform(point_velocity(jointtwist, body_origin), jointtf)
            body_origin_accel = transform(point_acceleration(jointtwist, jointaccel, body_origin), jointtf)

            # v
            v_rbdl[vstart .+ (0 : 2)] .= body_origin_velocity.v
            v_rbdl[vstart .+ (3 : 5)] .= angular(jointtwist)

            # v̇
            v̇_rbdl[vstart .+ (0 : 2)] .= body_origin_accel.v
            v̇_rbdl[vstart .+ (3 : 5)] .= angular(jointaccel)

            # τ
            τ_rbdl[vstart .+ (0 : 2)] .= linear(transform(jointwrench, jointtf))
            τ_rbdl[vstart .+ (3 : 5)] .= angular(jointwrench)
        else
            error("Joint type not yet supported")
        end
        vstart += num_velocities(joint)
    end

    (q_rbdl, v_rbdl, v̇_rbdl, τ_rbdl)
end

# Pinocchio
struct PinocchioWriter <: CSVWriter
    mechanism::Mechanism{Float64}
    q_permutation::Vector{Int}
    v_permutation::Vector{Int}
end

const PINOCCHIO_JOINT_NAMES = Dict{String, Vector{String}}()
PINOCCHIO_JOINT_NAMES["iiwa"] = [
    "lbr_iiwa_joint_1",
    "lbr_iiwa_joint_2",
    "lbr_iiwa_joint_3",
    "lbr_iiwa_joint_4",
    "lbr_iiwa_joint_5",
    "lbr_iiwa_joint_6",
    "lbr_iiwa_joint_7"
]
PINOCCHIO_JOINT_NAMES["hyq"] = [
    "base_link_0_to_world",
    "lf_haa_joint",
    "lf_hfe_joint",
    "lf_kfe_joint",
    "lh_haa_joint",
    "lh_hfe_joint",
    "lh_kfe_joint",
    "rf_haa_joint",
    "rf_hfe_joint",
    "rf_kfe_joint",
    "rh_haa_joint",
    "rh_hfe_joint",
    "rh_kfe_joint"
]
PINOCCHIO_JOINT_NAMES["atlas"] = [
    "pelvis_to_world",
    "back_bkz",
    "back_bky",
    "back_bkx",
    "l_arm_shz",
    "l_arm_shx",
    "l_arm_ely",
    "l_arm_elx",
    "l_arm_uwy",
    "l_arm_mwx",
    "l_arm_lwy",
    "neck_ay",
    "r_arm_shz",
    "r_arm_shx",
    "r_arm_ely",
    "r_arm_elx",
    "r_arm_uwy",
    "r_arm_mwx",
    "r_arm_lwy",
    "l_leg_hpz",
    "l_leg_hpx",
    "l_leg_hpy",
    "l_leg_kny",
    "l_leg_aky",
    "l_leg_akx",
    "r_leg_hpz",
    "r_leg_hpx",
    "r_leg_hpy",
    "r_leg_kny",
    "r_leg_aky",
    "r_leg_akx"
]

function PinocchioWriter(mechanism::Mechanism, robot_name::String)
    joint_names = PINOCCHIO_JOINT_NAMES[robot_name]
    joints_pinocchio = findjoint.(Ref(mechanism), joint_names)
    @assert isempty(setdiff(joints_pinocchio, tree_joints(mechanism)))
    state = MechanismState(mechanism)
    q_permutation = Int[]
    v_permutation = Int[]
    for joint in joints_pinocchio
        qrange = configuration_range(state, joint)
        vrange = velocity_range(state, joint)
        if joint_type(joint) isa OneDegreeOfFreedomFixedAxis
            append!(q_permutation, qrange)
            append!(v_permutation, vrange)
        elseif joint_type(joint) isa QuaternionFloating
            append!(q_permutation, qrange[[5, 6, 7, 2, 3, 4, 1]])
            append!(v_permutation, vrange[[4, 5, 6, 1, 2, 3]])
        else
            error("Joint type not supported")
        end
    end
    @assert length(q_permutation) == num_positions(mechanism)
    @assert length(v_permutation) == num_velocities(mechanism)
    PinocchioWriter(mechanism, q_permutation, v_permutation)
end

shortlibname(::PinocchioWriter) = "pinocchio"

function configuration_coordinate_names(writer::PinocchioWriter)
    names = configuration_coordinate_names.(tree_joints(writer.mechanism), Ref(writer)) |> flatten |> collect
    names[writer.q_permutation]
end

function tangential_coordinate_names(writer::PinocchioWriter, prefix::String)
    names = tangential_coordinate_names.(tree_joints(writer.mechanism), Ref(prefix), Ref(writer)) |> flatten |> collect
    names[writer.v_permutation]
end

function make_sample_data(writer::PinocchioWriter, sample::Sample)
    q_pinocchio = sample.q[writer.q_permutation]
    v_pinocchio = sample.v[writer.v_permutation]
    v̇_pinocchio = sample.v̇[writer.v_permutation]
    τ_pinocchio = sample.τ[writer.v_permutation]
    (q_pinocchio, v_pinocchio, v̇_pinocchio, τ_pinocchio)
end

function configuration_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, ::PinocchioWriter)
    # NOTE: still transformed by q_permutation after (easier that way)
    jointprefix = "q_$(string(joint))"
    [
        jointprefix * "_qs",
        jointprefix * "_qx",
        jointprefix * "_qy",
        jointprefix * "_qz",
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
    ]
end

function tangential_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, prefix::String, ::PinocchioWriter)
    # NOTE: still transformed by v_permutation after (easier that way)
    jointprefix = "$(prefix)_$(string(joint))"
    [
        jointprefix * "_wx",
        jointprefix * "_wy",
        jointprefix * "_wz",
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
    ]
end

# RobCoGen
struct RobCoGenWriter <:CSVWriter
    mechanism::Mechanism{Float64}
    non_floating_joints::Vector{Joint{Float64}}
    non_floating_q_indices::Vector{Int}

    function RobCoGenWriter(mechanism::Mechanism{Float64}, robot_name::String)
        non_floating_joints = Joint{Float64}[j for j in tree_joints(mechanism) if !isfloating(j)]
        state = MechanismState(mechanism)
        non_floating_q_indices = [configuration_range(state, j) for j in non_floating_joints] |> flatten |> collect
        new(mechanism, non_floating_joints, non_floating_q_indices)
    end
end

shortlibname(::RobCoGenWriter) = "robcogen"

function tangential_coordinate_names(joint::Joint{<:Any, <:QuaternionFloating}, prefix::String, ::RobCoGenWriter)
    jointprefix = "$(prefix)_$(string(joint))"
    [
        jointprefix * "_wx",
        jointprefix * "_wy",
        jointprefix * "_wz",
        jointprefix * "_x",
        jointprefix * "_y",
        jointprefix * "_z",
    ]
end

function configuration_coordinate_names(writer::RobCoGenWriter)
    configuration_coordinate_names.(writer.non_floating_joints, Ref(writer)) |> flatten |> collect
end

function tangential_coordinate_names(writer::RobCoGenWriter, prefix::String)
    tangential_coordinate_names.(tree_joints(writer.mechanism), Ref(prefix), Ref(writer)) |> flatten |> collect
end

function make_sample_data(writer::RobCoGenWriter, sample::Sample)
    (sample.q[writer.non_floating_q_indices], sample.v, sample.v̇, sample.τ)
end


using JLD, PyPlot

# Figure 1
fig = figure(figsize=(7.08661, 7.08661), dpi=1000)

k = 0
for m in [25, 50, 100]
    for sgn in [-1, 0, 1]
        file = jldopen("res/coverage_$(m)_$(sgn).jld", "r")
        coverage = read(file, "coverage")
        close(file)

        global k += 1

        ax = subplot(3, 3, k)

        plot(0.0:0.05:1.0, 0.0:0.05:1.0, color="grey", linestyle=":", linewidth=.25)

        plot(0.05:0.05:0.95, coverage[1:19, 1], linewidth=.25, marker="o", markersize=2)
        plot(0.05:0.05:0.95, coverage[1:19, 2], linewidth=.25, marker="v", markersize=2)

        xlim(0.0, 1.0)
        ylim(0.0, 1.0)

        tick_params("both", labelsize="xx-small")

        title("m = $(m), sgn = $(sgn)", size="x-small")

        if m == 100 && sgn == 0
            xlabel(L"$1 - \alpha$", size="x-small")
        end
        if m == 50 && sgn == -1
            ylabel(L"proportion $T \leq \hat c_{T, 1-\alpha}$", size="x-small")
        end
    end
end

tight_layout()

savefig("res/exper3_coverage.pdf")

close(fig)

# studentized
fig = figure(figsize=(7.08661, 7.08661), dpi=1000)

k = 0
for m in [25, 50, 100]
    for sgn in [-1, 0, 1]
        file = jldopen("res/coverage_$(m)_$(sgn).jld", "r")
        coverage = read(file, "coverage")
        close(file)

        global k += 1

        ax = subplot(3, 3, k)

        plot(0.0:0.05:1.0, 0.0:0.05:1.0, color="grey", linestyle=":", linewidth=.25)

        plot(0.05:0.05:0.95, coverage[1:19, 4], linewidth=.25, marker="o", markersize=2)
        plot(0.05:0.05:0.95, coverage[1:19, 5], linewidth=.25, marker="v", markersize=2)

        xlim(0.0, 1.0)
        ylim(0.0, 1.0)

        tick_params("both", labelsize="xx-small")

        title("m = $(m), sgn = $(sgn)", size="x-small")

        if m == 100 && sgn == 0
            xlabel(L"$1 - \alpha$", size="x-small")
        end
        if m == 50 && sgn == -1
            ylabel(L"proportion $T \leq \hat c_{T, 1-\alpha}$", size="x-small")
        end
    end
end

tight_layout()

savefig("res/exper3_coverage_studentized.pdf")

close(fig)

# compare SparKLIE+1 vs SparKLIE+2
fig = figure(figsize=(7.08661, 7.08661), dpi=1000)

k = 0
for m in [25, 50, 100]
    for sgn in [-1, 0, 1]
        file = jldopen("res/coverage_$(m)_$(sgn).jld", "r")
        coverage = read(file, "coverage")
        close(file)

        global k += 1

        ax = subplot(3, 3, k)

        plot(0.05:0.05:0.95, coverage[1:19, 3], linewidth=.25, marker="o", markersize=2)

        xlim(0.00, 1.0)
        ylim(0.75, 1.0)

        tick_params("both", labelsize="xx-small")

        title("m = $(m), sgn = $(sgn)", size="x-small")

        if m == 100 && sgn == 0
            xlabel(L"$1 - \alpha$", size="x-small")
        end
        if m == 50 && sgn == -1
            ylabel("proportion SparKLIE+1 == SparKLIE+2", size="x-small")
        end
    end
end

tight_layout()

savefig("res/exper3_compare.pdf")

close(fig)

# compare SparKLIE+1 vs SparKLIE+2, studentized
fig = figure(figsize=(7.08661, 7.08661), dpi=1000)

k = 0
for m in [25, 50, 100]
    for sgn in [-1, 0, 1]
        file = jldopen("res/coverage_$(m)_$(sgn).jld", "r")
        coverage = read(file, "coverage")
        close(file)

        global k += 1

        ax = subplot(3, 3, k)

        plot(0.05:0.05:0.95, coverage[1:19, 6], linewidth=.25, marker="o", markersize=2)

        xlim(0.00, 1.0)
        ylim(0.75, 1.0)

        tick_params("both", labelsize="xx-small")

        title("m = $(m), sgn = $(sgn)", size="x-small")

        if m == 100 && sgn == 0
            xlabel(L"$1 - \alpha$", size="x-small")
        end
        if m == 50 && sgn == -1
            ylabel("proportion SparKLIE+1 == SparKLIE+2", size="x-small")
        end
    end
end

tight_layout()

savefig("res/exper3_compare_studentized.pdf")

close(fig)


using HDF5

Base.Base.@kwdef mutable struct DelayInfo <: Info
    delays::AbstractArray{Float64,3} = Array{Float64,3}(undef, 0, 0, 0)
    rates::AbstractArray{Float64,3} = Array{Float64,3}(undef, 0, 0, 0)
    time_array::AbstractVector{Float64} = Vector{Float64}(undef, 0)
    jds::AbstractVector{Float64} = Vector{Float64}(undef, 0)
    dut1::Union{Float64,Nothing} = nothing
end # mutable struct DelayInfo

#=
function to_hdf5(group::HDF5.Group, info::DelayInfo)
    invoke(to_hdf5, Tuple{HDF5.Group, Info}, group, info)
end
=#


relaxed_infeas_tols = (tol_infeas = 1e-9,)
insts = OrderedDict()
insts["minimal"] = [
    ((0.7, 4, 1e-3, true, true),),
    ((0.7, 4, 1e-3, false, true), :SOCExpPSD),
    ((2.0, 4, 1e-3, true, false), nothing, relaxed_infeas_tols),
    ((2.0, 4, 1e-3, false, false), :SOCExpPSD, relaxed_infeas_tols),
    ]
insts["fast"] = [
    ((1.0, 2, 1e-3, true, false),),
    ((1.0, 2, 1e-3, false, false), :SOCExpPSD),
    ((4.0, 2, 1e-3, true, false),),
    ((4.0, 2, 1e-3, false, false), :SOCExpPSD),
    ]
insts["various"] = vcat(insts["minimal"], insts["fast"])
return (ContractionJuMP, insts)


precompile(Tuple{Type{Array{T, 2} where T}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(+)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 1}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}}}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(+)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 1}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.ScaledHotVector{Float64}}}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(-)), Tuple{FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(*)), Tuple{Int64, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(-)), Tuple{FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}}}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(-)), Tuple{Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(+)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 2}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}}}})
precompile(Tuple{Type{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Axes, F, Args} where Args<:Tuple where F where Axes}, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}})
precompile(Tuple{Type{FrankWolfe.KSparseLMO{T} where T}, Int64, Float64})
precompile(Tuple{Type{FrankWolfe.LpNormLMO{2, p} where p}, Float64})
precompile(Tuple{Type{FrankWolfe.LpNormLMO{Float64, 1}}, Float64})
precompile(Tuple{Type{FrankWolfe.MultiCacheLMO{500, FrankWolfe.BirkhoffPolytopeLMO, SparseArrays.SparseMatrixCSC{Float64, Int64}}}, FrankWolfe.BirkhoffPolytopeLMO})
precompile(Tuple{Type{FrankWolfe.MultiCacheLMO{500, FrankWolfe.KSparseLMO{Float64}, SparseArrays.SparseVector{Float64, Int64}}}, FrankWolfe.KSparseLMO{Float64}})
precompile(Tuple{Type{FrankWolfe.NuclearNormLMO{T} where T}, Float64})
precompile(Tuple{Type{FrankWolfe.ProbabilitySimplexOracle{Base.Rational{Base.GMP.BigInt}}}, Int64})
precompile(Tuple{Type{FrankWolfe.ProbabilitySimplexOracle{T} where T}, Float64})
precompile(Tuple{Type{FrankWolfe.ProbabilitySimplexOracle{T} where T}, Int64})
precompile(Tuple{Type{FrankWolfe.RankOneMatrix{T, UT, VT} where VT<:(AbstractArray{T, 1} where T) where UT<:(AbstractArray{T, 1} where T) where T}, Array{Float64, 1}, Array{Float64, 1}})
precompile(Tuple{Type{FrankWolfe.VectorCacheLMO{FrankWolfe.BirkhoffPolytopeLMO, SparseArrays.SparseMatrixCSC{Float64, Int64}}}, FrankWolfe.BirkhoffPolytopeLMO})
precompile(Tuple{Type{FrankWolfe.VectorCacheLMO{FrankWolfe.KSparseLMO{Float64}, SparseArrays.SparseVector{Float64, Int64}}}, FrankWolfe.KSparseLMO{Float64}})
precompile(Tuple{Type{FrankWolfe.VectorCacheLMO{FrankWolfe.NuclearNormLMO{Float64}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}}, FrankWolfe.NuclearNormLMO{Float64}})
precompile(Tuple{Type{NamedTuple{(:epsilon, :max_iteration, :line_search, :print_iter, :emphasis, :L, :verbose, :trajectory, :K, :weight_purge_threshold), T} where T<:Tuple}, Tuple{Float64, Int64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Float64, Bool, Bool, Float64, Float64}})
precompile(Tuple{Type{NamedTuple{(:epsilon, :max_iteration, :line_search, :print_iter, :hessian, :emphasis, :L, :accelerated, :verbose, :trajectory, :K, :weight_purge_threshold), T} where T<:Tuple}, Tuple{Float64, Int64, FrankWolfe.Adaptive, Float64, Array{Float64, 2}, FrankWolfe.Emphasis, Float64, Bool, Bool, Bool, Float64, Float64}})
precompile(Tuple{Type{NamedTuple{(:L, :verbose, :line_search, :max_iteration, :print_iter, :trajectory), T} where T<:Tuple}, Tuple{Int64, Bool, FrankWolfe.Adaptive, Int64, Float64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :epsilon, :line_search, :print_iter, :emphasis, :trajectory, :verbose), T} where T<:Tuple}, Tuple{Int64, Float64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :L, :print_iter, :emphasis, :verbose, :epsilon, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Int64, Float64, FrankWolfe.Emphasis, Bool, Float64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :L, :print_iter, :emphasis, :verbose, :epsilon, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Agnostic, Int64, Float64, FrankWolfe.Emphasis, Bool, Float64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :L, :print_iter, :emphasis, :verbose, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Shortstep, Int64, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :L, :print_iter, :epsilon, :emphasis, :verbose, :away_steps, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Int64, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :L, :print_iter, :epsilon, :momentum, :emphasis, :verbose, :away_steps, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Int64, Float64, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :L, :print_iter, :verbose, :emphasis), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.RationalShortstep, Int64, Float64, Bool, FrankWolfe.Emphasis}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :emphasis, :verbose, :epsilon, :trajectory, :away_steps), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Bool, Float64, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :emphasis, :verbose, :epsilon, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Bool, Float64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :emphasis, :verbose, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :emphasis, :verbose, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Agnostic, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :emphasis, :verbose), T} where T<:Tuple}, Tuple{Float64, FrankWolfe.Agnostic, Float64, FrankWolfe.Emphasis, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :emphasis, :verbose), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Agnostic, Float64, FrankWolfe.Emphasis, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :epsilon, :emphasis, :trajectory, :cache_size, :verbose), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, FrankWolfe.Emphasis, Bool, Int64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :epsilon, :emphasis, :trajectory, :verbose), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :epsilon, :emphasis, :verbose, :away_steps, :trajectory), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :epsilon, :emphasis, :verbose, :trajectory, :lazy), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :epsilon, :linesearch_tol, :emphasis, :trajectory, :verbose), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :linesearch_tol, :epsilon, :emphasis, :lazy, :trajectory, :verbose), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :linesearch_tol, :epsilon, :emphasis, :trajectory, :verbose), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Adaptive, Float64, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :verbose, :emphasis), T} where T<:Tuple}, Tuple{Int64, FrankWolfe.Agnostic, Float64, Bool, FrankWolfe.Emphasis}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :line_search, :print_iter, :verbose), T} where T<:Tuple}, Tuple{Float64, FrankWolfe.Nonconvex, Float64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :L, :line_search, :print_iter, :emphasis, :cache_size, :verbose), T} where T<:Tuple}, Tuple{Int64, Int64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Int64, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :L, :line_search, :print_iter, :emphasis, :verbose), T} where T<:Tuple}, Tuple{Int64, Int64, FrankWolfe.Adaptive, Float64, FrankWolfe.Emphasis, Bool}})
precompile(Tuple{Type{NamedTuple{(:max_iteration, :L, :line_search, :print_iter, :linesearch_tol, :emphasis, :trajectory, :verbose), T} where T<:Tuple}, Tuple{Int64, Int64, FrankWolfe.Adaptive, Float64, Float64, FrankWolfe.Emphasis, Bool, Bool}})
precompile(Tuple{Type{NamedTuple{(:momentum, :verbose, :rng, :line_search, :max_iteration, :print_iter, :batch_size, :trajectory), T} where T<:Tuple}, Tuple{Float64, Bool, Random._GLOBAL_RNG, FrankWolfe.Nonconvex, Int64, Float64, Int64, Bool}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :cache_size, :x, :v), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, Int64, Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v, :active_set_length, :non_simplex_iter), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}, Int64, Int64}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v, :active_set_length), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}, Int64}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, Array{Float64, 1}, FrankWolfe.ScaledHotVector{Float64}}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, FrankWolfe.ScaledHotVector{Float64}, FrankWolfe.ScaledHotVector{Float64}}})
precompile(Tuple{Type{NamedTuple{(:t, :primal, :dual, :dual_gap, :time, :x, :v), T} where T<:Tuple}, Tuple{Int64, Float64, Float64, Float64, Float64, SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}}})
precompile(Tuple{Type{NamedTuple{(:verbose, :rng, :line_search, :max_iteration, :print_iter, :batch_size, :trajectory), T} where T<:Tuple}, Tuple{Bool, Random._GLOBAL_RNG, FrankWolfe.Nonconvex, Int64, Float64, Int64, Bool}})
precompile(Tuple{typeof(Base.afoldl), typeof(Base.:(*)), LinearAlgebra.Transpose{Float64, Array{Float64, 1}}, FrankWolfe.ScaledHotVector{Float64}})
precompile(Tuple{typeof(Base.:(+)), Array{Float64, 1}, FrankWolfe.ScaledHotVector{Base.MPFR.BigFloat}})
precompile(Tuple{typeof(Base.:(+)), Array{Float64, 1}, FrankWolfe.ScaledHotVector{Float64}})
precompile(Tuple{typeof(Base.:(*)), Array{Float64, 2}, FrankWolfe.ScaledHotVector{Float64}})
precompile(Tuple{typeof(Base.axes), Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}}})
precompile(Tuple{typeof(Base.Broadcast.broadcasted), Function, Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}})
precompile(Tuple{typeof(Base.Broadcast.broadcasted), Function, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 1}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}}}})
precompile(Tuple{typeof(Base.Broadcast.broadcasted), Function, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 1}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.ScaledHotVector{Float64}}}})
precompile(Tuple{typeof(Base.Broadcast.broadcasted), Function, Float64, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}})
precompile(Tuple{typeof(Base.Broadcast.broadcasted), Function, FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}})
precompile(Tuple{typeof(Base.Broadcast.broadcasted), Function, Int64, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(-)), Tuple{FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Array{Base.Rational{Base.GMP.BigInt}, 1}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(-)), Tuple{Array{Base.Rational{Base.GMP.BigInt}, 1}, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Array{Float64, 1}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(-)), Tuple{Array{Float64, 1}, FrankWolfe.ScaledHotVector{Float64}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Array{Float64, 1}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(+)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 1}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}}}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Array{Float64, 1}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(+)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 1}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.ScaledHotVector{Float64}}}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Array{Float64, 1}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(*)), Tuple{Int64, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(Base.:(-)), Tuple{FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}}}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Array{Float64, 2}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(-)), Tuple{Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize), Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}}})
precompile(Tuple{typeof(Base.Broadcast.materialize!), Base.Broadcast.DefaultArrayStyle{2}, Array{Float64, 2}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(+)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(*)), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(Base.:(-)), Tuple{Int64, Float64}}, Array{Float64, 2}}}, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(Base.:(*)), Tuple{Float64, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}}}}})
precompile(Tuple{typeof(Base.collect), FrankWolfe.ScaledHotVector{Float64}})
precompile(Tuple{typeof(Base.copy), FrankWolfe.ScaledHotVector{Float64}})
precompile(Tuple{typeof(Base.deepcopy), FrankWolfe.ScaledHotVector{Float64}})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{Array{Base.Rational{Base.GMP.BigInt}, 1}, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}, Base.MPFR.BigFloat, Base.Rational{Base.GMP.BigInt}, Array{Any, 1}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{Array{Base.Rational{Base.GMP.BigInt}, 1}, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}, Base.MPFR.BigFloat, Base.Rational{Base.GMP.BigInt}, Array{Any, 1}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{Array{Float64, 1}, FrankWolfe.ScaledHotVector{Float64}, Float64, Float64, Array{Any, 1}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{Array{Float64, 1}, FrankWolfe.ScaledHotVector{Float64}, Float64, Float64, Array{Any, 1}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}, Float64, Float64, Array{Any, 1}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{Array{Float64, 2}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}, Float64, Float64, Array{Any, 1}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseMatrixCSC{Float64, Int64}, SparseArrays.SparseMatrixCSC{Float64, Int64}, Float64, Float64, Array{Any, 1}, FrankWolfe.ActiveSet{SparseArrays.SparseMatrixCSC{Float64, Int64}, Float64, SparseArrays.SparseMatrixCSC{Float64, Int64}}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseMatrixCSC{Float64, Int64}, SparseArrays.SparseMatrixCSC{Float64, Int64}, Float64, Float64, Array{Any, 1}, FrankWolfe.ActiveSet{SparseArrays.SparseMatrixCSC{Float64, Int64}, Float64, SparseArrays.SparseMatrixCSC{Float64, Int64}}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}, Base.Rational{Base.GMP.BigInt}, Base.Rational{Base.GMP.BigInt}, Array{Any, 1}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}, Base.Rational{Base.GMP.BigInt}, Base.Rational{Base.GMP.BigInt}, Array{Any, 1}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}, Float64, Float64, Array{Any, 1}, FrankWolfe.ActiveSet{FrankWolfe.ScaledHotVector{Float64}, Float64, SparseArrays.SparseVector{Float64, Int64}}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}, Float64, Float64, Array{Any, 1}, FrankWolfe.ActiveSet{FrankWolfe.ScaledHotVector{Float64}, Float64, SparseArrays.SparseVector{Float64, Int64}}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}, Float64, Float64, Array{Any, 1}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Float64, Int64}, FrankWolfe.ScaledHotVector{Float64}, Float64, Float64, Array{Any, 1}}, Int64, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Float64, Array{Any, 1}, FrankWolfe.ActiveSet{SparseArrays.SparseVector{Float64, Int64}, Float64, SparseArrays.SparseVector{Float64, Int64}}}, Int64})
precompile(Tuple{typeof(Base.indexed_iterate), Tuple{SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Float64, Array{Any, 1}, FrankWolfe.ActiveSet{SparseArrays.SparseVector{Float64, Int64}, Float64, SparseArrays.SparseVector{Float64, Int64}}}, Int64, Int64})
precompile(Tuple{typeof(Base.length), FrankWolfe.MultiCacheLMO{500, FrankWolfe.BirkhoffPolytopeLMO, SparseArrays.SparseMatrixCSC{Float64, Int64}}})
precompile(Tuple{typeof(Base.length), FrankWolfe.MultiCacheLMO{500, FrankWolfe.KSparseLMO{Float64}, SparseArrays.SparseVector{Float64, Int64}}})
precompile(Tuple{typeof(Base.length), FrankWolfe.VectorCacheLMO{FrankWolfe.BirkhoffPolytopeLMO, SparseArrays.SparseMatrixCSC{Float64, Int64}}})
precompile(Tuple{typeof(Base.length), FrankWolfe.VectorCacheLMO{FrankWolfe.KSparseLMO{Float64}, SparseArrays.SparseVector{Float64, Int64}}})
precompile(Tuple{typeof(Base.length), FrankWolfe.VectorCacheLMO{FrankWolfe.NuclearNormLMO{Float64}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}})
precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, FrankWolfe.Adaptive})
precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, FrankWolfe.Agnostic})
precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, FrankWolfe.Emphasis})
precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, FrankWolfe.Nonconvex})
precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, FrankWolfe.RationalShortstep})
precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, FrankWolfe.Shortstep})
precompile(Tuple{typeof(Base.push!), Array{FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}, 1}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}})
precompile(Tuple{typeof(Base.size), FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}})
precompile(Tuple{typeof(FrankWolfe.backtrackingLS), Function, SparseArrays.SparseMatrixCSC{Float64, Int64}, Array{Float64, 2}, Array{Float64, 2}, Float64})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.BirkhoffPolytopeLMO, Array{Float64, 2}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.KSparseLMO{Float64}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.LpNormLMO{Float64, 1}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.LpNormLMO{Float64, 2}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.MultiCacheLMO{500, FrankWolfe.BirkhoffPolytopeLMO, SparseArrays.SparseMatrixCSC{Float64, Int64}}, SparseArrays.SparseMatrixCSC{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.MultiCacheLMO{500, FrankWolfe.KSparseLMO{Float64}, SparseArrays.SparseVector{Float64, Int64}}, SparseArrays.SparseVector{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.NuclearNormLMO{Float64}, SparseArrays.SparseMatrixCSC{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.ProbabilitySimplexOracle{Base.Rational{Base.GMP.BigInt}}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.ProbabilitySimplexOracle{Float64}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.VectorCacheLMO{FrankWolfe.BirkhoffPolytopeLMO, SparseArrays.SparseMatrixCSC{Float64, Int64}}, SparseArrays.SparseMatrixCSC{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.VectorCacheLMO{FrankWolfe.KSparseLMO{Float64}, SparseArrays.SparseVector{Float64, Int64}}, SparseArrays.SparseVector{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.compute_extreme_point), FrankWolfe.VectorCacheLMO{FrankWolfe.NuclearNormLMO{Float64}, FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}}, SparseArrays.SparseMatrixCSC{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), Array{Float64, 1}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), Array{Float64, 1}, SparseArrays.SparseVector{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), Array{Float64, 2}, Array{Float64, 2}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}, Array{Float64, 2}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), FrankWolfe.RankOneMatrix{Float64, Array{Float64, 1}, Array{Float64, 1}}, SparseArrays.SparseMatrixCSC{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), FrankWolfe.ScaledHotVector{Float64}, SparseArrays.SparseVector{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), SparseArrays.SparseMatrixCSC{Float64, Int64}, Array{Float64, 2}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), SparseArrays.SparseMatrixCSC{Float64, Int64}, SparseArrays.SparseMatrixCSC{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), SparseArrays.SparseVector{Float64, Int64}, Array{Float64, 1}})
precompile(Tuple{typeof(FrankWolfe.fast_dot), SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, Array{Float64, 1}, Array{Float64, 1}, Array{Float64, 1}, Float64, Float64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, Array{Float64, 1}, Array{Float64, 1}, Array{Float64, 1}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, Array{Float64, 2}, Array{Float64, 2}, SparseArrays.SparseMatrixCSC{Float64, Int64}, Float64, Float64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, SparseArrays.SparseMatrixCSC{Float64, Int64}, SparseArrays.SparseMatrixCSC{Float64, Int64}, SparseArrays.SparseMatrixCSC{Float64, Int64}, Float64, Float64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Float64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Float64, Int64, Float64, Int64, Int64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Adaptive, Int64, Function, Function, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Int64, Int64, Float64, Int64, Int64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.RationalShortstep, Int64, Function, Function, FrankWolfe.ScaledHotVector{Base.Rational{Base.GMP.BigInt}}, SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, Base.Rational{Base.GMP.BigInt}, Int64, Int64, Float64, Int64, Base.Rational{Base.GMP.BigInt}})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.RationalShortstep, Int64, Function, Function, SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, SparseArrays.SparseVector{Base.Rational{Base.GMP.BigInt}, Int64}, Base.Rational{Base.GMP.BigInt}, Int64, Int64, Float64, Int64, Base.Rational{Base.GMP.BigInt}})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Shortstep, Int64, Function, Function, Array{Float64, 1}, Array{Float64, 1}, Array{Float64, 1}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Shortstep, Int64, Function, Function, Array{Float64, 1}, Array{Float64, 1}, SparseArrays.SparseVector{Float64, Int64}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Shortstep, Int64, Function, Function, FrankWolfe.ScaledHotVector{Float64}, Array{Float64, 1}, SparseArrays.SparseVector{Float64, Int64}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Shortstep, Int64, Function, Function, FrankWolfe.ScaledHotVector{Float64}, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.line_search_wrapper), FrankWolfe.Shortstep, Int64, Function, Function, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, SparseArrays.SparseVector{Float64, Int64}, Float64, Int64, Int64, Float64, Int64, Float64})
precompile(Tuple{typeof(FrankWolfe.plot_trajectories), Array{Array{Any, 1}, 1}, Array{String, 1}})
precompile(Tuple{typeof(FrankWolfe.plot_trajectories), Array{Array{Any, 1}, 1}, Array{String, 2}})
precompile(Tuple{typeof(FrankWolfe.print_callback), Tuple{String, String, Float64, Float64, Float64, Float64, Float64, Int64, Int64}, String})
precompile(Tuple{typeof(FrankWolfe.print_callback), Tuple{String, String, Float64, Float64, Float64, Float64, Float64, Int64}, String})
precompile(Tuple{typeof(FrankWolfe.print_callback), Tuple{String, String, Float64, Float64, Float64, Float64, Float64}, String})
precompile(Tuple{typeof(LinearAlgebra.dot), Array{Float64, 1}, FrankWolfe.ScaledHotVector{Float64}})


abstract type AbstractRenewableFormulation <: AbstractDeviceFormulation end

abstract type AbstractRenewableDispatchForm <: AbstractRenewableFormulation end

struct RenewableFixed <: AbstractRenewableFormulation end

struct RenewableFullDispatch <: AbstractRenewableDispatchForm end

struct RenewableConstantPowerFactor <: AbstractRenewableDispatchForm end

# renewable variables

function activepower_variables(ps_m::CanonicalModel, devices::Array{R,1}, time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen}

    add_variable(ps_m,
                 devices,
                 time_range,
                 :Pre,
                 false,
                 :var_active)

    return

end

function reactivepower_variables(ps_m::CanonicalModel, devices::Array{R,1}, time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen}

    add_variable(ps_m,
                 devices,
                 time_range,
                 :Qre,
                 false,
                 :var_reactive)

    return

end

###### output constraints without Time Series ##############

function activepower_constraints(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                device_formulation::Type{D},
                                system_formulation::Type{S},
                                parameters::Bool) where {R <: PSY.RenewableGen,
                                                                    D <: AbstractRenewableDispatchForm,
                                                                    S <: PM.AbstractPowerFormulation}

    if parameters
        device_timeseries_param_ub(ps_m,
                                    _get_time_series(devices),
                                    time_range,
                                    :renewable_active_ub,
                                    Symbol("Pre_$(eltype(devices))"),
                                    :Pre)
    else
        device_timeseries_ub(ps_m,
                            _get_time_series(devices),
                            time_range,
                            :renewable_active_ub,
                            :Pre)
    end

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                device_formulation::Type{RenewableFullDispatch},
                                system_formulation::Type{S}) where {R <: PSY.RenewableGen,
                                                                    S <: PM.AbstractPowerFormulation}

    range_data = [(r.name, r.tech.reactivepowerlimits) for r in devices]

    device_range(ps_m,
                range_data ,
                time_range,
                :renewable_reactive_range,
                :Qre)

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                device_formulation::Type{RenewableConstantPowerFactor},
                                system_formulation::Type{S}) where {R <: PSY.RenewableGen,
                                                                    S <: PM.AbstractPowerFormulation}

    names = [r.name for r in devices]

    ps_m.constraints[:renewable_reactive] = JuMP.Containers.DenseAxisArray{JuMP.ConstraintRef}(undef, names, time_range)

    for t in time_range, d in devices
        ps_m.constraints[:renewable_reactive][d.name, t] = JuMP.@constraint(ps_m.JuMPmodel, ps_m.variables[:Qre][d.name, t] == ps_m.variables[:Pre][d.name, t]*sin(acos(d.tech.powerfactor)))
    end

    return

end

###### output constraints with Time Series ##############

function _get_time_series(devices::Array{T}) where {T <: PSY.RenewableGen}

    names = Vector{String}(undef, length(devices))
    series = Vector{Vector{Float64}}(undef, length(devices))

    for (ix,d) in enumerate(devices)
        names[ix] = d.name
        series[ix] = values(d.scalingfactor)*d.tech.installedcapacity
    end

    return names, series

end

function activepower_constraints(ps_m::CanonicalModel,
                                 devices::Array{R,1},
                                 device_formulation::Type{D},
                                 system_formulation::Type{S},
                                 time_range::UnitRange{Int64},
                                 parameters::Bool) where {R <: PSY.RenewableGen,
                                                                      D <: AbstractRenewableDispatchForm,
                                                                      S <: PM.AbstractPowerFormulation}

    if parameters
        device_timeseries_param_ub(ps_m,
                                   _get_time_series(devices),
                                   time_range,
                                   :renewable_active_ub,
                                   Symbol("Pre_$(eltype(devices))"),
                                   :Pre)
    else
        device_timeseries_ub(ps_m,
                            _get_time_series(devices),
                            time_range,
                            :renewable_active_ub,
                            :Pre)
    end

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                   devices::Array{R,1},
                                   device_formulation::Type{RenewableFullDispatch},
                                   system_formulation::Type{S},
                                   time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                        S <: PM.AbstractPowerFormulation}

    range_data = [(r.name, r.tech.reactivepowerlimits) for r in devices]

    device_range(ps_m,
                range_data ,
                time_range,
                :renewable_reactive_range,
                :Qre)

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                   devices::Array{R,1},
                                   device_formulation::Type{RenewableConstantPowerFactor},
                                   system_formulation::Type{S},
                                   time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                        S <: PM.AbstractPowerFormulation}

    names = [r.name for r in devices]

    ps_m.constraints[:renewable_reactive] = JuMP.Containers.DenseAxisArray{JuMP.ConstraintRef}(undef, names, time_range)

    for t in time_range, d in devices
        ps_m.constraints[:renewable_reactive][d.name, t] = JuMP.@constraint(ps_m.JuMPmodel, ps_m.variables[:Qre][d.name, t] == ps_m.variables[:Pre][d.name, t]*sin(acos(d.tech.powerfactor)))
    end

    return

end

##### Injection Expression with parameters #####

function _nodal_expression_param(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractPowerFormulation}

    ts_data_active = Vector{Tuple{String,Int64,Vector{Float64}}}(undef, length(devices))
    ts_data_reactive = Vector{Tuple{String,Int64,Vector{Float64}}}(undef, length(devices))

    for (ix,d) in enumerate(devices)
        ts_data_active[ix] = (d.name, d.bus.number, values(d.scalingfactor)*d.tech.installedcapacity)
        ts_data_reactive[ix] = (d.name, d.bus.number, values(d.scalingfactor)*d.tech.installedcapacity)
    end

    add_parameters(ps_m,
                   ts_data_active,
                   time_range,
                   Symbol("Pre_$(eltype(devices))"),
                   :var_active)
    add_parameters(ps_m,
                   ts_data_reactive,
                   time_range,
                   Symbol("Qre_$(eltype(devices))"),
                   :var_reactive)

    return

end

function _nodal_expression_param(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractActivePowerFormulation}

    ts_data_active = Vector{Tuple{String,Int64,Vector{Float64}}}(undef, length(devices))

    for (ix,d) in enumerate(devices)
        ts_data_active[ix] = (d.name, d.bus.number, values(d.scalingfactor)*d.tech.installedcapacity)
    end

    add_parameters(ps_m,
                   ts_data_active,
                   time_range,
                   Symbol("Pre_$(eltype(devices))"),
                   :var_active)
    return

end

###### injection expression with fixed values #######

function _nodal_expression_fixed(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractPowerFormulation}

    for t in time_range, d in devices
        _add_to_expression!(ps_m.expressions[:var_active],
                            d.bus.number,
                            t,
                            d.tech.installedcapacity * values(d.scalingfactor)[t])
        _add_to_expression!(ps_m.expressions[:var_reactive],
                            d.bus.number,
                            t,
                            d.tech.installedcapacity * values(d.scalingfactor)[t]*sin(acos(d.tech.powerfactor)))
    end

    return

end

function _nodal_expression_fixed(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractActivePowerFormulation}

    for t in time_range, d in devices
        _add_to_expression!(ps_m.expressions[:var_active],
                            d.bus.number,
                            t,
                            d.tech.installedcapacity * values(d.scalingfactor)[t])
    end

    return

end

function nodal_expression(ps_m::CanonicalModel,
                          devices::Array{R,1},
                          system_formulation::Type{S},
                          time_range::UnitRange{Int64},
                          parameters::Bool) where {R <: PSY.RenewableGen,
                                                          S <: PM.AbstractPowerFormulation}

    if parameters
        _nodal_expression_param(ps_m, devices, system_formulation, time_range)
    else
        _nodal_expression_fixed(ps_m, devices, system_formulation, time_range)
    end

    return

end

######## renewable generation cost ##############

function cost_function(ps_m::CanonicalModel,
                       devices::Array{PSY.RenewableCurtailment,1},
                       device_formulation::Type{D},
                       system_formulation::Type{S}) where {D <: AbstractRenewableDispatchForm,
                                                           S <: PM.AbstractPowerFormulation}

    add_to_cost(ps_m, devices, :Pre, :curtailpenalty, -1)

    return

end


using FastAI

DATASETNAME = "imagenette2-160";

##

taskdata = Datasets.loadtaskdata(Datasets.datasetpath(DATASETNAME), ImageClassificationTask)
image, class = getobs(taskdata, 1)


function summarize_regression_test(fields, correct_fields)
    for (field_name, φ, φ_c) in zip(keys(fields), fields, correct_fields)
        Δ = φ .- φ_c

        Δ_min      = minimum(Δ)
        Δ_max      = maximum(Δ)
        Δ_mean     = mean(Δ)
        Δ_abs_mean = mean(abs, Δ)
        Δ_std      = std(Δ)

        matching    = sum(φ .≈ φ_c)
        grid_points = length(φ_c)

        @info @sprintf("Δ%s: min=%+.6e, max=%+.6e, mean=%+.6e, absmean=%+.6e, std=%+.6e (%d/%d matching grid points)",
                       field_name, Δ_min, Δ_max, Δ_mean, Δ_abs_mean, Δ_std, matching, grid_points)
    end
end

function get_fields_from_checkpoint(filename)
    file = jldopen(filename)

    tracers = keys(file["tracers"])
    tracers = Tuple(Symbol(c) for c in tracers)

    velocity_fields = (u = file["velocities/u/data"],
                       v = file["velocities/v/data"],
                       w = file["velocities/w/data"])

    tracer_fields =
        NamedTuple{tracers}(Tuple(file["tracers/$c/data"] for c in tracers))

    current_tendency_velocity_fields = (u = file["timestepper/Gⁿ/u/data"],
                                        v = file["timestepper/Gⁿ/v/data"],
                                        w = file["timestepper/Gⁿ/w/data"])

    current_tendency_tracer_fields =
        NamedTuple{tracers}(Tuple(file["timestepper/Gⁿ/$c/data"] for c in tracers))

    previous_tendency_velocity_fields = (u = file["timestepper/G⁻/u/data"],
                                         v = file["timestepper/G⁻/v/data"],
                                         w = file["timestepper/G⁻/w/data"])

    previous_tendency_tracer_fields =
        NamedTuple{tracers}(Tuple(file["timestepper/G⁻/$c/data"] for c in tracers))

    close(file)

    solution = merge(velocity_fields, tracer_fields)
    Gⁿ = merge(current_tendency_velocity_fields, current_tendency_tracer_fields)
    G⁻ = merge(previous_tendency_velocity_fields, previous_tendency_tracer_fields)

    return solution, Gⁿ, G⁻
end

include("regression_tests/thermal_bubble_regression_test.jl")
include("regression_tests/rayleigh_benard_regression_test.jl")
include("regression_tests/ocean_large_eddy_simulation_regression_test.jl")

@testset "Regression" begin
    @info "Running regression tests..."

    for arch in archs
        @testset "Thermal bubble [$(typeof(arch))]" begin
            @info "  Testing thermal bubble regression [$(typeof(arch))]"
            run_thermal_bubble_regression_test(arch)
        end

        @testset "Rayleigh–Bénard tracer [$(typeof(arch))]" begin
            @info "  Testing Rayleigh–Bénard tracer regression [$(typeof(arch))]"
            run_rayleigh_benard_regression_test(arch)
        end

        @testset "Ocean large eddy simulation [$(typeof(arch))]" begin
            for closure in (AnisotropicMinimumDissipation(), ConstantSmagorinsky())
                closurename = string(typeof(closure).name.wrapper)
                @info "  Testing oceanic large eddy simulation regression [$closurename, $(typeof(arch))]"
                run_ocean_large_eddy_simulation_regression_test(arch, closure)
            end
        end
    end
end


#=
Author : Ng Yeesian
Modified : Guy Farmer / Zander Tedjo / Michael Hilborn
defines various deployment and dispatch models and functions that will be run on them
=#
# using Gurobi
abstract type DeploymentModel end

abstract type DispatchModel end

#=function respond_to!(redeploy::RedeployModel, i::Int, t::Int)
    @assert length(redeploy.ambulances[i]) > 0 "$i: $(redeploy.ambulances[i])"
    amb = popfirst!(redeploy.ambulances[i])
    @assert amb != 0
    @assert redeploy.status[amb] == :available "$amb: $(redeploy.status[amb])"
    redeploy.status[amb] = :responding
    redeploy.fromtime[amb] = t
    amb
end

function arriveatscene!(redeploy::RedeployModel, amb::Int, t::Int)
    @assert redeploy.status[amb] == :responding "$amb: $(redeploy.status[amb])"
    @assert redeploy.hospital[amb] == 0
    redeploy.status[amb] = :atscene
    redeploy.fromtime[amb] = t
end

function conveying!(redeploy::RedeployModel, amb::Int, hosp::Int, t::Int)
    @assert redeploy.status[amb] == :atscene "$amb: $(redeploy.status[amb])"
    @assert redeploy.hospital[amb] != 0
    redeploy.status[amb] = :conveying
    redeploy.fromtime[amb] = t
    redeploy.hospital[amb] = hosp
end

function returning_to!(redeploy::RedeployModel, amb::Int, t::Int)
    @assert redeploy.status[amb] == :conveying "$amb: $(redeploy.status[amb])"
    @assert redeploy.hospital[amb] != 0
    redeploy.status[amb] = :returning
    redeploy.fromtime[amb] = t
    redeploy.assignment[amb]
end

function redeploying_to!(redeploy::RedeployModel, amb::Int, i::Int, t::Int)
    # DEBUG:
    println("redeploying amb $amb from $(redeploy.assignment[amb]) to $i")
    ambulances = redeploy.ambulances[redeploy.assignment[amb]]
    @assert !in(amb, redeploy.ambulances[i])
    @assert in(amb, ambulances)
    deleteat!(ambulances, findfirst(ambulances, amb))
    @assert !in(amb, ambulances)
    redeploy.assignment[amb] = i
    redeploy.status[amb] = :redeploying
    redeploy.fromtime[amb] = t
end

function returned_to!(redeploy::RedeployModel, amb::Int, t::Int)
    @assert in(redeploy.status[amb], (:returning, :redeploying)) "$amb: $(redeploy.status[amb])"
    redeploy.hospital[amb] = 0
    redeploy.status[amb] = :available
    redeploy.fromtime[amb] = t
    @assert !in(amb, redeploy.ambulances[redeploy.assignment[amb]])
    push!(redeploy.ambulances[redeploy.assignment[amb]], amb)
end

function redirected!(redeploy::RedeployModel, amb::Int, t::Int)
    @assert in(redeploy.status[amb], (:returning, :redeploying)) "$amb: $(redeploy.status[amb])"
    redeploy.hospital[amb] = 0
    redeploy.status[amb] = :responding
    redeploy.fromtime[amb] = t
end
=#


mutable struct Header
    tracenum::Int32
    o1::Float32
    n1::Int32
    d1::Float32
    sx::Float32
    sy::Float32
    gx::Float32
    gy::Float32
    mx::Float32
    my::Float32
    hx::Float32
    hy::Float32
    h::Float32
    az::Float32
    ang::Float32
    isx::Int32
    isy::Int32
    igx::Int32
    igy::Int32
    imx::Int32
    imy::Int32
    ihx::Int32
    ihy::Int32
    ih::Int32
    iaz::Int32
    iang::Int32
    selev::Float32
    gelev::Float32
    sstat::Float32
    gstat::Float32
    trid::Int32
end

header_count = Dict{AbstractString,Int32}()
header_count["tracenum"] = 0
header_count["o1"]    = 4
header_count["n1"]    = 8
header_count["d1"]    = 12
header_count["sx"]    = 16
header_count["sy"]    = 20
header_count["gx"]    = 24
header_count["gy"]    = 28
header_count["mx"]    = 32
header_count["my"]    = 36
header_count["hx"]    = 40
header_count["hy"]    = 44
header_count["h"]     = 48
header_count["az"]    = 52
header_count["ang"]   = 56
header_count["isx"]   = 60
header_count["isy"]   = 64
header_count["igx"]   = 68
header_count["igy"]   = 72
header_count["imx"]   = 76
header_count["imy"]   = 80
header_count["ihx"]   = 84
header_count["ihy"]   = 88
header_count["ih"]    = 92
header_count["iaz"]   = 96
header_count["iang"]  = 100
header_count["selev"] = 104
header_count["gelev"] = 108
header_count["sstat"] = 112
header_count["gstat"] = 116
header_count["trid"]  = 120

"""
    InitSeisHeader

Initialize a variable of composite type Header corresponding to the data header in seis format.
All the fields are initialized to 0.0.
Type ?SeisMain.Header for a detail of the fields included.
"""
function InitSeisHeader()
    h = Header(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
	       0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
	       0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
	       0.0)
    return h
end

"""
    GrabHeader(stream,j)

Extract the header from trace j of a file in seis format.

# Arguments
- `stream::IOStream`: data file in seis format
- `j::Integer`: Trace number
"""
function GrabHeader(stream,j)
    position = 4*length(fieldnames(Header))*(j-1)
    seek(stream,position)
    h = InitSeisHeader()
    h.tracenum = read(stream,typeof(h.tracenum))
    h.o1    = read(stream,typeof(h.o1))
    h.n1    = read(stream,typeof(h.n1))
    h.d1    = read(stream,typeof(h.d1))
    h.sx    = read(stream,typeof(h.sx))
    h.sy    = read(stream,typeof(h.sy))
    h.gx    = read(stream,typeof(h.gx))
    h.gy    = read(stream,typeof(h.gy))
    h.mx    = read(stream,typeof(h.mx))
    h.my    = read(stream,typeof(h.my))
    h.hx    = read(stream,typeof(h.hx))
    h.hy    = read(stream,typeof(h.hy))
    h.h     = read(stream,typeof(h.h))
    h.az    = read(stream,typeof(h.az))
    h.ang   = read(stream,typeof(h.ang))
    h.isx   = read(stream,typeof(h.isx))
    h.isy   = read(stream,typeof(h.isy))
    h.igx   = read(stream,typeof(h.igx))
    h.igy   = read(stream,typeof(h.igy))
    h.imx   = read(stream,typeof(h.imx))
    h.imy   = read(stream,typeof(h.imy))
    h.ihx   = read(stream,typeof(h.ihx))
    h.ihy   = read(stream,typeof(h.ihy))
    h.ih    = read(stream,typeof(h.ih))
    h.iaz   = read(stream,typeof(h.iaz))
    h.iang  = read(stream,typeof(h.iang))
    h.selev = read(stream,typeof(h.selev))
    h.gelev = read(stream,typeof(h.gelev))
    h.sstat = read(stream,typeof(h.sstat))
    h.gstat = read(stream,typeof(h.gstat))
    h.trid  = read(stream,typeof(h.trid))
    return h
end

"""
    PutHeader(stream,j)

Write the header of trace j to a file in seis format.

# Arguments
- `stream::IOStream`: data file in seis format
- `j::Integer`: Trace number
"""
function PutHeader(stream,h,j)
    position = 4*length(fieldnames(Header))*(j-1)
    seek(stream,position)
    write(stream,h.tracenum)
    write(stream,h.o1)
    write(stream,h.n1)
    write(stream,h.d1)
    write(stream,h.sx)
    write(stream,h.sy)
    write(stream,h.gx)
    write(stream,h.gy)
    write(stream,h.mx)
    write(stream,h.my)
    write(stream,h.hx)
    write(stream,h.hy)
    write(stream,h.h)
    write(stream,h.az)
    write(stream,h.ang)
    write(stream,h.isx)
    write(stream,h.isy)
    write(stream,h.igx)
    write(stream,h.igy)
    write(stream,h.imx)
    write(stream,h.imy)
    write(stream,h.ihx)
    write(stream,h.ihy)
    write(stream,h.ih)
    write(stream,h.iaz)
    write(stream,h.iang)
    write(stream,h.selev)
    write(stream,h.gelev)
    write(stream,h.sstat)
    write(stream,h.gstat)
    write(stream,h.trid)
end

primitive type Header32Bits 32 end

"""
    BitsToHeader(h_in)

Reinterpret an input header to its correct type.
The function outputs a variable of composite type Header.
"""
function BitsToHeader(h_in)
    h = InitSeisHeader()
    h.tracenum = reinterpret(typeof(h.tracenum),h_in[1])
    h.o1 = reinterpret(typeof(h.o1),h_in[2])
    h.n1 = reinterpret(typeof(h.n1),h_in[3])
    h.d1 = reinterpret(typeof(h.d1),h_in[4])
    h.sx = reinterpret(typeof(h.sx),h_in[5])
    h.sy = reinterpret(typeof(h.sy),h_in[6])
    h.gx = reinterpret(typeof(h.gx),h_in[7])
    h.gy = reinterpret(typeof(h.gy),h_in[8])
    h.mx = reinterpret(typeof(h.mx),h_in[9])
    h.my = reinterpret(typeof(h.my),h_in[10])
    h.hx = reinterpret(typeof(h.hx),h_in[11])
    h.hy = reinterpret(typeof(h.hy),h_in[12])
    h.h = reinterpret(typeof(h.h),h_in[13])
    h.az = reinterpret(typeof(h.az),h_in[14])
    h.ang = reinterpret(typeof(h.ang),h_in[15])
    h.isx = reinterpret(typeof(h.isx),h_in[16])
    h.isy = reinterpret(typeof(h.isy),h_in[17])
    h.igx = reinterpret(typeof(h.igx),h_in[18])
    h.igy = reinterpret(typeof(h.igy),h_in[19])
    h.imx = reinterpret(typeof(h.imx),h_in[20])
    h.imy = reinterpret(typeof(h.imy),h_in[21])
    h.ihx = reinterpret(typeof(h.ihx),h_in[22])
    h.ihy = reinterpret(typeof(h.ihy),h_in[23])
    h.ih = reinterpret(typeof(h.ih),h_in[24])
    h.iaz = reinterpret(typeof(h.iaz),h_in[25])
    h.iang = reinterpret(typeof(h.iang),h_in[26])
    h.selev = reinterpret(typeof(h.selev),h_in[27])
    h.gelev = reinterpret(typeof(h.gelev),h_in[28])
    h.sstat = reinterpret(typeof(h.sstat),h_in[29])
    h.gstat = reinterpret(typeof(h.gstat),h_in[30])
    h.trid = reinterpret(typeof(h.trid),h_in[31])
    return h
end

"""
    HeaderToBits(h_in)

Reinterpret a composite type variable of type Header to 32 bits
The function outputs an Vector with 31 elements of type Header32Bit.
"""
function HeaderToBits(h);
    h_out = [reinterpret(Header32Bits,h.tracenum);
	     reinterpret(Header32Bits,h.o1);
	     reinterpret(Header32Bits,h.n1);
	     reinterpret(Header32Bits,h.d1);
	     reinterpret(Header32Bits,h.sx);
	     reinterpret(Header32Bits,h.sy);
	     reinterpret(Header32Bits,h.gx);
	     reinterpret(Header32Bits,h.gy);
	     reinterpret(Header32Bits,h.mx);
	     reinterpret(Header32Bits,h.my);
	     reinterpret(Header32Bits,h.hx);
	     reinterpret(Header32Bits,h.hy);
	     reinterpret(Header32Bits,h.h);
	     reinterpret(Header32Bits,h.az);
	     reinterpret(Header32Bits,h.ang);
	     reinterpret(Header32Bits,h.isx);
	     reinterpret(Header32Bits,h.isy);
	     reinterpret(Header32Bits,h.igx);
	     reinterpret(Header32Bits,h.igy);
	     reinterpret(Header32Bits,h.imx);
	     reinterpret(Header32Bits,h.imy);
	     reinterpret(Header32Bits,h.ihx);
	     reinterpret(Header32Bits,h.ihy);
	     reinterpret(Header32Bits,h.ih);
	     reinterpret(Header32Bits,h.iaz);
	     reinterpret(Header32Bits,h.iang);
	     reinterpret(Header32Bits,h.selev);
	     reinterpret(Header32Bits,h.gelev);
	     reinterpret(Header32Bits,h.sstat);
	     reinterpret(Header32Bits,h.gstat);
	     reinterpret(Header32Bits,h.trid)]
    return h_out
end

"""
    GetNumTraces(file)

Calculate the total number of traces in a data file with seis format
"""
function GetNumTraces(in)

    filename_h = ParseHeaderName(in)
    nhead = length(fieldnames(Header))
    stream_h = open(filename_h)
    nx = round(Int,filesize(stream_h)/(nhead*4))
    close(stream_h)
    return nx

end

"""
    ParseHeaderName(file::AbstractString)

Parse the name of the header file from an extent file to a String variable.
"""
function ParseHeaderName(filename::AbstractString)

    f = open(filename,"r")
    fstring = read(f,String)
    close(f)
    ini = first(something(findlast("\theaders=",fstring), 0:-1))
    ini == 0 ? headers = "NULL" : headers = fstring[something(findnext(r"\theaders=.*", fstring, ini), 0:-1)][11:end-1]
    return headers

end

"""
    ParseDataName(file::AbstractString)

Parse the name of the data file from an extent file to a String variable.
"""
function ParseDataName(filename::AbstractString)

    f = open(filename,"r")
    fstring = read(f,String)
    close(f)
    ini = first(something(findlast("\tin=",fstring), 0:-1))
    ini == 0 ? in = "NULL" : in = fstring[something(findnext(r"\tin=.*", fstring, ini), 0:-1)][6:end-1]

    return in

end

"""
    ParseDataFormat(file::AbstractString)

Parse the format of the data type in the extent file to a String variable.
"""
function ParseDataFormat(filename::AbstractString)

    f = open(filename,"r")
    fstring = read(f,String)
    close(f)
    ini = first(something(findlast("\tdata_format=",fstring), 0:-1))
    ini == 0 ? data_format = "native_float" : data_format = fstring[something(findnext(r"\tdata_format=.*", fstring, ini), 0:-1)][15:end-1]
    return data_format

end

"""
    ParseDataESize(file::AbstractString)

Parse the esize in the extent file to a Int32 variable.
"""
function ParseDataESize(filename::AbstractString)

    f = open(filename,"r")
    fstring = read(f,String)
    close(f)
    ini = first(something(findlast("\tesize=",fstring), 0:-1))
    ini == 0 ? esize = Int32(4) : esize = parse(Int32, fstring[something(findnext(r"\tesize=.*", fstring, ini), 0:-1)][8:end])
    return esize

end

"""
    ExtractHeader(h::Array{Header,1},key::AbstractString)

Extracts the values of a field in the header. The output is a vector of the same type as the field.
"""
function ExtractHeader(h::Array{Header,1},key::AbstractString)

    keytype = eval(Meta.parse("typeof(SeisMain.InitSeisHeader().$(key))"))
    out = keytype[]
    for ix = 1 : length(h)
	push!(out,getfield(h[ix],Symbol(key)))
    end
    return out

end

mutable struct Extent
    n1::Int32
    n2::Int32
    n3::Int32
    n4::Int32
    n5::Int32
    o1::Float32
    o2::Float32
    o3::Float32
    o4::Float32
    o5::Float32
    d1::Float32
    d2::Float32
    d3::Float32
    d4::Float32
    d5::Float32
    label1::AbstractString
    label2::AbstractString
    label3::AbstractString
    label4::AbstractString
    label5::AbstractString
    unit1::AbstractString
    unit2::AbstractString
    unit3::AbstractString
    unit4::AbstractString
    unit5::AbstractString
    title::AbstractString
end

"""
    ReadTextHeader(filein)

Reads the extent file of data in seis format.
The result outputs in a variable of composite type Extent.
Type ?SeisMain.Extent for a detail on the included fields.
"""
function ReadTextHeader(filename)

    f = open(filename,"r")
    fstring = read(f,String)
    close(f)
    ini = first(something(findlast("\tn1=",fstring),0:-1))
    ini == 0 ? n1 = Int32(1) : n1 = parse(Int32, fstring[something(findnext(r"\tn1=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\tn2=",fstring),0:-1))
    ini == 0 ? n2 = Int32(1) : n2 = parse(Int32, fstring[something(findnext(r"\tn2=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\tn3=",fstring),0:-1))
    ini == 0 ? n3 = Int32(1) : n3 = parse(Int32, fstring[something(findnext(r"\tn3=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\tn4=",fstring),0:-1))
    ini == 0 ? n4 = Int32(1) : n4 = parse(Int32, fstring[something(findnext(r"\tn4=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\tn5=",fstring),0:-1))
    ini == 0 ? n5 = Int32(1) : n5 = parse(Int32, fstring[something(findnext(r"\tn5=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\to1=",fstring),0:-1))
    ini == 0 ? o1 = Float32(0) : o1 = parse(Float32, fstring[something(findnext(r"\to1=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\to2=",fstring),0:-1))
    ini == 0 ? o2 = Float32(0) : o2 = parse(Float32, fstring[something(findnext(r"\to2=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\to3=",fstring),0:-1))
    ini == 0 ? o3 = Float32(0) : o3 = parse(Float32, fstring[something(findnext(r"\to3=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\to4=",fstring),0:-1))
    ini == 0 ? o4 = Float32(0) : o4 = parse(Float32, fstring[something(findnext(r"\to4=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\to5=",fstring),0:-1))
    ini == 0 ? o5 = Float32(0) : o5 = parse(Float32, fstring[something(findnext(r"\to5=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\td1=",fstring),0:-1))
    ini == 0 ? d1 = Float32(1) : d1 = parse(Float32, fstring[something(findnext(r"\td1=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\td2=",fstring),0:-1))
    ini == 0 ? d2 = Float32(1) : d2 = parse(Float32, fstring[something(findnext(r"\td2=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\td3=",fstring),0:-1))
    ini == 0 ? d3 = Float32(1) : d3 = parse(Float32, fstring[something(findnext(r"\td3=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\td4=",fstring),0:-1))
    ini == 0 ? d4 = Float32(1) : d4 = parse(Float32, fstring[something(findnext(r"\td4=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\td5=",fstring),0:-1))
    ini == 0 ? d5 = Float32(1) : d5 = parse(Float32, fstring[something(findnext(r"\td5=.*",fstring,ini))][5:end])
    ini = first(something(findlast("\tlabel1=",fstring),0:-1))
    ini == 0 ? label1 = "" : label1 = fstring[something(findnext(r"\tlabel1=.*",fstring,ini))][10:end-1]
    ini = first(something(findlast("\tlabel2=",fstring),0:-1))
    ini == 0 ? label2 = "" : label2 = fstring[something(findnext(r"\tlabel2=.*",fstring,ini))][10:end-1]
    ini = first(something(findlast("\tlabel3=",fstring),0:-1))
    ini == 0 ? label3 = "" : label3 = fstring[something(findnext(r"\tlabel3=.*",fstring,ini))][10:end-1]
    ini = first(something(findlast("\tlabel4=",fstring),0:-1))
    ini == 0 ? label4 = "" : label4 = fstring[something(findnext(r"\tlabel4=.*",fstring,ini))][10:end-1]
    ini = first(something(findlast("\tlabel5=",fstring),0:-1))
    ini == 0 ? label5 = "" : label5 = fstring[something(findnext(r"\tlabel5=.*",fstring,ini))][10:end-1]
    ini = first(something(findlast("\tunit1=",fstring),0:-1))
    ini == 0 ? unit1 = "" : unit1 = fstring[something(findnext(r"\tunit1=.*",fstring,ini))][9:end-1]
    ini = first(something(findlast("\tunit2=",fstring),0:-1))
    ini == 0 ? unit2 = "" : unit2 = fstring[something(findnext(r"\tunit2=.*",fstring,ini))][9:end-1]
    ini = first(something(findlast("\tunit3=",fstring),0:-1))
    ini == 0 ? unit3 = "" : unit3 = fstring[something(findnext(r"\tunit3=.*",fstring,ini))][9:end-1]
    ini = first(something(findlast("\tunit4=",fstring),0:-1))
    ini == 0 ? unit4 = "" : unit4 = fstring[something(findnext(r"\tunit4=.*",fstring,ini))][9:end-1]
    ini = first(something(findlast("\tunit5=",fstring),0:-1))
    ini == 0 ? unit5 = "" : unit5 = fstring[something(findnext(r"\tunit5=.*",fstring,ini))][9:end-1]
    ini = first(something(findlast("\ttitle=",fstring),0:-1))
    ini == 0 ? title = "" : title = fstring[something(findnext(r"\ttitle=.*",fstring,ini))][9:end-1]
    extent = Extent(convert(Int32,n1),convert(Int32,n2),convert(Int32,n3),convert(Int32,n4),convert(Int32,n5),
		    convert(Float32,o1),convert(Float32,o2),convert(Float32,o3),convert(Float32,o4),convert(Float32,o5),
		    convert(Float32,d1),convert(Float32,d2),convert(Float32,d3),convert(Float32,d4),convert(Float32,d5),
		    label1,label2,label3,label4,label5,
		    unit1,unit2,unit3,unit4,unit5,
		    title)
    return extent
end

"""
    WriteTextHeader(filename,extent,format,esize,filename_d,filename_h)

Writes the extent file, corresponding to the seis format, to the file filename.

# Arguments
- `extent`: variable of type Extent to write to file
- `format`: format of the data type in file @data@
- `esize`: esize
- `filename_d`: path of the @data@ file as a String variable
- `filename_h`: path of the @headers@ file a a String variable
"""
function WriteTextHeader(filename,extent,format,esize,filename_d,filename_h)
    # write the text header
    stream = open(filename, "w")
    write(stream,join(["	n1=",extent.n1,"\n"]))
    write(stream,join(["	n2=",extent.n2,"\n"]))
    write(stream,join(["	n3=",extent.n3,"\n"]))
    write(stream,join(["	n4=",extent.n4,"\n"]))
    write(stream,join(["	n5=",extent.n5,"\n"]))
    write(stream,join(["	o1=",extent.o1,"\n"]))
    write(stream,join(["	o2=",extent.o2,"\n"]))
    write(stream,join(["	o3=",extent.o3,"\n"]))
    write(stream,join(["	o4=",extent.o4,"\n"]))
    write(stream,join(["	o5=",extent.o5,"\n"]))
    write(stream,join(["	d1=",extent.d1,"\n"]))
    write(stream,join(["	d2=",extent.d2,"\n"]))
    write(stream,join(["	d3=",extent.d3,"\n"]))
    write(stream,join(["	d4=",extent.d4,"\n"]))
    write(stream,join(["	d5=",extent.d5,"\n"]))
    write(stream,join(["	label1=\"",extent.label1,"\"\n"]))
    write(stream,join(["	label2=\"",extent.label2,"\"\n"]))
    write(stream,join(["	label3=\"",extent.label3,"\"\n"]))
    write(stream,join(["	label4=\"",extent.label4,"\"\n"]))
    write(stream,join(["	label5=\"",extent.label5,"\"\n"]))
    write(stream,join(["	unit1=\"",extent.unit1,"\"\n"]))
    write(stream,join(["	unit2=\"",extent.unit2,"\"\n"]))
    write(stream,join(["	unit3=\"",extent.unit3,"\"\n"]))
    write(stream,join(["	unit4=\"",extent.unit4,"\"\n"]))
    write(stream,join(["	unit5=\"",extent.unit5,"\"\n"]))
    write(stream,join(["	title=\"",extent.title,"\"\n"]))
    write(stream,join(["	data_format=\"",format,"\"\n"]))
    write(stream,join(["	esize=",esize,"\n"]))
    write(stream,join(["	in=\"",filename_d,"\"\n"]))
    write(stream,join(["	headers=\"",filename_h,"\"\n"]))
    close(stream)
end