text:
@deprecate get_vel_s velf(state).s
@deprecate get_vel_t velf(state).t

@deprecate propagate(veh, state, roadway, Δt) propagate(veh, state, a, roadway, Δt)

function simulate!(
    scene::Frame{E},
    roadway::R,
    models::Dict{I,M},
    nticks::Int64,
    timestep::Float64;
    rng::AbstractRNG = Random.GLOBAL_RNG,
    callbacks = nothing
) where {E<:Entity,A,R,I,M<:DriverModel}
    Base.depwarn(
"`simulate!` without specifying a pre-allocated data structure for `scenes` is now deprecated.\n
You probably want to use the `simulate` function instead.\n
Alternatively, you can provide a pre-allocated data structure via the `scenes=` keyword",
        :simulate_no_prealloc
    )
    return simulate(scene, roadway, models, nticks, timestep, rng=rng, callbacks=callbacks)
end

"""
    # TODO: this should be removed, but where to document the function then?
    run_callback(callback::Any, scenes::Vector{F}, roadway::R, models::Dict{I,M}, tick::Int) where {F,I,R,M<:DriverModel}
    run_callback(callback::Any, rec::EntityQueueRecord{S,D,I}, roadway::R, models::Dict{I,M}, tick::Int) where {S,D,I,R,M<:DriverModel}
run callback and return whether simlation should terminate
A new method should be implemented when defining a new callback object.
"""
function run_callback(callback, scenes, actions::Union{Nothing, Vector{Frame{A}}}, roadway, models, tick) where {A<:EntityAction}
    Base.depwarn(
"Using a deprecated version of `run_callback`. Since v0.7.10, user-defined callback functions should also take an `actions` argument.
 If you have implemented `run_callback` with an actions argument, make sure the method signature is more specific than this one.\n
Check the section `Simulation > Callbacks` in the package documentation for more information
 on how to define callback functions.\n
Your function call is being forwarded to `run_callback` without actions argument.",
        :run_callback_no_actions
    )
    return run_callback(callback, scenes, roadway, models, tick)
end


text:
@auto_hash_equals immutable LintMessage
    location:: Location
    code    :: Symbol #[E|W|I][1-9][1-9][1-9]
    scope   :: String
    variable:: String
    message :: String
end
location(msg::LintMessage) = msg.location

function Base.show(io::IO, m::LintMessage)
    s = string(location(m), " ", m.code, " ", m.variable, ": ")
    print(io, s)
    ident = min(60, length(s))
    lines = split(m.message, "\n")
    print(io, lines[1])
    for l in lines[2:end]
        print(io, "\n", " " ^ ident, l)
    end
end

function Base.isless(m1::LintMessage, m2::LintMessage)
    if file(m1) != file(m2)
        return isless(file(m1), file(m2))
    end
    if level(m1) != level(m2)
        # ERROR < WARN < INFO
        return iserror(m1) || isinfo(m2)
    end
    if line(m1) != line(m2)
        return line(m1) < line(m2)
    end
    if m1.code != m2.code
        return m1.code < m2.code
    end
    if m1.variable != m2.variable
        return m1.variable < m2.variable
    end
    return m1.message < m2.message
end

iserror(m::LintMessage) = string(m.code)[1] == 'E'
iswarning(m::LintMessage) = string(m.code)[1] == 'W'
isinfo(m::LintMessage) = string(m.code)[1] == 'I'

const ERRORLEVELS = Dict{Char, Symbol}('E'=>:ERROR, 'W'=>:WARN, 'I'=>:INFO)
level(m::LintMessage) = ERRORLEVELS[string(m.code)[1]]

ext: jl


text:
# PREAMBLE

# PKG_SETUP

# ## Setup

using ContactImplicitMPC
using LinearAlgebra

# ## Simulation
s_sim = get_simulation("hopper_3D", "sine2_3D_lc", "sinusoidal");
model_sim = s_sim.model
env_sim = s_sim.env

s = get_simulation("hopper_3D", "flat_3D_lc", "flat");
model = s.model
env = s.env

nq = model.nq
nu = model.nu
nc = model.nc

# ## Reference Trajectory
ref_traj = deepcopy(get_trajectory(s.model, s.env,
    joinpath(module_dir(), "src/dynamics/hopper_3D/gaits/gait_forward.jld2"),
    load_type = :joint_traj));

H = ref_traj.H
h = ref_traj.h

# ## MPC setup
N_sample = 10
H_mpc = 20
h_sim = h / N_sample
H_sim = 1200 # 12000
κ_mpc = 1.0e-4

obj = TrackingObjective(model, env, H_mpc,
    q = [Diagonal(1.0e-1 * [3,3,0.1,5e+1,5e+1,5e+1,10])   for t = 1:H_mpc],
    u = [Diagonal(1.0e-0 * [1e-1, 1e-1, 1e1]) for t = 1:H_mpc],
    γ = [Diagonal(1.0e-100 * ones(model.nc)) for t = 1:H_mpc],
    b = [Diagonal(1.0e-100 * ones(model.nc * friction_dim(env))) for t = 1:H_mpc])

p = ci_mpc_policy(ref_traj, s, obj,
    H_mpc = H_mpc,
    N_sample = N_sample,
    κ_mpc = κ_mpc,
    n_opts = NewtonOptions(
        r_tol = 3e-4,
        max_iter = 5),
    mpc_opts = CIMPCOptions(
        altitude_update = true,
        altitude_impact_threshold = 0.05,
        altitude_verbose = true,
        )
    )

p = ci_mpc_policy(ref_traj, s, obj,
    H_mpc = H_mpc,
    N_sample = N_sample,
    κ_mpc = κ_mpc,
	mode = :configuration,
    n_opts = NewtonOptions(
		r_tol = 3e-4,
		max_iter = 5,
		max_time = ref_traj.h, # HARD REAL TIME
		),
    mpc_opts = CIMPCOptions(
        altitude_update = true,
        altitude_impact_threshold = 0.05,
        altitude_verbose = true,
        ),
	ip_opts = InteriorPointOptions(
		max_iter = 100,
		verbose = false,
		r_tol = 1.0e-4,
		κ_tol = 1.0e-4,
		diff_sol = true,
		solver = :empty_solver,
		),
    );

# ## Initial conditions
q1_sim, v1_sim = initial_conditions(ref_traj);

# ## Simulator
sim = simulator(s, H_sim, h=h_sim, policy=p);

# ## Simulate
simulate!(sim, q1_sim, v1_sim);
@benchmark simulate!($sim, $q1_sim, $v1_sim)

# ## Visualizer
vis = ContactImplicitMPC.Visualizer()
ContactImplicitMPC.render(vis)

# ## Visualize
plot_surface!(vis, s_sim.env, n=200)
visualize_robot!(vis, model, sim.traj)

# ## Timing result
# Julia is [JIT-ed](https://en.wikipedia.org/wiki/Just-in-time_compilation) so re-run the MPC setup through Simulate for correct timing results.
process!(sim.stats, N_sample) # Time budget
H_sim * h_sim / sum(sim.stats.policy_time) # Speed ratio


text:
export load_vtree, zoo_vtree, zoo_vtree_file

"""
    load_vtree(file::String, ::Type{V}=PlainVtree)::V where V<:Vtree

Load a vtree file from file. Currently only supports ".vtree" format.
"""
function load_vtree(file::String, ::Type{V}=PlainVtree)::V where V<:Vtree
    return compile_vtree_format_lines(parse_vtree_file(file), V)
end

using Pkg.Artifacts

zoo_vtree_file(name) =
    artifact"circuit_model_zoo" * zoo_version * "/vtrees/$name"

function zoo_vtree(name, ::Type{V}=PlainVtree)::V where V<:Vtree
    load_vtree(zoo_vtree_file(name), V)
end

function parse_vtree_comment_line(ln::String)
    VtreeCommentLine(lstrip(chop(ln, head = 1, tail = 0)))
end

function parse_vtree_inner_line(ln::String)
    tokens = split(ln)
    node_id = parse(UInt32, tokens[2])
    left_id = parse(UInt32, tokens[3])
    right_id = parse(UInt32, tokens[4])
    VtreeInnerLine(node_id, left_id, right_id)
end

function parse_vtree_leaf_line(ln::String)
    tokens = split(ln)
    node_id = parse(UInt32, tokens[2])
    var_id  = parse(UInt32, tokens[3])
    VtreeLeafLine(node_id, var_id)
end

function parse_vtree_header_line(ln::String)
    VtreeHeaderLine()
end

function parse_vtree_file(file::String)::VtreeFormatLines
    q = Vector{VtreeFormatLine}()
    open(file) do file
        for ln in eachline(file)
            if ln[1] == 'c'
                push!(q, parse_vtree_comment_line(ln))
            elseif ln[1] == 'L'
                push!(q, parse_vtree_leaf_line(ln))
            elseif ln[1] == 'I'
                push!(q, parse_vtree_inner_line(ln))
            elseif startswith(ln, "vtree")
                push!(q, parse_vtree_header_line(ln))
            else
                error("Don't know how to parse vtree file format line $ln")
            end
        end
    end
    q
end


text:
""" :( """
function DryBubble_θ_liq_ice(::Type{FT}) where {FT}
    z_in = FT[
          25.,   75.,  125.,  175.,  225.,  275.,  325.,  375.,  425.,
         475.,  525.,  575.,  625.,  675.,  725.,  775.,  825.,  875.,
         925.,  975., 1025., 1075., 1125., 1175., 1225., 1275., 1325.,
        1375., 1425., 1475., 1525., 1575., 1625., 1675., 1725., 1775.,
        1825., 1875., 1925., 1975., 2025., 2075., 2125., 2175., 2225.,
        2275., 2325., 2375., 2425., 2475., 2525., 2575., 2625., 2675.,
        2725., 2775., 2825., 2875., 2925., 2975., 3025., 3075., 3125.,
        3175., 3225., 3275., 3325., 3375., 3425., 3475., 3525., 3575.,
        3625., 3675., 3725., 3775., 3825., 3875., 3925., 3975., 4025.,
        4075., 4125., 4175., 4225., 4275., 4325., 4375., 4425., 4475.,
        4525., 4575., 4625., 4675., 4725., 4775., 4825., 4875., 4925.,
        4975., 5025., 5075., 5125., 5175., 5225., 5275., 5325., 5375.,
        5425., 5475., 5525., 5575., 5625., 5675., 5725., 5775., 5825.,
        5875., 5925., 5975., 6025., 6075., 6125., 6175., 6225., 6275.,
        6325., 6375., 6425., 6475., 6525., 6575., 6625., 6675., 6725.,
        6775., 6825., 6875., 6925., 6975., 7025., 7075., 7125., 7175.,
        7225., 7275., 7325., 7375., 7425., 7475., 7525., 7575., 7625.,
        7675., 7725., 7775., 7825., 7875., 7925., 7975., 8025., 8075.,
        8125., 8175., 8225., 8275., 8325., 8375., 8425., 8475., 8525.,
        8575., 8625., 8675., 8725., 8775., 8825., 8875., 8925., 8975.,
        9025., 9075., 9125., 9175., 9225., 9275., 9325., 9375., 9425.,
        9475., 9525., 9575., 9625., 9675., 9725., 9775., 9825., 9875.,
        9925., 9975.
    ]
    θ_liq_ice_in = FT[
        299.9834, 299.9836, 299.9841, 299.985 , 299.9864, 299.9883,
        299.9907, 299.9936, 299.9972, 300.0012, 300.0058, 300.011 ,
        300.0166, 300.0228, 300.0293, 300.0363, 300.0436, 300.0512,
        300.0591, 300.0672, 300.0755, 300.0838, 300.0921, 300.1004,
        300.1086, 300.1167, 300.1245, 300.132 , 300.1393, 300.1461,
        300.1525, 300.1583, 300.1637, 300.1685, 300.1726, 300.1762,
        300.179 , 300.1812, 300.1826, 300.1833, 300.1833, 300.1826,
        300.1812, 300.179 , 300.1762, 300.1727, 300.1685, 300.1637,
        300.1584, 300.1525, 300.1461, 300.1393, 300.1321, 300.1245,
        300.1167, 300.1087, 300.1005, 300.0922, 300.0838, 300.0755,
        300.0673, 300.0592, 300.0513, 300.0437, 300.0364, 300.0294,
        300.0228, 300.0167, 300.0111, 300.0059, 300.0013, 299.9972,
        299.9937, 299.9908, 299.9884, 299.9865, 299.9851, 299.9842,
        299.9837, 299.9835, 299.9835, 299.9835, 299.9835, 299.9835,
        299.9835, 299.9835, 299.9835, 299.9835, 299.9835, 299.9835,
        299.9835, 299.9835, 299.9835, 299.9835, 299.9835, 299.9835,
        299.9835, 299.9835, 299.9835, 299.9835, 299.9835, 299.9835,
        299.9835, 299.9835, 299.9835, 299.9835, 299.9835, 299.9835,
        299.9835, 299.9835, 299.9835, 299.9835, 299.9835, 299.9835,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9836, 299.9836,
        299.9836, 299.9836, 299.9836, 299.9836, 299.9837, 299.9837,
        299.9837, 299.9837, 299.9837, 299.9837, 299.9837, 299.9837,
        299.9837, 299.9837, 299.9837, 299.9837, 299.9837, 299.9837,
        299.9837, 299.9837, 299.9837, 299.9837, 299.9837, 299.9837,
        299.9837, 299.9837
    ]
    profile = Dierckx.Spline1D(z_in, θ_liq_ice_in; k = 1)
    return profile
end

""" :( """
function DryBubble_updrafts_z(::Type{FT}) where {FT}
    z_in = FT[
          75.,  125.,  175.,  225.,  275.,  325.,  375.,  425.,  475.,
         525.,  575.,  625.,  675.,  725.,  775.,  825.,  875.,  925.,
         975., 1025., 1075., 1125., 1175., 1225., 1275., 1325., 1375.,
        1425., 1475., 1525., 1575., 1625., 1675., 1725., 1775., 1825.,
        1875., 1925., 1975., 2025., 2075., 2125., 2175., 2225., 2275.,
        2325., 2375., 2425., 2475., 2525., 2575., 2625., 2675., 2725.,
        2775., 2825., 2875., 2925., 2975., 3025., 3075., 3125., 3175.,
        3225., 3275., 3325., 3375., 3425., 3475., 3525., 3575., 3625.,
        3675., 3725., 3775., 3825., 3875., 3925.]
    return z_in
end

""" :( """
function DryBubble_updrafts_θ_liq_ice(::Type{FT}) where {FT}
    z_in = DryBubble_updrafts_z(FT)
    θ_liq_in = FT[
        299.9882, 299.996 , 300.0063, 300.0205, 300.04  , 300.0594,
        300.0848, 300.1131, 300.1438, 300.1766, 300.2198, 300.2567,
        300.2946, 300.3452, 300.3849, 300.4245, 300.4791, 300.5182,
        300.574 , 300.6305, 300.6668, 300.7222, 300.7771, 300.8074,
        300.8591, 300.9092, 300.9574, 300.9758, 301.0182, 301.0579,
        301.0944, 301.1276, 301.1572, 301.1515, 301.1729, 301.1902,
        301.2033, 301.2122, 301.2167, 301.2169, 301.2127, 301.2041,
        301.1913, 301.1743, 301.1533, 301.1593, 301.1299, 301.097 ,
        301.0606, 301.0212, 300.9788, 300.9607, 300.9125, 300.8625,
        300.8108, 300.7806, 300.7256, 300.6701, 300.6338, 300.5772,
        300.5212, 300.482 , 300.4272, 300.3875, 300.3354, 300.2968,
        300.2587, 300.2216, 300.1782, 300.1452, 300.1143, 300.0859,
        300.0603, 300.0408, 300.0211, 300.0067, 299.9963, 299.9884]
    profile = Dierckx.Spline1D(z_in, θ_liq_in; k = 1)
    return profile
end

""" :( """
function DryBubble_updrafts_area(::Type{FT}) where {FT}
    z_in = DryBubble_updrafts_z(FT)
    Area_in = FT[
        0.04 , 0.055, 0.07 , 0.08 , 0.085, 0.095, 0.1  , 0.105, 0.11 ,
        0.115, 0.115, 0.12 , 0.125, 0.125, 0.13 , 0.135, 0.135, 0.14 ,
        0.14 , 0.14 , 0.145, 0.145, 0.145, 0.15 , 0.15 , 0.15 , 0.15 ,
        0.155, 0.155, 0.155, 0.155, 0.155, 0.155, 0.16 , 0.16 , 0.16 ,
        0.16 , 0.16 , 0.16 , 0.16 , 0.16 , 0.16 , 0.16 , 0.16 , 0.16 ,
        0.155, 0.155, 0.155, 0.155, 0.155, 0.155, 0.15 , 0.15 , 0.15 ,
        0.15 , 0.145, 0.145, 0.145, 0.14 , 0.14 , 0.14 , 0.135, 0.135,
        0.13 , 0.13 , 0.125, 0.12 , 0.115, 0.115, 0.11 , 0.105, 0.1  ,
        0.095, 0.085, 0.08 , 0.07 , 0.055, 0.04]
    profile = Dierckx.Spline1D(z_in, Area_in; k = 1)
    return profile
end

""" :( """
function DryBubble_updrafts_w(::Type{FT}) where {FT}
    z_in = DryBubble_updrafts_z(FT)
    W_in = FT[
        0.017 , 0.0266, 0.0344, 0.0417, 0.0495, 0.0546, 0.061 , 0.0668,
        0.0721, 0.0768, 0.0849, 0.0887, 0.092 , 0.0996, 0.1019, 0.1037,
        0.1106, 0.1114, 0.1179, 0.1243, 0.1238, 0.1297, 0.1355, 0.1335,
        0.1387, 0.1437, 0.1485, 0.1448, 0.1489, 0.1527, 0.1564, 0.1597,
        0.1628, 0.1565, 0.1588, 0.1609, 0.1626, 0.1641, 0.1652, 0.166 ,
        0.1665, 0.1667, 0.1666, 0.1662, 0.1655, 0.1736, 0.1722, 0.1706,
        0.1686, 0.1664, 0.1639, 0.1698, 0.1667, 0.1634, 0.1599, 0.1641,
        0.1601, 0.1559, 0.1589, 0.1543, 0.1496, 0.1514, 0.1464, 0.1475,
        0.1422, 0.1425, 0.1424, 0.1419, 0.1361, 0.135 , 0.1335, 0.1316,
        0.1294, 0.1302, 0.1271, 0.1264, 0.1269, 0.1256]
    profile = Dierckx.Spline1D(z_in, W_in; k = 1)
    return profile
end

""" :( """
function DryBubble_updrafts_T(::Type{FT}) where {FT}
    z_in = DryBubble_updrafts_z(FT)
    T_in = FT[
        299.2557, 298.775 , 298.2969, 297.8227, 297.3536, 296.8843,
        296.421 , 295.9603, 295.502 , 295.0456, 294.5994, 294.1468,
        293.6951, 293.2556, 292.8054, 292.3549, 291.9188, 291.4677,
        291.0325, 290.5978, 290.1434, 289.7073, 289.2706, 288.81  ,
        288.3698, 287.928 , 287.4842, 287.0118, 286.5622, 286.1099,
        285.6544, 285.1957, 284.7335, 284.2379, 283.7677, 283.2937,
        282.8157, 282.3337, 281.8476, 281.3574, 280.8631, 280.3649,
        279.8626, 279.3565, 278.8467, 278.362 , 277.8447, 277.3241,
        276.8006, 276.2742, 275.7454, 275.2388, 274.705 , 274.1694,
        273.6327, 273.1155, 272.576 , 272.0363, 271.514 , 270.9736,
        270.4339, 269.9094, 269.3711, 268.8465, 268.311 , 267.7877,
        267.2649, 266.7432, 266.2159, 265.698 , 265.1821, 264.6685,
        264.1574, 263.6518, 263.1461, 262.6451, 262.1476, 261.6524]
    profile = Dierckx.Spline1D(z_in, T_in; k = 1)
    return profile
end


text:
include("Utils.jl")

struct Factored
    a
    x1
    x2
end

function toexpanded(F::Factored)
    _validitycheck

    b = -F.a * (F.x1 + F.x2)
    c = F.a * F.x1 * F.x2

    return Expanded(F.a, b, c)
end

function tovertex(F::Factored)
    return tovertex(toexpanded(F))
end

function calcvalue(F::Factored, x)
    return F.a * (x - F.x1) * (x - F.x2)
end


text:
using NDTensors,
      Test
using HDF5

@testset "Write to Disk and Read from Disk" begin

  @testset "HDF5 readwrite Dense storage" begin
    # Real case

    D = randomTensor(3,4)

    fo = h5open("data.h5","w")
    write(fo,"D",D.store)
    close(fo)

    fi = h5open("data.h5","r")
    rDstore = read(fi,"D",Dense{Float64})
    close(fi)
    @test rDstore ≈ D.store

    # Complex case

    D = randomTensor(ComplexF64,3,4)

    fo = h5open("data.h5","w")
    write(fo,"D",D.store)
    close(fo)

    fi = h5open("data.h5","r")
    rDstore = read(fi,"D",Dense{ComplexF64})
    close(fi)
    @test rDstore ≈ D.store
  end

  @testset "HDF5 readwrite BlockSparse storage" begin
    # Indices
    indsA = ([2,3],[4,5])

    # Locations of non-zero blocks
    locs = [(1,2),(2,1)]

    # Real case

    B = randomBlockSparseTensor(locs,indsA)

    fo = h5open("data.h5","w")
    write(fo,"B",B.store)
    close(fo)

    fi = h5open("data.h5","r")
    rBstore = read(fi,"B",BlockSparse{Float64})
    close(fi)
    @test rBstore ≈ B.store

    # Complex case

    B = randomBlockSparseTensor(ComplexF64,locs,indsA)

    fo = h5open("data.h5","w")
    write(fo,"B",B.store)
    close(fo)

    fi = h5open("data.h5","r")
    rBstore = read(fi,"B",BlockSparse{ComplexF64})
    close(fi)
    @test rBstore ≈ B.store
  end

  #
  # Clean up the test hdf5 file
  #
  rm("data.h5",force=true)

end

nothing


text:
unstructured_index(i::T, j::T, nx::T) where {T<:Integer} = (j - 1) * nx + i

function unstructured_grid(coords::T, corners::T) where {T<:AbstractArray{<:Real,3}}
    nx = size(coords, 1)
    ny = size(coords, 2)
    ne = nx * ny
    np = (nx + 1) * (ny + 1)

    points = zeros(np, 2)
    for i = 1:nx+1, j = 1:ny+1
        idx = unstructured_index(i, j, nx + 1)
        points[idx, :] .= corners[i, j, :]
    end

    cells = zeros(Int, ne, 4)
    for i = 1:nx, j = 1:ny
        idx = unstructured_index(i, j, nx)
        cells[idx, 1] = unstructured_index(i, j, nx + 1)
        cells[idx, 2] = unstructured_index(i + 1, j, nx + 1)
        cells[idx, 3] = unstructured_index(i + 1, j + 1, nx + 1)
        cells[idx, 4] = unstructured_index(i, j + 1, nx + 1)
    end

    return cells, points
end

function unstructured_grid(x::T, y::T, x0, x1, y0, y1) where {T<:AbstractMatrix}
    Δx = zero(x[:, 1])
    Δx[1] = 2.0 * (x[1, 1] - x0)
    for i = 2:length(Δx)-1
        Δx[i] = 2.0 * (x[i, 1] - x[i-1, 1] - Δx[i-1])
    end
    Δx[end] = 2.0 * (x1 - x[end, 1])

    Δy = zero(y[1, :])
    Δy[1] = 2.0 * (y[1, 1] - y0)
    for i = 2:length(Δy)-1
        Δy[i] = 2.0 * (y[1, i] - y[1, i-1] - Δy[i-1])
    end
    Δy[end] = 2.0 * (y1 - y[1, end])

    points = zeros(length(Δx) + 1, length(Δy) + 1, 2)
    for i in axes(points, 1), j in axes(points, 2)
        if i <= length(Δx) && j <= length(Δy)
            points[i, j, 1] = x[i, j] - Δx[i] / 2
            points[i, j, 2] = y[i, j] - Δy[j] / 2
        elseif i == length(Δx) + 1 && j <= length(Δy)
            points[i, j, 1] = x[i-1, j] + Δx[i-1] / 2
            points[i, j, 2] = y[i-1, j] - Δy[j] / 2
        elseif i <= length(Δx) && j == length(Δy) + 1
            points[i, j, 1] = x[i, j-1] - Δx[i] / 2
            points[i, j, 2] = y[i, j-1] + Δy[j-1] / 2
        elseif i == length(Δx) + 1 && j == length(Δy) + 1
            points[i, j, 1] = x[i-1, j-1] + Δx[i-1] / 2
            points[i, j, 2] = y[i-1, j-1] + Δy[j-1] / 2
        end
    end

    unstructured_grid(cat(x, y, dims = 3), points)
end

function unstructured_grid(coords::T, xrange, yrange) where {T<:AbstractArray{<:Real,3}}
    xcoords = coords[:, :, 1]
    ycoords = coords[:, :, 2]
    x0, x1 = xrange
    y0, y1 = yrange

    unstructured_grid(xcoords, ycoords, x0, x1, y0, y1)
end

"""
    triangulate(points::T) where {T<:AbstractMatrix}

Split rectangular mesh into triangles

- @arg points: structured node coordinates with shape (npoints, ndim)
"""
function triangulate(points::T) where {T<:AbstractMatrix}
    sp = pyimport("scipy.spatial")
    tri = sp.Delaunay(points)

    return tri.simplices .+ 1 # python is zero-indexed
end


text:
#!/usr/bin/julia

# Sidef concept expressed in Julia

import Base.sqrt
import Base./;

mutable struct S_Number
    value::Number;

    function S_Number(x::Number)
        this = new();
        this.value = x;
        return this;
    end
end

# S_number / S_Number
function /(x::S_Number, y::S_Number)
    S_Number(x.value / y.value);
end

# int(S_Number)
function int(x::S_Number)
    S_Number(round(Int, x.value));
end

# sqrt(S_Number)
function sqrt(x::S_Number)
    S_Number(sqrt(x.value));
end

n1 = S_Number(42);
n2 = S_Number(2);

println(n1);
println(n2);

println(n1 / n2);

method = Symbol("/");
println(eval(:($method(n1, n2))));

#########################################

d = Symbol("/");
x = S_Number(12);
y = S_Number(4);
println("$x / $y == ", eval(:($d(x, y))));

code = "25.3->int->sqrt";

if (m = match(r"\G(\d+(?:\.\d+)?)", code)) != nothing
    str = m.captures[1];
    n = S_Number(parse(Float64, str));
    pos = length(str);

    while (m = match(r"\G(->|\.)", code, pos)) != nothing
        pos += length(m.captures[1]);
        if (m = match(r"\G(\w+)", code, pos)) != nothing
            cap = m.captures[1];
            pos += length(cap);
            method = Symbol(cap);
            print(n, "->", cap);
            n = eval(:($method(n)));
            println(" == ", n);
        end
    end

end

abstract type Animal
end

struct Horse <: Animal
  name::String
end

run(::Animal) = println("Run, animal, run!")
sleep(a::Animal) = println("It's late; ", a.name, " is going to bed...")

function //(z::Horse)
    println("Run horsie, run!")
    println("Horsie's name is ", z.name, ".")
end

foo = Horse("Hashy");
//(foo);

sleep(Horse("Hashy"))


text:
module LaplacianOpt

import JuMP
import LinearAlgebra
import Memento
import MathOptInterface
import JSON
import Graphs
import TikzGraphs
import TikzPictures

const MOI = MathOptInterface
const LA = LinearAlgebra
const LOpt = LaplacianOpt

# Create our module level logger (this will get precompiled)
const _LOGGER = Memento.getlogger(@__MODULE__)

# Register the module level logger at runtime so that folks can access the logger via `getlogger(LaplacianOpt)`
# NOTE: If this line is not included then the precompiled `LaplacianOpt._LOGGER` won't be registered at runtime.
__init__() = Memento.register(_LOGGER)

"Suppresses information and warning messages output by LaplacianOpt, for fine grained control use the Memento package"
function silence()
    Memento.info(_LOGGER, "Suppressing information and warning messages for the rest of this session.  Use the Memento package for more fine-grained control of logging.")
    Memento.setlevel!(Memento.getlogger(LaplacianOpt), "error")
end

"allows the user to set the logging level without the need to add Memento"
function logger_config!(level)
    Memento.config!(Memento.getlogger("LaplacianOpt"), level)
end

include("data.jl")
include("types.jl")
include("utility.jl")
include("relaxations.jl")
include("lopt_model.jl")
include("variables.jl")
include("constraints.jl")
include("objective.jl")
include("solution.jl")
include("log.jl")

end # module


text:
module ScrollAreas

using Genie, Stipple, StippleUI, StippleUI.API
import Genie.Renderer.Html: HTMLString, normal_element, template, register_normal_element

export scrollarea

register_normal_element("q__scroll__area", context = @__MODULE__)

function scrollarea(args...; kwargs...)
  q__scroll__area(args...; kw(kwargs)...)
end

end


text:
################################################################################
#                   CORE DISPATCHVARIABLEREF METHOD EXTENSIONS
################################################################################
# Extend dispatch_variable_ref
function dispatch_variable_ref(model::InfiniteModel,
                               index::IndependentParameterIndex
                               )::IndependentParameterRef
    return IndependentParameterRef(model, index)
end

function dispatch_variable_ref(model::InfiniteModel,
                               index::FiniteParameterIndex
                               )::FiniteParameterRef
    return FiniteParameterRef(model, index)
end

# Extend _add_data_object
function _add_data_object(model::InfiniteModel,
                          object::ScalarParameterData{<:IndependentParameter}
                          )::IndependentParameterIndex
    index =  MOIUC.add_item(model.independent_params, object)
    push!(model.param_object_indices, index)
    return index
end

function _add_data_object(model::InfiniteModel,
                          object::ScalarParameterData{<:FiniteParameter}
                          )::FiniteParameterIndex
    return MOIUC.add_item(model.finite_params, object)
end

# Extend _data_dictionary (type based)
function _data_dictionary(model::InfiniteModel,
    ::Type{IndependentParameter})::MOIUC.CleverDict
    return model.independent_params
end

function _data_dictionary(model::InfiniteModel,
    ::Type{FiniteParameter})::MOIUC.CleverDict
    return model.finite_params
end

# Extend _data_dictionary (ref based)
function _data_dictionary(pref::IndependentParameterRef)::MOIUC.CleverDict
    return JuMP.owner_model(pref).independent_params
end

function _data_dictionary(pref::FiniteParameterRef)::MOIUC.CleverDict
    return JuMP.owner_model(pref).finite_params
end

# Extend _data_object
function _data_object(pref::ScalarParameterRef)::AbstractDataObject
    object = get(_data_dictionary(pref), JuMP.index(pref), nothing)
    if isnothing(object)
        error("Invalid scalar parameter reference, cannot find ",
              "corresponding parameter in the model. This is likely ",
              "caused by using the reference of a deleted parameter.")
    end
    return object
end

################################################################################
#                             CORE OBJECT METHODS
################################################################################
# Extend _core_variable_object for IndependentParameterRefs
function _core_variable_object(pref::IndependentParameterRef)::IndependentParameter
    return _data_object(pref).parameter
end

# Extend _core_variable_object for FiniteParameterRefs
function _core_variable_object(pref::FiniteParameterRef)::FiniteParameter
    return _data_object(pref).parameter
end

# Extend _parameter_number
function _parameter_number(pref::IndependentParameterRef)::Int
    return _data_object(pref).parameter_num
end

# Extend _parameter_numbers
function _parameter_numbers(pref::IndependentParameterRef)::Vector{Int}
    return [_parameter_number(pref)]
end

# Extend _object_number
function _object_number(pref::IndependentParameterRef)::Int
    return _data_object(pref).object_num
end

# Extend _object_numbers
function _object_numbers(pref::IndependentParameterRef)::Vector{Int}
    return [_object_number(pref)]
end

## Set helper methods for adapting data_objects with parametric changes
# No change needed
function _adaptive_data_update(
    pref::ScalarParameterRef,
    param::P,
    data::ScalarParameterData{P}
    )::Nothing where {P <: ScalarParameter}
    data.parameter = param
    return
end

# Reconstruction is necessary
function _adaptive_data_update(
    pref::ScalarParameterRef,
    param::P1,
    data::ScalarParameterData{P2}
    )::Nothing  where {P1, P2}
    new_data = ScalarParameterData(param, data.object_num, data.parameter_num,
                                   data.name, data.parameter_func_indices,
                                   data.infinite_var_indices,
                                   data.derivative_indices, data.measure_indices,
                                   data.constraint_indices, data.in_objective,
                                   data.generative_measures,
                                   data.has_internal_supports,
                                   data.has_generative_supports,
                                   data.has_deriv_constrs)
    _data_dictionary(pref)[JuMP.index(pref)] = new_data
    return
end

# Extend _set_core_variable_object for ScalarParameterRefs
function _set_core_variable_object(pref::ScalarParameterRef,
                                   param::ScalarParameter)::Nothing
    _adaptive_data_update(pref, param, _data_object(pref))
    return
end

################################################################################
#                            PARAMETER DEFINITION
################################################################################
# Define the default derivative evaluation method
const DefaultDerivativeMethod = FiniteDifference()

# Check that supports don't violate the domain bounds
function _check_supports_in_bounds(_error::Function,
                                   supports::Union{<:Real, Vector{<:Real}},
                                   domain::AbstractInfiniteDomain)::Nothing
    if !supports_in_domain(supports, domain)
        _error("Supports violate the domain bounds.")
    end
    return
end

"""
    build_parameter(
        _error::Function, domain::InfiniteScalarDomain;
        [num_supports::Int = 0,
        supports::Union{Real, Vector{<:Real}} = Float64[],
        sig_digits::Int = DefaultSigDigits,
        derivative_method::AbstractDerivativeMethod = DefaultDerivativeMethod]
    )::IndependentParameter

Returns a [`IndependentParameter`](@ref) given the appropriate information.
This is analagous to `JuMP.build_variable`. Errors if supports violate the
bounds associated with `domain`. This is meant to primarily serve as a
helper method for [`@infinite_parameter`](@ref). Here `derivative_method`
specifies the numerical evalution method that will be applied to derivatives that
are taken with respect to this infinite parameter.

**Example**
```julia-repl
julia> param = build_parameter(error, IntervalDomain(0, 3), supports = Vector(0:3));
```
"""
function build_parameter(
    _error::Function,
    domain::InfiniteScalarDomain;
    num_supports::Int = 0,
    supports::Union{Real, Vector{<:Real}} = Float64[],
    sig_digits::Int = DefaultSigDigits,
    derivative_method::AbstractDerivativeMethod = DefaultDerivativeMethod,
    extra_kwargs...
    )
    for (kwarg, _) in extra_kwargs
        _error("Unrecognized keyword argument $kwarg")
    end
    label = UserDefined
    length_supports = length(supports)
    if !isempty(supports)
        supports = round.(supports, sigdigits = sig_digits)
        _check_supports_in_bounds(_error, supports, domain)
        num_supports == 0 || @warn("Ignoring num_supports since supports is not empty.")
    elseif num_supports != 0
        supports, label = generate_support_values(domain, num_supports = num_supports,
                                                  sig_digits = sig_digits)
    end
    supports_dict = DataStructures.SortedDict{Float64, Set{DataType}}(
                                            i => Set([label]) for i in supports)
    if length_supports != 0 && (length(supports_dict) != length_supports)
        @warn("Support points are not unique, eliminating redundant points.")
    end
    return IndependentParameter(domain, supports_dict, sig_digits, derivative_method,
                                generative_support_info(derivative_method))
end

# Fallback for bad domain types
function build_parameter(
    _error::Function,
    domain::AbstractInfiniteDomain,
    kwargs...
    )
    _error("Expected scalar infinite domain for each independent parameter, ",
           "but got a domain of type `$(domain)`. If you are trying to use an ",
           "`InfiniteArrayDomain`, try setting `independent = false`.")
end

"""
    build_parameter(_error::Function, value::Real)::FiniteParameter

Returns a [`FiniteParameter`](@ref) given the appropriate information.
This is analagous to `JuMP.build_variable`. This is meant to primarily serve as
a helper method for [`@finite_parameter`](@ref).

**Example**
```jldoctest; setup = :(using InfiniteOpt)
julia> build_finite_parameter(error, 1)
FiniteParameter(1.0)
```
"""
function build_parameter(
    _error::Function,
    value::Real;
    extra_kwargs...
    )::FiniteParameter
    for (kwarg, _) in extra_kwargs
        _error("Unrecognized keyword argument $kwarg")
    end
    return FiniteParameter(value)
end

# Generic fallback
function build_parameter(_error::Function, arg, kwargs...)
    _error("Unexpected input given for the value of the parameter.")
end

"""
    add_parameter(model::InfiniteModel, p::IndependentParameter,
                  [name::String = ""])::GeneralVariableRef

Returns a [`GeneralVariableRef`](@ref) associated with the parameter `p` that is added
to `model`. This adds a parameter to the model in a manner similar to
`JuMP.add_variable`. This is used to add parameters with the use of
[`@infinite_parameter`](@ref).
[`build_parameter`](@ref build_parameter(::Function, ::InfiniteScalarDomain))
should be used to construct `p`.

**Example**
```julia-repl
julia> p = build_parameter(error, IntervalDomain(0, 3), supports = Vector(0:3));

julia> param_ref = add_parameter(model, p, "name")
name
```
"""
function add_parameter(
    model::InfiniteModel,
    p::IndependentParameter,
    name::String = ""
    )::GeneralVariableRef
    obj_num = length(_param_object_indices(model)) + 1
    param_num = model.last_param_num += 1
    data_object = ScalarParameterData(p, obj_num, param_num, name)
    obj_index = _add_data_object(model, data_object)
    model.name_to_param = nothing
    return GeneralVariableRef(model, obj_index.value, typeof(obj_index))
end

"""
    add_parameter(model::InfiniteModel, p::FiniteParameter,
                  [name::String = ""])::GeneralVariableRef

Returns a [`GeneralVariableRef`](@ref) associated with the parameter `p` that is
added to `model`. This adds a parameter to the model in a manner similar to
`JuMP.add_variable`. This is to add parameters with the use of
[`@finite_parameter`](@ref).
[`build_parameter`](@ref build_parameter(::Function, ::Real)) should be used to
construct `p`.

**Example**
```julia-repl
julia> p = build_parameter(error, 42);

julia> param_ref = add_parameter(model, p, "name")
name
```
"""
function add_parameter(
    model::InfiniteModel,
    p::FiniteParameter,
    name::String = ""
    )::GeneralVariableRef
    data_object = ScalarParameterData(p, -1, -1, name)
    obj_index = _add_data_object(model, data_object)
    model.name_to_param = nothing
    return GeneralVariableRef(model, obj_index.value, typeof(obj_index))
end

################################################################################
#                           PARAMETER DEPENDENCIES
################################################################################
# Extend _infinite_variable_dependencies
function _infinite_variable_dependencies(pref::ScalarParameterRef
    )::Vector{InfiniteVariableIndex}
    return _data_object(pref).infinite_var_indices
end

# Extend _parameter_function_dependencies
function _parameter_function_dependencies(pref::ScalarParameterRef
    )::Vector{ParameterFunctionIndex}
    return _data_object(pref).parameter_func_indices
end

# Extend _derivative_dependencies
function _derivative_dependencies(pref::ScalarParameterRef
    )::Vector{DerivativeIndex}
    return _data_object(pref).derivative_indices
end

# Extend _measure_dependencies
function _measure_dependencies(pref::ScalarParameterRef
    )::Vector{MeasureIndex}
    return _data_object(pref).measure_indices
end

# Extend _constraint_dependencies
function _constraint_dependencies(pref::ScalarParameterRef
    )::Vector{InfOptConstraintIndex}
    return _data_object(pref).constraint_indices
end

# Extend _generative_measures
function _generative_measures(pref::ScalarParameterRef
    )::Vector{MeasureIndex}
    return _data_object(pref).generative_measures
end

################################################################################
#                             USED_BY FUNCTIONS
################################################################################
"""
    used_by_infinite_variable(pref::IndependentParameterRef)::Bool

Return true if `pref` is used by an infinite variable or false otherwise.

**Example**
```julia-repl
julia> used_by_infinite_variable(t)
true
```
"""
function used_by_infinite_variable(pref::IndependentParameterRef)::Bool
    return !isempty(_infinite_variable_dependencies(pref))
end

# FiniteParameter
used_by_infinite_variable(pref::FiniteParameterRef)::Bool = false

"""
    used_by_parameter_function(pref::IndependentParameterRef)::Bool

Return true if `pref` is used by an infinite parameter function or false otherwise.

**Example**
```julia-repl
julia> used_by_parameter_function(t)
false
```
"""
function used_by_parameter_function(pref::IndependentParameterRef)::Bool
    return !isempty(_parameter_function_dependencies(pref))
end

# FiniteParameter
used_by_parameter_function(pref::FiniteParameterRef)::Bool = false

"""
    used_by_measure(pref::Union{IndependentParameterRef, FiniteParameterRef})::Bool

Return true if `pref` is used by a measure or false otherwise.

**Example**
```julia-repl
julia> used_by_measure(t)
false
```
"""
function used_by_measure(pref::ScalarParameterRef)::Bool
    return !isempty(_measure_dependencies(pref))
end

"""
    used_by_constraint(pref::Union{IndependentParameterRef, FiniteParameterRef})::Bool

Return true if `pref` is used by a constraint or false otherwise.

**Example**
```julia-repl
julia> used_by_constraint(t)
true
```
"""
function used_by_constraint(pref::ScalarParameterRef)::Bool
    return !isempty(_constraint_dependencies(pref))
end

"""
    used_by_objective(pref::FiniteParameterRef)::Bool

Return true if `pref` is used by the objective function.

**Example**
```julia-repl
```
"""
function used_by_objective(pref::FiniteParameterRef)::Bool
    return _data_object(pref).in_objective
end

# IndependentParameter
used_by_objective(::IndependentParameterRef)::Bool = false

"""
    used_by_derivative(pref::IndependentParameterRef)::Bool

Return true if `pref` is used by a derivative or false otherwise.

**Example**
```julia-repl
julia> used_by_derivative(t)
false
```
"""
function used_by_derivative(pref::IndependentParameterRef)::Bool
    return !isempty(_derivative_dependencies(pref))
end

# FiniteParameter
used_by_derivative(::FiniteParameterRef)::Bool = false

"""
    is_used(pref::Union{IndependentParameterRef, FiniteParameterRef})::Bool

Return true if `pref` is used in the model or false otherwise.

**Example**
```julia-repl
julia> is_used(t)
true
```
"""
function is_used(pref::ScalarParameterRef)::Bool
    return used_by_measure(pref) || used_by_constraint(pref) ||
           used_by_infinite_variable(pref) || used_by_objective(pref) ||
           used_by_derivative(pref) || used_by_parameter_function(pref)
end

################################################################################
#                              NAME METHODS
################################################################################

"""
    JuMP.name(pref::Union{IndependentParameterRef, FiniteParameterRef})::String

Extend the `JuMP.name` function to accomodate infinite parameters. Returns the
name string associated with `pref`.

**Example**
```julia-repl
julia> name(t)
"t"
```
"""
function JuMP.name(pref::ScalarParameterRef)::String
    object = get(_data_dictionary(pref), JuMP.index(pref), nothing)
    return isnothing(object) ? "" : object.name
end

"""
    JuMP.set_name(pref::ScalarParameterRef, name::String)

Extend the `JuMP.set_name` function to accomodate infinite parameters. Set a new
base name to be associated with `pref`.

**Example**
```julia-repl
julia> set_name(t, "time")

julia> name(t)
"time"
```
"""
function JuMP.set_name(pref::ScalarParameterRef, name::String)::Nothing
    _data_object(pref).name = name
    JuMP.owner_model(pref).name_to_param = nothing
    return
end

# Make a parameter reference
function _make_parameter_ref(model::InfiniteModel,
                             index::AbstractInfOptIndex)::GeneralVariableRef
    return GeneralVariableRef(model, MOIUC.key_to_index(index), typeof(index))
end

function _make_parameter_ref(model::InfiniteModel,
                             index::DependentParameterIndex
                             )::GeneralVariableRef
    return GeneralVariableRef(model, MOIUC.key_to_index(index.object_index),
                              typeof(index), index.param_index)
end

# Get the name_to_param Dictionary
function _param_name_dict(model::InfiniteModel
    )::Union{Dict{String, AbstractInfOptIndex}, Nothing}
    return model.name_to_param
end

# Update name_to_param
function _update_param_name_dict(model::InfiniteModel,
    param_dict::MOIUC.CleverDict{K, V}
    )::Nothing where {K, V <: ScalarParameterData}
    name_dict = _param_name_dict(model)
    for (index, data_object) in param_dict
        param_name = data_object.name
        if haskey(name_dict, param_name)
            # IndependentParameterIndex(-1) is a special value that means
            # this string does not map to a unique variable name.
            name_dict[param_name] = IndependentParameterIndex(-1)
        else
            name_dict[param_name] = index
        end
    end
    model.name_to_param = name_dict
    return
end

function _update_param_name_dict(model::InfiniteModel,
    param_dict::MOIUC.CleverDict{K, V}
    )::Nothing where {K, V <: MultiParameterData}
    name_dict = _param_name_dict(model)
    for (index, data_object) in param_dict
        param_nums = data_object.parameter_nums
        for i in eachindex(param_nums)
            name = data_object.names[i]
            if haskey(name_dict, name)
                # IndependentParameterIndex(-1) is a special value that means
                # this string does not map to a unique variable name.
                name_dict[name] = IndependentParameterIndex(-1)
            else
                individual_index = DependentParameterIndex(index, i)
                name_dict[name] = individual_index
            end
         end
    end
    model.name_to_param = name_dict
    return
end

"""
    parameter_by_name(model::InfiniteModel,
                      name::String)::Union{GeneralVariableRef, Nothing}

Return the parameter reference assoociated with a parameter name. Errors if
multiple parameters have the same name. Returns nothing if no such name exists.

**Example**
```julia-repl
julia> parameter_by_name(model, "t")
t
```
"""
function parameter_by_name(model::InfiniteModel,
                           name::String)::Union{GeneralVariableRef, Nothing}
    if isnothing(_param_name_dict(model))
        model.name_to_param = Dict{String, AbstractInfOptIndex}()
        _update_param_name_dict(model, model.independent_params)
        _update_param_name_dict(model, model.dependent_params)
        _update_param_name_dict(model, model.finite_params)
    end
    index = get(_param_name_dict(model), name, nothing)
    if isnothing(index)
        return nothing
    elseif index == IndependentParameterIndex(-1)
        error("Multiple parameters have the name $name.")
    else
        return _make_parameter_ref(model, index)
    end
end

################################################################################
#                       GENERATIVE SUPPORT FUNCTIONS
################################################################################
# Extend copy for NoGenerativeSupports
function Base.copy(d::NoGenerativeSupports)::NoGenerativeSupports
    return NoGenerativeSupports()
end

# Extend copy for UniformGenerativeInfo
function Base.copy(d::UniformGenerativeInfo)::UniformGenerativeInfo
    return UniformGenerativeInfo(copy(d.support_basis), d.label)
end

"""
    support_label(info::AbstractGenerativeInfo)::DataType

Return the support label to be associated with generative supports produced in
accordance with `info`. This is intended an internal method that should be
extended for user defined types of [`AbstractGenerativeInfo`](@ref).
"""
function support_label(info::AbstractGenerativeInfo)
    error("`support_label` not defined for generative support info type " *
          "$(typeof(info)).")
end

# UniformGenerativeInfo
function support_label(info::UniformGenerativeInfo)::DataType
    return info.label
end

# NoGenerativeSupports
function support_label(info::NoGenerativeSupports)::DataType
    return _NoLabel
end

"""
    generative_support_info(pref::IndependentParameterRef)::AbstractGenerativeInfo

Return the generative support information associated with `pref`.
"""
function generative_support_info(pref::IndependentParameterRef)::AbstractGenerativeInfo
    return _core_variable_object(pref).generative_supp_info
end

"""
    has_generative_supports(pref::IndependentParameterRef)::Bool

Return whether generative supports have been added to `pref` in accordance
with its generative support info.
"""
function has_generative_supports(pref::IndependentParameterRef)::Bool
    return _data_object(pref).has_generative_supports
end

# Specify if a parameter has generative supports
function _set_has_generative_supports(pref::IndependentParameterRef,
                                      status::Bool)::Nothing
    _data_object(pref).has_generative_supports = status
    return
end

# Reset (remove) the generative supports if needed
function _reset_generative_supports(pref::IndependentParameterRef)::Nothing
    if has_generative_supports(pref)
        label = support_label(generative_support_info(pref))
        delete_supports(pref, label = label) # this also calls _set_has_generative_supports
    end
    return
end

# Specify the generative_support_info
function _set_generative_support_info(pref::IndependentParameterRef,
    info::AbstractGenerativeInfo)::Nothing
    sig_digits = significant_digits(pref)
    method = derivative_method(pref)
    domain = _parameter_domain(pref)
    supps = _parameter_supports(pref)
    new_param = IndependentParameter(domain, supps, sig_digits, method, info)
    _reset_generative_supports(pref)
    _set_core_variable_object(pref, new_param)
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

"""
    make_generative_supports(info::AbstractGenerativeInfo,
                             pref::IndependentParameterRef,
                             existing_supps::Vector{Float64}
                             )::Vector{Float64}

Generate the generative supports for `pref` in accordance with `info` and the
`existing_supps` that `pref` has. The returned supports should not include
`existing_supps`. This is intended as internal method to enable
[`add_generative_supports`](@ref) and should be extended for any user defined
`info` types that are created to enable new measure and/or derivative evaluation
techniques that require the creation of generative supports.
"""
function make_generative_supports(info::AbstractGenerativeInfo, pref, supps)
    error("`make_generative_supports` is not defined for generative support " *
          "info of type $(typeof(info)).")
end

# UniformGenerativeInfo
function make_generative_supports(info::UniformGenerativeInfo,
    pref, supps)::Vector{Float64}
    # collect the preliminaries
    basis = info.support_basis
    num_internal = length(basis)
    num_existing = length(supps)
    num_existing <= 1 && error("$(pref) does not have enough supports for " *
                                "creating generative supports.")
    internal_nodes = Vector{Float64}(undef, num_internal * (num_existing - 1))
    # generate the internal node supports
    for i in Iterators.take(eachindex(supps), num_existing - 1)
        lb = supps[i]
        ub = supps[i+1]
        internal_nodes[(i-1)*num_internal+1:i*num_internal] = basis * (ub - lb) .+ lb
    end
    return internal_nodes
end

## Define internal dispatch methods for adding generative supports
# AbstractGenerativeInfo
function _add_generative_supports(pref, info::AbstractGenerativeInfo)::Nothing
    if !has_generative_supports(pref)
        existing_supps = supports(pref, label = All)
        supps = make_generative_supports(info, pref, existing_supps)
        add_supports(pref, supps, label = support_label(info))
        _set_has_generative_supports(pref, true)
    end
    return
end

# NoGenerativeSupports
function _add_generative_supports(pref, info::NoGenerativeSupports)::Nothing
    return
end

"""
    add_generative_supports(pref::IndependentParameterRef)::Nothing

Create generative supports for `pref` if needed in accordance with its
generative support info using [`make_generative_supports`](@ref) and add them to
`pref`. This is intended as an internal function, but can be useful user defined
optimizer model extensions that utlize our support system.
"""
function add_generative_supports(pref::IndependentParameterRef)::Nothing
    info = generative_support_info(pref)
    _add_generative_supports(pref, info)
    return
end

################################################################################
#                        DERIVATIVE METHOD FUNCTIONS
################################################################################
# Determine if any derivatives have derivative constraints
function has_derivative_constraints(pref::IndependentParameterRef)::Bool
    return _data_object(pref).has_deriv_constrs
end

# Make update function for whether it has derivative supports
function _set_has_derivative_constraints(pref::IndependentParameterRef,
                                         status::Bool)::Nothing
    _data_object(pref).has_deriv_constrs = status
    return
end

"""
    derivative_method(pref::IndependentParameterRef)::AbstractDerivativeMethod

Returns the numerical derivative evaluation method employed with `pref` when it
is used as an operator parameter in a derivative.

**Example**
```julia-repl
julia> derivative_method(pref)
FiniteDifference(Backward, true)
```
"""
function derivative_method(pref::IndependentParameterRef)::AbstractDerivativeMethod
    return _core_variable_object(pref).derivative_method
end

# Make method to reset derivative constraints (supports are handled separately)
function _reset_derivative_constraints(pref::Union{IndependentParameterRef,
                                                   DependentParameterRef})::Nothing
    if has_derivative_constraints(pref)
        @warn("Support/method changes will invalidate existing derivative evaluation " *
              "constraints that have been added to the InfiniteModel. Thus, " *
              "these are being deleted.")
        for idx in _derivative_dependencies(pref)
            delete_derivative_constraints(DerivativeRef(JuMP.owner_model(pref), idx))
        end
        _set_has_derivative_constraints(pref, false)
    end
    return
end

"""
    set_derivative_method(pref::IndependentParameterRef,
                          method::AbstractDerivativeMethod)::Nothing

Specfies the desired derivative evaluation method `method` for derivatives that are
taken with respect to `pref`. Any internal supports exclusively associated with
the previous method will be deleted. Also, if any derivatives were evaluated
manually, the associated derivative evaluation constraints will be deleted. Errors
if new derivative method generates supports that are incompatible with existing
measures.

**Example**
```julia-repl
julia> set_derivative_method(d, OrthogonalCollocation(2))

```
"""
function set_derivative_method(pref::IndependentParameterRef,
    method::NonGenerativeDerivativeMethod
    )::Nothing
    old_param = _core_variable_object(pref)
    domain = _parameter_domain(pref)
    supps = _parameter_supports(pref)
    sig_figs = significant_digits(pref)
    if isempty(_generative_measures(pref))
        _reset_generative_supports(pref)
        new_param = IndependentParameter(domain, supps, sig_figs, method,
                                         NoGenerativeSupports())
    else
        info = generative_support_info(pref)
        new_param = IndependentParameter(domain, supps, sig_figs, method, info)
    end
    _reset_derivative_constraints(pref)
    _set_core_variable_object(pref, new_param)
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

# GenerativeDerivativeMethod
function set_derivative_method(pref::IndependentParameterRef,
    method::GenerativeDerivativeMethod
    )::Nothing
    new_info = generative_support_info(method)
    old_info = generative_support_info(pref)
    if !isempty(_generative_measures(pref)) && new_info != old_info
        error("Generative derivative method conflicts with existing generative " *
              "measures.")
    end
    old_param = _core_variable_object(pref)
    domain = _parameter_domain(pref)
    supps = _parameter_supports(pref)
    sig_figs = significant_digits(pref)
    new_param = IndependentParameter(domain, supps, sig_figs, method, new_info)
    _reset_derivative_constraints(pref)
    _reset_generative_supports(pref)
    _set_core_variable_object(pref, new_param)
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

################################################################################
#                               SET FUNCTIONS
################################################################################
# Internal functions
function _parameter_domain(pref::IndependentParameterRef)::InfiniteScalarDomain
    return _core_variable_object(pref).domain
end
function _update_parameter_domain(pref::IndependentParameterRef,
                               domain::AbstractInfiniteDomain)::Nothing
    # old supports will always be discarded
    sig_digits = significant_digits(pref)
    method = derivative_method(pref)
    info = generative_support_info(pref)
    new_param = IndependentParameter(domain, DataStructures.SortedDict{Float64, Set{DataType}}(),
                                     sig_digits, method, info)
    _set_core_variable_object(pref, new_param)
    _reset_derivative_constraints(pref)
    _set_has_generative_supports(pref, false)
    _set_has_internal_supports(pref, false)
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

"""
    infinite_domain(pref::IndependentParameterRef)::InfiniteScalarDomain

Return the infinite domain associated with `pref`.

**Example**
```julia-repl
julia> infinite_domain(t)
[0, 1]
```
"""
function infinite_domain(pref::IndependentParameterRef)::InfiniteScalarDomain
    return _parameter_domain(pref)
end

"""
    set_infinite_domain(pref::IndependentParameterRef,
                     domain::InfiniteScalarDomain)::Nothing

Reset the infinite domain of `pref` with another `InfiniteScalarDomain`. An error will
be thrown if `pref` is being used by some measure.

**Example**
```julia-repl
julia> set_infinite_domain(t, IntervalDomain(0, 2))

julia> infinite_domain(t)
[0, 2]
```
"""
function set_infinite_domain(pref::IndependentParameterRef,
                          domain::InfiniteScalarDomain)::Nothing
    if used_by_measure(pref)
        error("$pref is used by a measure so changing its " *
              "infinite domain is not allowed.")
    end
    _update_parameter_domain(pref, domain)
    return
end

################################################################################
#                        LOWER/UPPER BOUND FUNCTIONS
################################################################################
"""
    JuMP.has_lower_bound(pref::IndependentParameterRef)::Bool

Extend the `JuMP.has_lower_bound` function to accomodate infinite parameters.
Return true if the domain associated with `pref` has a defined lower bound or if a
lower bound can be found. Extensions with user-defined infinite domain types
should extend `JuMP.has_lower_bound(domain::NewType)`.

**Example**
```julia-repl
julia> has_lower_bound(t)
true
```
"""
function JuMP.has_lower_bound(pref::IndependentParameterRef)::Bool
    domain = _parameter_domain(pref)
    return JuMP.has_lower_bound(domain)
end

"""
    JuMP.lower_bound(pref::IndependentParameterRef)::Real

Extend the `JuMP.lower_bound` function to accomodate infinite parameters.
Returns the lower bound associated with the infinite domain. Errors if such a bound
is not well-defined.

**Example**
```julia-repl
julia> lower_bound(t)
0.0
```
"""
function JuMP.lower_bound(pref::IndependentParameterRef)::Real
    domain = _parameter_domain(pref)
    if !JuMP.has_lower_bound(pref)
        error("Parameter $(pref) does not have a lower bound.")
    end
    return JuMP.lower_bound(domain)
end

"""
    JuMP.set_lower_bound(pref::IndependentParameterRef, lower::Real)::Nothing

Extend the `JuMP.set_lower_bound` function to accomodate infinite parameters.
Updates the infinite domain lower bound if such an operation is supported. Set
extensions that seek to employ this should extend
`JuMP.set_lower_bound(domain::NewType, lower::Number)`.

**Example**
```julia-repl
julia> set_lower_bound(t, -1)

julia> lower_bound(t)
-1.0
```
"""
function JuMP.set_lower_bound(pref::IndependentParameterRef, lower::Real)::Nothing
    domain = _parameter_domain(pref)
    new_domain = JuMP.set_lower_bound(domain, lower)
    _update_parameter_domain(pref, new_domain)
    return
end

"""
    JuMP.has_upper_bound(pref::IndependentParameterRef)::Bool

Extend the `JuMP.has_upper_bound` function to accomodate infinite parameters.
Return true if the domain associated with `pref` has a defined upper bound or if a
upper bound can be found. Extensions with user-defined domains should extend
`JuMP.has_upper_bound(domain::NewType)`.

**Example**
```julia-repl
julia> has_upper_bound(t)
true
```
"""
function JuMP.has_upper_bound(pref::IndependentParameterRef)::Bool
    domain = _parameter_domain(pref)
    return JuMP.has_upper_bound(domain)
end

"""
    JuMP.upper_bound(pref::IndependentParameterRef)::Real

Extend the `JuMP.upper_bound` function to accomodate infinite parameters.
Returns the upper bound associated with the infinite domain. Errors if such a bound
is not well-defined. Extensions with user-defined domain types should extend
`JuMP.has_upper_bound(domain::NewType)` and `JuMP.upper_bound(domain::NewType)` if
appropriate.

**Example**
```julia-repl
julia> upper_bound(t)
1.0
```
"""
function JuMP.upper_bound(pref::IndependentParameterRef)::Real
    domain = _parameter_domain(pref)
    if !JuMP.has_upper_bound(pref)
        error("Parameter $(pref) does not have a upper bound.")
    end
    return JuMP.upper_bound(domain)
end

"""
    JuMP.set_upper_bound(pref::IndependentParameterRef, lower::Real)::Nothing

Extend the `JuMP.set_upper_bound` function to accomodate infinite parameters.
Updates the infinite domain upper bound if and only if it is an IntervalDomain. Errors
otherwise. Extensions with user-defined infinite domains should extend
`JuMP.set_upper_bound(domain::NewType, upper::Number)` if appropriate.

**Example**
```julia-repl
julia> set_upper_bound(t, 2)

julia> upper_bound(t)
2.0
```
"""
function JuMP.set_upper_bound(pref::IndependentParameterRef, upper::Real)::Nothing
    domain = _parameter_domain(pref)
    new_domain = JuMP.set_upper_bound(domain, upper)
    _update_parameter_domain(pref, new_domain)
    return
end

################################################################################
#                               SUPPORT FUNCTIONS
################################################################################
# Internal functions
function _parameter_supports(pref::IndependentParameterRef)
    return _core_variable_object(pref).supports
end
function _parameter_support_values(pref::IndependentParameterRef)::Vector{Float64}
    return collect(keys(_parameter_supports(pref)))
end
function _update_parameter_supports(pref::IndependentParameterRef,
    supports::DataStructures.SortedDict{Float64, Set{DataType}})::Nothing
    domain = _parameter_domain(pref)
    method = derivative_method(pref)
    sig_figs = significant_digits(pref)
    info = generative_support_info(pref)
    new_param = IndependentParameter(domain, supports, sig_figs, method, info)
    _set_core_variable_object(pref, new_param)
    _reset_derivative_constraints(pref)
    _set_has_generative_supports(pref, false)
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

"""
    has_internal_supports(pref::Union{IndependentParameterRef, DependentParameterRef})::Bool

Indicate if `pref` has internal supports that will be hidden from the user by
default.
"""
function has_internal_supports(
    pref::Union{IndependentParameterRef, DependentParameterRef}
    )::Bool
    return _data_object(pref).has_internal_supports
end

# update has internal supports
function _set_has_internal_supports(
    pref::Union{IndependentParameterRef, DependentParameterRef},
    status::Bool
    )::Nothing
    _data_object(pref).has_internal_supports = status
    return
end

"""
    significant_digits(pref::IndependentParameterRef)::Int

Return the number of significant digits enforced on the supports of `pref`.

**Example**
```julia-repl
julia> significant_digits(t)
12
```
"""
function significant_digits(pref::IndependentParameterRef)::Int
    return _core_variable_object(pref).sig_digits
end

"""
    num_supports(pref::IndependentParameterRef;
                 [label::Type{<:AbstractSupportLabel} = PublicLabel])::Int

Return the number of support points associated with `pref`. By default, only the
number of public supports are counted. The full amount can be determined by setting
`label = All`. Moreover, the amount of labels that satisfy `label` is obtained
using an [`AbstractSupportLabel`](@ref).

**Example**
```julia-repl
julia> num_supports(t)
2
```
"""
function num_supports(pref::IndependentParameterRef;
                      label::Type{<:AbstractSupportLabel} = PublicLabel)::Int
    supports_dict = _parameter_supports(pref)
    if label == All || (!has_internal_supports(pref) && label == PublicLabel)
        return length(supports_dict)
    else
        return count(p -> any(v -> v <: label, p[2]), supports_dict)
    end
end

"""
    has_supports(pref::IndependentParameterRef)::Bool

Return true if `pref` has supports or false otherwise.

**Example**
```julia-repl
julia> has_supports(t)
true
```
"""
has_supports(pref::IndependentParameterRef)::Bool = !isempty(_parameter_supports(pref))

"""
    supports(pref::IndependentParameterRef;
             [label::Type{<:AbstractSupportLabel} = PublicLabel])::Vector{Float64}

Return the support points associated with `pref`. Errors if there are no
supports. Users can query just support points generated by a certain method
using the keyword argument `label`. By default, the function returns all public
support points regardless of the associated label. The full collection is given by setting
`label = All`. Moreover, the amount of labels that satisfy `label` is obtained
using an [`AbstractSupportLabel`](@ref).

**Example**
```julia-repl
julia> supports(t)
2-element Array{Float64,1}:
 0.0
 1.0
```
"""
function supports(pref::IndependentParameterRef;
                  label::Type{<:AbstractSupportLabel} = PublicLabel)::Vector{Float64}
    if label == All || (!has_internal_supports(pref) && label == PublicLabel)
        return _parameter_support_values(pref)
    else
        return findall(x -> any(v -> v <: label, x), _parameter_supports(pref))
    end
end

# Return a matrix os supports when given a vector of IndependentParameterRefs (for measures)
function supports(prefs::Vector{IndependentParameterRef};
                  label::Type{<:AbstractSupportLabel} = PublicLabel,
                  use_combinatorics::Bool = true)::Matrix{Float64}
    # generate the support matrix considering all the unique combinations
    if use_combinatorics
        supp_list = Tuple(supports(p, label = label) for p in prefs)
        inds = CartesianIndices(ntuple(i -> 1:length(supp_list[i]), length(prefs)))
        supps = Matrix{Float64}(undef, length(prefs), length(inds))
        for (k, idx) in enumerate(inds)
            supps[:, k] = [supp_list[i][j] for (i, j) in enumerate(idx.I)]
        end
        return supps
    # generate the support matrix while negating the unique combinations
    else
        num_supps = num_supports(first(prefs), label = label)
        trans_supps = Matrix{Float64}(undef, num_supps, length(prefs))
        for i in eachindex(prefs)
            supp = supports(prefs[i], label = label)
            if length(supp) != num_supps
                error("Cannot simultaneously query the supports of multiple " *
                      "independent parameters if the support dimensions do not match " *
                      "while ignoring the combinatorics. Try setting `use_combinatorics = true`.")
            else
                @inbounds trans_supps[:, i] = supp
            end
        end
        return permutedims(trans_supps)
    end
end

"""
    set_supports(pref::IndependentParameterRef, supports::Vector{<:Real};
                 [force::Bool = false,
                 label::Type{<:AbstractSupportLabel} = UserDefined]
                 )::Nothing

Specify the support points for `pref`. Errors if the supports violate the bounds
associated with the infinite domain. Warns if the points are not unique. If `force`
this will overwrite exisiting supports otherwise it will error if there are
existing supports.

**Example**
```julia-repl
julia> set_supports(t, [0, 1])

julia> supports(t)
2-element Array{Int64,1}:
 0
 1
```
"""
function set_supports(pref::IndependentParameterRef, supports::Vector{<:Real};
                      force::Bool = false,
                      label::Type{<:AbstractSupportLabel} = UserDefined
                      )::Nothing
    if has_supports(pref) && !force
        error("Unable set supports for $pref since it already has supports." *
              " Consider using `add_supports` or use `force = true` to " *
              "overwrite the existing supports.")
    end
    domain = _parameter_domain(pref)
    supports = round.(supports, sigdigits = significant_digits(pref))
    _check_supports_in_bounds(error, supports, domain)
    supports_dict = DataStructures.SortedDict{Float64, Set{DataType}}(
                                            i => Set([label]) for i in supports)
    if length(supports_dict) != length(supports)
        @warn("Support points are not unique, eliminating redundant points.")
    end
    _update_parameter_supports(pref, supports_dict)
    _set_has_internal_supports(pref, label <: InternalLabel)
    return
end

"""
    add_supports(pref::IndependentParameterRef,
                 supports::Union{Real, Vector{<:Real}};
                 [label::Type{<:AbstractSupportLabel} = UserDefined])::Nothing

Add additional support points for `pref` with identifying label `label`.

**Example**
```julia-repl
julia> add_supports(t, 0.5)

julia> supports(t)
3-element Array{Float64,1}:
 0.0
 0.5
 1.0

julia> add_supports(t, [0.25, 1])

julia> supports(t)
4-element Array{Float64,1}:
 0.0
 0.25
 0.5
 1.0
```
"""
function add_supports(pref::IndependentParameterRef,
                      supports::Union{Real, Vector{<:Real}};
                      label::Type{<:AbstractSupportLabel} = UserDefined,
                      check::Bool = true)::Nothing
    domain = infinite_domain(pref)
    supports = round.(supports, sigdigits = significant_digits(pref))
    check && _check_supports_in_bounds(error, supports, domain)
    supports_dict = _parameter_supports(pref)
    added_new_support = false
    for s in supports
        if haskey(supports_dict, s)
            push!(supports_dict[s], label)
        else
            supports_dict[s] = Set([label])
            added_new_support = true
        end
    end
    if label <: InternalLabel
        _set_has_internal_supports(pref, true)
    end
    if added_new_support
        _reset_derivative_constraints(pref)
        _reset_generative_supports(pref)
        if is_used(pref)
            set_optimizer_model_ready(JuMP.owner_model(pref), false)
        end
    end
    return
end

"""
    delete_supports(pref::IndependentParameterRef;
                    [label::Type{<:AbstractSupportLabel} = All])::Nothing

Delete the support points for `pref`. If `label != All` then delete `label` and
any supports that solely depend on it.

**Example**
```julia-repl
julia> delete_supports(t)

julia> supports(t)
ERROR: Parameter t does not have supports.
```
"""
function delete_supports(pref::IndependentParameterRef;
                         label::Type{<:AbstractSupportLabel} = All)::Nothing
    supp_dict = _parameter_supports(pref)
    if has_derivative_constraints(pref)
        @warn("Deleting supports invalidated derivative evaluations. Thus, these " *
              "are being deleted as well.")
        for idx in _derivative_dependencies(pref)
            delete_derivative_constraints(DerivativeRef(JuMP.owner_model(pref), idx))
        end
        _set_has_derivative_constraints(pref, false)
    end
    if label == All
        if used_by_measure(pref)
            error("Cannot delete the supports of $pref since it is used by " *
                  "a measure.")
        end
        empty!(supp_dict)
        _set_has_generative_supports(pref, false)
        _set_has_internal_supports(pref, false)
    else
        if has_generative_supports(pref) && support_label(generative_support_info(pref)) != label
            label = Union{label, support_label(generative_support_info(pref))}
        end
        _set_has_generative_supports(pref, false)
        filter!(p -> !all(v -> v <: label, p[2]), supp_dict)
        for (k, v) in supp_dict
            filter!(l -> !(l <: label), v)
        end
        if has_internal_supports(pref) && num_supports(pref, label = InternalLabel) == 0
            _set_has_internal_supports(pref, false)
        end
    end
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

# Make dispatch for an array of parameters
function delete_supports(prefs::AbstractArray{<:IndependentParameterRef};
                         label::Type{<:AbstractSupportLabel} = All)::Nothing
    delete_supports.(prefs, label = label)
    return
end

"""
    parameter_value(pref::FiniteParameterRef)::Float64

Return the value of a finite parameter reference `pref`. Errors if it is
an infinite parameter.

**Example**
```julia-repl
julia> value(cost)
42.0
```
"""
function parameter_value(pref::FiniteParameterRef)::Real
    return _core_variable_object(pref).value
end

"""
    JuMP.set_value(pref::FiniteParameterRef, value::Real)::Nothing

Set the value of `pref` so long as it is a finite parameter. Errors if it is
an infinite parameter.

**Example**
```julia-repl
julia> set_value(cost, 27)

julia> value(cost)
27.0
```
"""
function JuMP.set_value(pref::FiniteParameterRef, value::Real)::Nothing
    _data_object(pref).parameter = FiniteParameter(value)
    if is_used(pref)
        set_optimizer_model_ready(JuMP.owner_model(pref), false)
    end
    return
end

"""
    fill_in_supports!(pref::IndependentParameterRef;
                      [num_supports::Int = DefaultNumSupports])::Nothing

Automatically generate support points for a particular independent parameter `pref`.
Generating `num_supports` for the parameter. The supports are generated uniformly
if the underlying infinite domain is an `IntervalDomain` or they are generating randomly
accordingly to the distribution if the domain is a `UniDistributionDomain`.
Will add nothing if there are supports
and `modify = false`. Extensions that use user defined domain types should extend
[`generate_and_add_supports!`](@ref) and/or [`generate_support_values`](@ref)
as needed. Errors if the infinite domain type is not recognized.

**Example**
```julia-repl
julia> fill_in_supports!(x, num_supports = 4)

julia> supports(x)
4-element Array{Number,1}:
 0.0
 0.333
 0.667
 1.0

```
"""
function fill_in_supports!(pref::IndependentParameterRef;
                           num_supports::Int = DefaultNumSupports,
                           modify::Bool = true)::Nothing
    domain = infinite_domain(pref)
    current_amount = length(_parameter_supports(pref))
    if (modify || current_amount == 0) && current_amount < num_supports
        generate_and_add_supports!(pref, domain,
                                   num_supports = num_supports - current_amount,
                                   adding_extra = (current_amount > 0))
    end
    return
end

"""
    generate_and_add_supports!(pref::IndependentParameterRef,
                               domain::AbstractInfiniteDomain,
                               [method::Type{<:AbstractSupportLabel}];
                               [num_supports::Int = DefaultNumSupports])::Nothing

Generate supports for independent parameter `pref` via [`generate_support_values`](@ref)
and add them to `pref`. This is intended as an extendable internal method for
[`fill_in_supports!`](@ref fill_in_supports!(::IndependentParameterRef)).
Most extensions that empoy user-defined infinite domains can typically enable this
by extending [`generate_support_values`](@ref). Errors if the infinite domain type
is not recognized.
"""
function generate_and_add_supports!(pref::IndependentParameterRef,
                                    domain::AbstractInfiniteDomain;
                                    num_supports::Int = DefaultNumSupports,
                                    adding_extra::Bool = false)::Nothing
    sig_digits = significant_digits(pref)
    if isa(domain, IntervalDomain) && adding_extra
        supports, label = generate_support_values(domain, MCSample,
                                                  num_supports = num_supports,
                                                  sig_digits = sig_digits)
    else
        supports, label = generate_supports(domain,
                                            num_supports = num_supports,
                                            sig_digits = sig_digits)
    end
    add_supports(pref, supports, label = label)
    return
end

# Dispatch with method
function generate_and_add_supports!(pref::IndependentParameterRef,
                                    domain::AbstractInfiniteDomain,
                                    method::Type{<:AbstractSupportLabel};
                                    num_supports::Int = DefaultNumSupports,
                                    adding_extra::Bool = false)::Nothing
    sig_digits = significant_digits(pref)
    supports, label = generate_supports(domain, method,
                                        num_supports = num_supports,
                                        sig_digits = sig_digits)
    add_supports(pref, supports, label = label)
    return
end

################################################################################
#                               DELETE FUNCTIONS
################################################################################
# Check if parameter is used by measure data and error if it is to prevent bad
# deleting behavior
function _check_param_in_data(pref::GeneralVariableRef,
                              data::AbstractMeasureData)::Nothing
    prefs = parameter_refs(data)
    if isequal(pref, prefs) || any(isequal(pref), prefs)
        error("Unable to delete `$pref` since it is used to evaluate measures.")
    end
    return
end

# Update the dependent measures
function _update_measures(model::InfiniteModel,
                          pref::GeneralVariableRef)::Nothing
    for mindex in _measure_dependencies(pref)
        mref = dispatch_variable_ref(model, mindex)
        func = measure_function(mref)
        if func isa GeneralVariableRef
            data = measure_data(mref)
            new_func = zero(JuMP.GenericAffExpr{Float64, GeneralVariableRef})
            new_meas = Measure(new_func, data, Int[], Int[], true)
            _set_core_variable_object(mref, new_meas)
        else
            _remove_variable(func, pref)
        end
    end
    return
end

# Update the dependent constraints
function _update_constraints(model::InfiniteModel,
                             pref::GeneralVariableRef)::Nothing
    for cindex in copy(_constraint_dependencies(pref))
        cref = _make_constraint_ref(model, cindex)
        func = JuMP.jump_function(JuMP.constraint_object(cref))
        if func isa GeneralVariableRef
            set = JuMP.moi_set(JuMP.constraint_object(cref))
            new_func = zero(JuMP.GenericAffExpr{Float64, GeneralVariableRef})
            new_constr = JuMP.ScalarConstraint(new_func, set)
            _set_core_constraint_object(cref, new_constr)
            empty!(_object_numbers(cref))
        elseif func isa AbstractArray && any(isequal(pref), func)
            JuMP.delete(model, cref)
        else
            _remove_variable(func, pref)
        end
    end
    return
end

# Remove given object/parameter number and update the list
function _update_number_list(nums::Vector{Int}, list::Vector{Int})::Nothing
    filter!(e -> !(e in nums), list)
    max_num = maximum(nums)
    for i in eachindex(list)
        if list[i] > max_num
            list[i] -= length(nums)
        end
    end
    return
end

# Update the model with the removed parameter/object numbers
function _update_model_numbers(model::InfiniteModel, obj_num::Int,
                               param_nums::Vector{Int})::Nothing
    # update the independent parameters
    for (_, object) in _data_dictionary(model, IndependentParameter)
        if object.object_num > obj_num
            object.object_num -= 1
            object.parameter_num -= length(param_nums)
        end
    end
    # update the dependent parameters
    for (_, object) in _data_dictionary(model, DependentParameters)
        if object.object_num > obj_num
            object.object_num -= 1
            object.parameter_nums = object.parameter_nums .- length(param_nums)
        end
    end
    # update the infinite parameter functions
    for (_, object) in model.param_functions
        _update_number_list([obj_num], object.func.object_nums)
        _update_number_list(param_nums, object.func.parameter_nums)
    end
    # update the infinite variables
    for vref in JuMP.all_variables(model, InfiniteVariable)
        _update_number_list([obj_num], _object_numbers(vref))
        _update_number_list(param_nums, _parameter_numbers(vref))
    end
    # update the semi-infinite variables
    for vref in JuMP.all_variables(model, SemiInfiniteVariable)
        _update_number_list([obj_num], _object_numbers(vref))
        _update_number_list(param_nums, _parameter_numbers(vref))
    end
    # update the measures
    for mref in all_measures(model)
        _update_number_list([obj_num], _object_numbers(mref))
        _update_number_list(param_nums, _parameter_numbers(mref))
    end
    # update the constraints
    for (_, object) in model.constraints
        _update_number_list([obj_num], object.object_nums)
    end
    # update the central info
    deleteat!(_param_object_indices(model), obj_num)
    model.last_param_num -= length(param_nums)
    return
end

"""
    JuMP.delete(model::InfiniteModel, pref::ScalarParameterRef)::Nothing

Extend `JuMP.delete` to delete
scalar parameters and their dependencies. All variables, constraints, and
measure functions that depend on `pref` are updated to exclude it. Errors if the
parameter is used by an infinite variable or if it is contained in an
`AbstractMeasureData` DataType that is employed by
a measure since the measure becomes invalid otherwise. Thus, measures that
contain this dependency must be deleted first. Note that
[`parameter_refs`](@ref parameter_refs(::AbstractMeasureData)) needs to be
extended to allow deletion of parameters when custom `AbstractMeasureData`
datatypes are used.

**Example**
```julia-repl
julia> delete(model, x)
```
"""
function JuMP.delete(
    model::InfiniteModel,
    pref::IndependentParameterRef
    )::Nothing
    @assert JuMP.is_valid(model, pref) "Parameter reference is invalid."
    gvref = _make_parameter_ref(JuMP.owner_model(pref), JuMP.index(pref))
    # ensure deletion is okay (pref isn't used by measure data)
    for mindex in _measure_dependencies(pref)
        data = measure_data(dispatch_variable_ref(model, mindex))
        _check_param_in_data(gvref, data)
    end
    # ensure pref is not used by an infinite variable
    if used_by_infinite_variable(pref)
        error("Cannot delete `$pref` since it is used by an infinite ",
              "variable(s).")
    end
    # ensure pref is not used by a parameter function
    if used_by_parameter_function(pref)
        error("Cannot delete `$pref` since it is used by an parameter ",
              "function(s).")
    end
    # update optimizer model status
    if is_used(pref)
        set_optimizer_model_ready(model, false)
    end
    # delete dependence of measures on pref
    _update_measures(model, gvref)
    # delete any derivatives that use pref
    for index in _derivative_dependencies(pref)
        JuMP.delete(model, dispatch_variable_ref(model, index))
    end
    # update constraints in mapping to remove the parameter
    _update_constraints(model, gvref)
    # delete parameter information stored in model
    obj_num = _object_number(pref)
    param_nums = _parameter_numbers(pref)
    _delete_data_object(pref)
    # update the object numbers and parameter numbers
    _update_model_numbers(model, obj_num, param_nums)
    return
end

# FiniteParameterRef
function JuMP.delete(model::InfiniteModel, pref::FiniteParameterRef)::Nothing
    @assert JuMP.is_valid(model, pref) "Parameter reference is invalid."
    # update optimizer model status
    if is_used(pref)
        set_optimizer_model_ready(model, false)
    end
    gvref = _make_parameter_ref(model, JuMP.index(pref))
    # delete dependence of measures on pref
    _update_measures(model, gvref)
    # update constraints in mapping to remove the parameter
    _update_constraints(model, gvref)
    # update the objective if necessary
    if used_by_objective(pref)
        func = JuMP.objective_function(model)
        if func isa GeneralVariableRef
            new_func = zero(JuMP.GenericAffExpr{Float64, GeneralVariableRef})
            JuMP.set_objective_function(model, new_func)
            JuMP.set_objective_sense(model, MOI.FEASIBILITY_SENSE)
        else
            _remove_variable(func, gvref)
        end
    end
    # delete parameter information stored in model
    _delete_data_object(pref)
    return
end


text:
export message, GitNote

type GitNote
    id::Oid
    msg::UTF8String
end

GitNote(ptr::Ptr{Void}) = begin
    @assert ptr != C_NULL
    id  = Oid(ccall((:git_note_id, libgit2), Ptr{Uint8}, (Ptr{Void},), ptr))
    msg = utf8(bytestring(ccall((:git_note_message, libgit2), Ptr{Uint8}, (Ptr{Void},), ptr)))
    return GitNote(id, msg)
end

Oid(n::GitNote) = n.id

message(n::GitNote) = n.msg
notes(obj::GitObject, ref::MaybeString=nothing) = lookup_note(obj, ref)

function lookup_note(obj::GitObject, ref::MaybeString=nothing)
    oid = Oid(obj)
    note_ptr = Ptr{Void}[0]
    repo_ptr = ccall((:git_object_owner, libgit2), Ptr{Void}, (Ptr{Void},), obj)
    err = ccall((:git_note_read, libgit2), Cint,
                (Ptr{Ptr{Void}}, Ptr{Void}, Ptr{Uint8}, Ptr{Oid}),
                note_ptr, repo_ptr, ref != nothing ? ref : C_NULL, &oid)
    if err == GitErrorConst.ENOTFOUND
        return nothing
    elseif err != GitErrorConst.GIT_OK
        if note_ptr[1] != C_NULL
            ccall((:git_note_free, libgit2), Void, (Ptr{Void},), note_ptr[1])
        end
        throw(LibGitError(err))
    end
    n = GitNote(note_ptr[1])
    ccall((:git_note_free, libgit2), Void, (Ptr{Void},), note_ptr[1])
    return n
end


text:
struct Svmda
    fm
    lev::AbstractVector
    ni::AbstractVector
end

"""
    svmda(X, y; kern = "rbf",
        gamma = 1. / size(X, 2), degree = 3, coef0 = 0., cost = 1.)
Support vector machine for discrimination (C-SVC).
* `X` : X-data.
* `y : y-data (univariate).
* 'kern' : Type of kernel used to compute the Gram matrices.
    Possible values are "krbf", "kpol", "klin" or "ktanh".
* 'gamma' : See below.
* 'degree' : See below.
* 'coef0' : See below.
* 'cost' : Cost of constraints violation C parameter.

Kernel types :
* "krbf" -- radial basis function: exp(-gamma * |x - y|^2)
* "kpol" -- polynomial: (gamma * x' * y + coef0)^degree
* "klin* -- linear: x' * y
* "ktan" -- sigmoid: tanh(gamma * x' * y + coef0)

The function uses LIBSVM.jl (https://github.com/JuliaML/LIBSVM.jl)
that is an interface to library LIBSVM (Chang & Li 2001).

## References

Julia package LIBSVM.jl: https://github.com/JuliaML/LIBSVM.jl

Chang, C.-C. & Lin, C.-J. (2001). LIBSVM: a library for support vector machines.
Software available at http://www.csie.ntu.edu.tw/~cjlin/libsvm.
Detailed documentation (algorithms, formulae, ...) can be found in
http://www.csie.ntu.edu.tw/~cjlin/papers/libsvm.ps.gz

Chih-Chung Chang and Chih-Jen Lin, LIBSVM: a library for support vector machines.
ACM Transactions on Intelligent Systems and Technology, 2:27:1--27:27, 2011.
Software available at http://www.csie.ntu.edu.tw/~cjlin/libsvm

Schölkopf, B., Smola, A.J., 2002. Learning with kernels:
support vector machines, regularization, optimization, and beyond.
Adaptive computation and machine learning. MIT Press, Cambridge, Mass.

"""
function svmda(X, y; kern = "krbf",
    gamma = 1. / size(X, 2), degree = 3, coef0 = 0., cost = 1.)
    gamma = Float64(gamma) ; degree = Int64(degree) ; coef0  = Float64(coef0) ;
    cost  = Float64(cost)
    X = ensure_mat(X)
    y = vec(y)
    ztab = tab(y)
    if kern == "krbf"
        fkern = LIBSVM.Kernel.RadialBasis
    elseif kern == "kpol"
        fkern = LIBSVM.Kernel.Polynomial
    elseif kern == "klin"
        fkern = LIBSVM.Kernel.Linear
    elseif kern == "ktanh"
        fkern = LIBSVM.Kernel.Sigmoid
    end
    nt = 0
    fm = svmtrain(X', y;
        svmtype = SVC,
        kernel = fkern,
        gamma =  gamma,
        coef0 = coef0,
        degree = degree,
        cost = cost,
        nt = nt)
    Svmda(fm, ztab.keys, ztab.vals)
end

"""
    predict(object::Svmda, X)
Compute y-predictions from a fitted model.
* `object` : The fitted model.
* `X` : X-data for which predictions are computed.
"""
function predict(object::Svmda, X)
    pred = svmpredict(object.fm, X')[1]
    n = length(pred)
    pred = reshape(pred, n, 1)
    (pred = pred,)
end


text:
using ITensors

d = 1
i = Index([QN(0, 2) => d, QN(1, 2) => d], "i")

# Parity conserving ITensors
# By default they have flux 0
@show A = randomITensor(i', dag(i))
println()
@show B = randomITensor(i', dag(i))
println()
@show C = randomITensor(QN(1, 2), i', dag(i))
println()

# Add them
@show A + B
println()

# Can't add QN ITensors with different flux
#A + C

# Contract them
@show A' * B
println()
@show A' * C
println()

# Combine the indices to turn into a vector
comb = combiner(i', dag(i))
@show A * comb
println()


text:
function StructuredQueries._collect{T<:AbstractTable}(
    g_tbl::Grouped{T}, q::SQ.FilterNode
)::Grouped
    new_src = SQ._collect(g_tbl.source, q)
    return grouped(new_src, g_tbl.groupbys, g_tbl.predicate_aliases)
end