text:
isna(x, nastrings) = x == "" || x in nastrings

const common_date_formats = Any[
    dateformat"yyyy-mm-dd", dateformat"yyyy/mm/dd",
    dateformat"mm-dd-yyyy", dateformat"mm/dd/yyyy",
    dateformat"dd-mm-yyyy", dateformat"dd/mm/yyyy",
    dateformat"dd u yyyy",  dateformat"e, dd u yyyy"
]

const common_datetime_formats = Any[
    dateformat"yyyy-mm-ddTHH:MM:SS",
    dateformat"yyyy-mm-dd HH:MM:SS",
    ISODateTimeFormat,
    dateformat"yyyy-mm-dd HH:MM:SS.s",
    RFC1123Format,
    dateformat"yyyy/mm/dd HH:MM:SS.s",
    dateformat"yyyymmdd HH:MM:SS.s"
]

const DEFAULT_QUOTES = ('"', '\'')

function guessdateformat(str, len=lastindex(str))

    dts = Any[Date => d for d in common_date_formats]
    dts = vcat(dts, Any[DateTime => d for d in common_datetime_formats])

    for (typ, df) in dts
        x, l = try
            tryparsenext_internal(typ, str, 1, len, df)
        catch err
            continue
        end
        if !isnull(x)
            try
                typ(get(x)...)
                if l > len
                    return DateTimeToken(typ, df)
                end
            catch err; end
        end
    end
    return nothing
end

# force precompilation
guessdateformat("xyz", 3)

function getquotechar(x)
    if (length(x) > 0 && x[1] in DEFAULT_QUOTES) && last(x) == x[1]
        return x[1]
    end
    return '\0'
end

function guesstoken(x, opts, prevent_quote_wrap, @nospecialize(prev_guess=Unknown()), nastrings=NA_STRINGS, stringarraytype=StringArray)
    q = getquotechar(x)

    if isa(prev_guess, StringToken)
        # there is nothing wider than a string
        return prev_guess
    elseif q !== '\0'
        # remove quotes and detect inner token
        if isa(prev_guess, Quoted)
            prev_inner = prev_guess.inner
        else
            prev_inner = prev_guess
        end
        inner_string = strip(strip(x, q))
        if inner_string==""
            # If we come across a "", we classify it as a string column no matter what
            return Quoted(StringToken(stringarraytype<:StringArray ? StrRange : String), opts.quotechar, opts.escapechar)
        else
            inner_token = guesstoken(inner_string, opts, true, prev_inner, nastrings, stringarraytype)
            return Quoted(inner_token, opts.quotechar, opts.escapechar)
        end
    elseif isa(prev_guess, Quoted)
        # but this token is not quoted
        return Quoted(guesstoken(x, opts, true, prev_guess.inner, nastrings, stringarraytype), opts.quotechar, opts.escapechar)
    elseif isa(prev_guess, NAToken)
        # This column is nullable
        if isna(x, nastrings)
            # x is null too, return previous guess
            return prev_guess
        else
            tok = guesstoken(x, opts, false, prev_guess.inner, nastrings, stringarraytype)
            if isa(tok, Quoted) && isa(tok.inner, StringToken)
                return tok # never wrap a string in NAToken
            elseif isa(tok, Quoted)
                # Always put the quoted wrapper on top
                return Quoted(NAToken(tok.inner), opts.quotechar, opts.escapechar)
            else
                return NAToken(tok, nastrings=nastrings)
            end
        end
    elseif isna(x, nastrings)
        return NAToken(prev_guess, nastrings=nastrings)
    else
        # x is neither quoted, nor null,
        # prev_guess is not a NAToken or a StringToken
        ispercent = strip(x)[end] == '%'
        if ispercent
            x = x[1:end-1]
        end
        if tryparse(Int, x) !== nothing || tryparse(Float64, x) !== nothing
            T = tryparse(Int, x) === nothing ? Float64 : Int

            if ispercent
                return Percentage()
            end

            if prev_guess == Unknown()
                return Numeric(T)
            elseif isa(prev_guess, Numeric)
                return Numeric(promote_type(T, fieldtype(prev_guess)))
            else
                # something like a date turned into a single number?
                y1 = StringToken(stringarraytype<:StringArray ? StrRange : String)
                return prevent_quote_wrap ? y1 : Quoted(y1, opts.quotechar, opts.escapechar)
            end
        else
            # fast-path
            if length(filter(isnumeric, x)) < 4
                y2 = StringToken(stringarraytype<:StringArray ? StrRange : String)
                return prevent_quote_wrap ? y2 : Quoted(y2, opts.quotechar, opts.escapechar)
            end

            maybedate = guessdateformat(x)
            if maybedate === nothing
                y3 = StringToken(stringarraytype<:StringArray ? StrRange : String)
                return prevent_quote_wrap ? y3 : Quoted(y3, opts.quotechar, opts.escapechar)
            else
                return maybedate
            end
        end
    end
end


text:
module TestVariableSOCtoRSOC

using Test

using MathOptInterface
const MOI = MathOptInterface

function runtests()
    for name in names(@__MODULE__; all = true)
        if startswith("$(name)", "test_")
            @testset "$(name)" begin
                getfield(@__MODULE__, name)()
            end
        end
    end
    return
end

include("../utilities.jl")

function test_soc1v()
    mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}())
    bridged_mock = MOI.Bridges.Variable.SOCtoRSOC{Float64}(mock)
    mock.optimize! =
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            [1 / √2 + 1 / 2, 1 / √2 - 1 / 2, 1 / √2],
            (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]],
        )
    MOI.Test.test_conic_SecondOrderCone_VectorOfVariables(
        bridged_mock,
        MOI.Test.Config(),
    )
    ceqs = MOI.get(
        mock,
        MOI.ListOfConstraintIndices{
            MOI.VectorAffineFunction{Float64},
            MOI.Zeros,
        }(),
    )
    @test length(ceqs) == 1
    MOI.set(bridged_mock, MOI.ConstraintName(), ceqs[1], "ceq")

    var_names = ["a", "b", "c"]
    MOI.set(
        mock,
        MOI.VariableName(),
        MOI.get(mock, MOI.ListOfVariableIndices()),
        var_names,
    )
    rsocs = MOI.get(
        mock,
        MOI.ListOfConstraintIndices{
            MOI.VectorOfVariables,
            MOI.RotatedSecondOrderCone,
        }(),
    )
    @test length(rsocs) == 1
    MOI.set(mock, MOI.ConstraintName(), rsocs[1], "rsoc")

    invs2 = 1 / √2
    s = """
    variables: a, b, c
    rsoc: [a, b, c] in MathOptInterface.RotatedSecondOrderCone(3)
    ceq: [$invs2*a + $invs2*b + -1.0] in MathOptInterface.Zeros(1)
    maxobjective: $invs2*a + -$invs2*b + c
    """
    model = MOI.Utilities.Model{Float64}()
    MOI.Utilities.loadfromstring!(model, s)
    MOI.Test.util_test_models_equal(mock, model, var_names, ["rsoc", "ceq"])
    var_names = ["x", "y", "z"]
    MOI.set(
        bridged_mock,
        MOI.VariableName(),
        MOI.get(bridged_mock, MOI.ListOfVariableIndices()),
        var_names,
    )
    socs = MOI.get(
        bridged_mock,
        MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.SecondOrderCone}(),
    )
    @test length(socs) == 1
    MOI.set(bridged_mock, MOI.ConstraintName(), socs[1], "soc")

    s = """
    variables: x, y, z
    soc: [x, y, z] in MathOptInterface.SecondOrderCone(3)
    ceq: [x + -1.0] in MathOptInterface.Zeros(1)
    maxobjective: y + z
    """
    model = MOI.Utilities.Model{Float64}()
    MOI.Utilities.loadfromstring!(model, s)
    MOI.Test.util_test_models_equal(
        bridged_mock,
        model,
        var_names,
        ["soc", "ceq"],
    )
    xyz = MOI.get(bridged_mock, MOI.ListOfVariableIndices())

    message = string(
        "Cannot delete variable as it is constrained with other",
        " variables in a `MOI.VectorOfVariables`.",
    )
    for i in eachindex(xyz)
        err = MOI.DeleteNotAllowed(xyz[i], message)
        @test_throws err MOI.delete(bridged_mock, xyz[i])
    end

    _test_delete_bridged_variables(
        bridged_mock,
        xyz,
        MOI.SecondOrderCone,
        3,
        ((MOI.VectorOfVariables, MOI.RotatedSecondOrderCone, 0),),
    )
    return
end

end  # module

TestVariableSOCtoRSOC.runtests()

ext: jl


text:
type MatchRule{T} end

# default transform is to do nothing
transform(fn::Function, value) = value

function transform(fn::Function, node::Node)
  if isa(node.children, Array)
    transformedchildren = [transform(fn, child) for child in node.children]
  else
    transformedchildren = transform(fn, node.children)
  end

  if method_exists(fn, (Node, Any, MatchRule{Symbol(node.name)}))
    label = MatchRule{Symbol(node.name)}()
  else
    label = MatchRule{:default}()
  end

  return fn(node, transformedchildren, label)
end


text:
using DSP, Test
using Statistics: mean

@testset "hilbert" begin
    # Testing hilbert transform
    t = (0:1/256:2-1/256)
    a0 = sinpi.(t)
    a1 = cospi.(t)
    a2 = sinpi.(2*t)
    a3 = cospi.(2*t)
    a = hcat(a0, a1, a2, a3)

    h = hcat(hilbert(a0), hilbert(a1), hilbert(a2), hilbert(a3))
    h_abs = abs.(h)
    h_angle = angle.(h)
    h_real = real.(h)
    #The real part should be equal to the original signals:
    @test h_real ≈ a

    #The absolute value should be one everywhere, for this input:
    @test h_abs ≈ ones(size(a))

    #For the 'slow' sine - the phase should go from -pi/2 to pi/2 in
    #the first 256 bins:
    @test h_angle[1:256,1] ≈ -pi/2:pi/256:pi/2-pi/256

    #For the 'slow' cosine - the phase should go from 0 to pi in the
    #same interval:
    @test h_angle[1:256,2] ≈ 0:pi/256:pi-pi/256

    #The 'fast' sine should make this phase transition in half the time:
    @test h_angle[1:128,3] ≈ -pi/2:pi/128:pi/2-pi/128

    #Ditto for the 'fast' cosine:
    @test h_angle[1:128,4] ≈ 0:pi/128:pi-pi/128

    #The imaginary part of hilbert(cos(t)) = sin(t) Wikipedia
    @test imag(h[:,2]) ≈ a0

    #Sanity check with odd number of samples
    h2 = hilbert([ones(10); zeros(9)])
    @test real(h2) ≈ [ones(10); zeros(9)]

    #Sanity check with integer arguments
    r = rand(1:20, 128)
    @test hilbert(r) == hilbert(map(Float64, r))

    # Test hilbert with 2D input
    @test h ≈ hilbert(a)
end

@testset "fft helpers" begin
    @test meanfreq(sin.(2*π*10*(0:1e-3:10*π)),1e3) ≈ 10.0 rtol=1e-3

    # nextfastfft
    @test nextfastfft(64) == 64
    @test nextfastfft(65) == 70
    @test nextfastfft(127) == 128
    @test nextfastfft((64,65,127)) == (64,70,128)
    @test nextfastfft(64,65,127) == nextfastfft((64,65,127))
end

## COMMON DSP TOOLS

@testset "dB conversion" begin
    # dB conversion
    @test 3dB ≈ db2pow(3)
    @test -3dB ≈ db2pow(-3)
    @test 3dBa ≈ db2amp(3)
    @test -3dBa ≈ db2amp(-3)
    @test isa(3e0dB, Float64)
    @test isa(3f0dB, Float32)
    test_num = convert(Float64, pi)
    @test pow2db(test_num) ≈ 10*log10(test_num)
    @test amp2db(test_num) ≈ 20*log10(test_num)
    @test test_num*dB ≈ db2pow(test_num)
    @test test_num*dBa ≈ db2amp(test_num)
    @test test_num ≈ db2pow(pow2db(test_num))
    @test test_num ≈ db2amp(amp2db(test_num))
end

@testset "rms" begin
    n = (5,6)
    for x in ( randn(n), randn(n)+randn(n)im )
        @test rms(x) ≈ sqrt(mean(abs.(x).^2))
        @test rmsfft(fft(x)) ≈ rms(x)
    end # for
end

@test shiftin!([1,2,3,4],[5,6]) ≈ [3,4,5,6]

## arraysplit
@test collect(DSP.arraysplit(collect(1:10), 3, 1)) == [ collect(1.0:3), collect(3.0:5), collect(5.0:7), collect(7.0:9)]

# DELAY FINDING UTILITIES

@testset "delay finders" begin
    # Test signals length
    L = 10
    # Test delay
    d = 3

    # Forcing valid test parameters values
    L = round(Int, abs(L))
    d = round(Int, min(abs(d), L))

    # Generating the test signal as random 0 mean white uniform noise
    x = 2.0 .* rand(L) .- 1.0

    # Tests

    @test finddelay([zeros(d); x], x) == d
    @test finddelay([zeros(d); x], -x) == d
    @test finddelay(x, [zeros(d); x]) == -d
    @test finddelay(-x, [zeros(d); x]) == -d

    @test shiftsignal(x, d) == [zeros(d); x[1:(end - d)]]
    @test shiftsignal(x, -d) == [x[(d + 1):end]; zeros(d)]

    y = copy(x)
    z = shiftsignal!(y, d)
    @test z == y
    @test y == [zeros(d); x[1:(end - d)]]

    y = copy(x)
    z = shiftsignal!(y, -d)
    @test z == y
    @test y == [x[(d + 1):end]; zeros(d)]

    y, s = alignsignals([zeros(d); x], x)
    @test y == [x; zeros(d)]
    @test s == d

    y, s = alignsignals(x, [zeros(d); x])
    @test y == [zeros(d); x[1:(end - d)]]
    @test s == -d

    y = copy(x)
    z, s = alignsignals!(y, [zeros(d); x])
    @test z == y
    @test s == -d
    @test y == [zeros(d); x[1:(end - d)]]

    y = [zeros(d); x]
    z, s = alignsignals!(y, x)
    @test z == y
    @test s == d
    @test y == [x; zeros(d)]

end


text:
# MIT license
# Copyright (c) Microsoft Corporation. All rights reserved.
# See LICENSE in the project root for full license information.

# TODO may want to test this but it's very slow on azure
@testset "Examples" begin
    @test_all_no_arg_functions Examples
end # testset Examples


text:
using ACE, Test, StaticArrays, BenchmarkTools, ACEbase
using ACE: read_dict, write_dict,
           evaluate, evaluate_d, evaluate_dd
using ACEbase.Testing: println_slim, print_tf, fdtest
using LinearAlgebra: norm
using LegibleLambdas

# TODO: move this into ACEbase.Testing
function save_load(obj)
   D = write_dict(obj)
   tmpf = tempname() * ".json"
   ACEbase.FIO.save_dict(tmpf, D)
   D2 = ACEbase.FIO.load_dict(tmpf)
   return read_dict(load_dict(tmpf))
end

##

@info("Testing Legible Lambdas")
ff = [ ((@λ r -> exp(-r)), r -> exp(-r), () -> 3 * rand()),
       ((@λ r -> 1 / (1 + r)^3), r -> 1 / (1 + r)^3, () -> 3 * rand()),
       ((@λ rr -> norm(rr)), rr -> norm(rr), () -> randn(SVector{3, Float64})),
       ((@λ rr -> exp(-sum(abs2, rr))), rr -> exp(-sum(abs2, rr)), () -> randn(SVector{3, Float64})),
       ]

for (f, g, rndx) in ff
   show(f);  println()
   xx = [ rndx() for _=1:30 ]
   println_slim( @test f.(xx) ≈ g.(xx) )
   f1 = save_load(f)
   println_slim( @test f.(xx) ≈ f1.(xx) )
   for ntest = 1:30
      x = rand() * 3
      print_tf(@test all(fdtest(x -> evaluate(f, x), x -> evaluate_d(f, x), x; verbose=false)))
   end
   println()
end

##

@info("Performance test")

f, g, rndx = ff[end]
f1 = read_dict(write_dict(f))

print("      Raw Anon:"); @btime $g(($rndx)())
print(" LegibleLambda:"); @btime evaluate($f, ($rndx)())
print("  Deserialized:"); @btime evaluate($f1, ($rndx)())

@info("Confirm zero allocations and performance")
tmin = minimum( (@benchmark $g(($rndx)())).times )
for h in [f, f1]
   bm = @benchmark evaluate($h, ($rndx)())
   println(@test bm.allocs == 0 && bm.memory == 0)
   println(@test (minimum(bm.times) <= 1.2 * tmin))
end


text:
#=
load:
- Julia version: 1.5.1
- Author: Berk
- Date: 2020-09-04
=#

using Pkg
Pkg.activate("test")

using DataFrames
using JuMP
using CPLEX
using CSV
# using Gurobi
# using Ipopt
using JLD
using MathOptInterface
using Test
using Random

include("../src/OCTHaGOn.jl")
using .OCTHaGOn
global MOI = MathOptInterface
global SOLVER_SILENT = with_optimizer(CPLEX.Optimizer, CPX_PARAM_SCRIND = 0)
# global GUROBI_SILENT = with_optimizer(Gurobi.Optimizer, OutputFlag = 0, Gurobi.Env())
Random.seed!(1);
MOI.Silent() = true;

include(OCTHaGOn.PROJECT_ROOT * "/test/tools/gams.jl");

include(OCTHaGOn.PROJECT_ROOT * "/test/tools/sagemark.jl");

include(OCTHaGOn.PROJECT_ROOT * "/test/tools/models.jl")

include(OCTHaGOn.DATA_DIR * "speed_reducer.jl")

include(OCTHaGOn.DATA_DIR * "water_network.jl")

include(OCTHaGOn.DATA_DIR * "oos.jl")

include(OCTHaGOn.DATA_DIR * "afpm.jl")

include(OCTHaGOn.DATA_DIR * "polynomial.jl")

include(OCTHaGOn.DATA_DIR * "foxes_and_rabbits.jl")

include(OCTHaGOn.BARON_DIR * "gear.jl")

include(OCTHaGOn.BARON_DIR * "minlp.jl")

include(OCTHaGOn.BARON_DIR * "minlp_demo.jl")

include(OCTHaGOn.BARON_DIR * "nlp1.jl")

include(OCTHaGOn.BARON_DIR * "nlp2.jl")

include(OCTHaGOn.BARON_DIR * "nlp3.jl")

include(OCTHaGOn.BARON_DIR * "pool1.jl")


text:
using Test

@testset "ExceptionUnwrapping.jl" begin
    include("ExceptionUnwrapping.jl")
end
@testset "test_throws_wrapped.jl" begin
    include("test_throws_wrapped.jl")
end


text:
using StatsBase

function test_grow_function(phenotypes, expected)
    @assert length(phenotypes) == length(expected)
    cells = collect(1:length(phenotypes))
    cell_phenotypes = phenotypes
    output_c = Array{Int}(length(cells)*4)
    output_p = Array{Float64}(size(output_c))

    num_runs = 10000
    data = Array{Int}(length(cells), num_runs)
    setup = GrowthScreen()
    setup.noise = 0.00001

    for i in 1:num_runs
        num_inserted = grow!(cells, cell_phenotypes, output_c, output_p, setup)
        data[:, i] = StatsBase.counts(output_c[1:num_inserted])
    end
    all(round.(mean(data, 2), 1) .== expected)
end

# test that, averaged over many runs, each phenotype corresponds to the
# expected number of doublings
@test test_grow_function([0, -1, 1, 0.5, -0.5], [2.0, 1.0, 4.0, 3.0, 1.5])
# unbalanced
@test test_grow_function([0, -1, -0.5, -0.8], [2.0, 1.0, 1.5, 1.2])


text:
# # Multinomial logistic regression

import MaximumLikelihoodProblems

import Distributions
import ForwardDiff
import LogDensityProblems
import NNlib
import TransformVariables

struct MultinomialLogisticRegression{Ty, TX}
    y::Ty
    X::TX
end

function (problem::MultinomialLogisticRegression)(θ)
    y = problem.y
    X = problem.X

    β = θ.β

    num_rows = size(X, 1)
    num_covariates = size(β, 1) # `size(β, 1)` is equal to `size(X, 2)`
    num_classes = size(β, 2) + 1

    ## the first column of all zeros corresponds to the base class
    ## i.e. the coefficient β₀ for the base class is always fixed to be zero
    β_with_base_class = hcat(zeros(num_covariates), β)
    η = X * β_with_base_class

    μ = NNlib.softmax(η; dims=2)
    log_likelihood = sum([Distributions.logpdf(Distributions.Multinomial(1, μ[i, :]), y[i, :]) for i = 1:num_rows])
    return log_likelihood
end

N = 10_000

## the first column (the column of all ones) is the intercept
X = hcat(ones(N), randn(N))

size_β = (2, 3)
β_true = [1.0 2.0 3.0; 4.0 5.0 6.0]
num_covariates = size(β_true, 1)
β_true_with_base_class = hcat(zeros(num_covariates), β_true)
η_true = X * β_true_with_base_class
μ_true = NNlib.softmax(η_true; dims=2)
y = vcat([rand(Distributions.Multinomial(1, μ_true[i,:]))' for i in 1:N]...)

problem = MultinomialLogisticRegression(y, X)
transformation = TransformVariables.as((β = TransformVariables.as(Array, size_β), ))
transformed_problem = LogDensityProblems.TransformedLogDensity(transformation,
                                                               problem)
transformed_gradient_problem = LogDensityProblems.ADgradient(:ForwardDiff,
                                                             transformed_problem)

β_hat_initial_guess = zeros(size_β)
θ_hat_initial = (; β = β_hat_initial_guess)

# θ_hat:
# (I recommend changing the value of `show_progress_meter` to `true` so that
# you can see the progress.)

θ_hat = MaximumLikelihoodProblems.fit(transformed_gradient_problem,
                                      θ_hat_initial;
                                      show_progress_meter = false) # change this `false` to `true`

# β_hat:

β_hat = θ_hat[:β]

# β\_hat\_with\_base\_class:

num_covariates = size(β_hat, 1)
β_hat_with_base_class = hcat(zeros(num_covariates), β_hat)

# Value of the log likelihood function evaluated at θ_hat:

MaximumLikelihoodProblems.loglikelihood(transformed_gradient_problem, θ_hat)


text:
#=
test_timeevolution:
- Julia version: 1.5.2
- Author: qcancam
- Date: 2021-01-03
=#

include("../src/MatrixProductStates.jl")

using .MatrixProductStates, SparseArrays, Arpack

g = 1.0; L = 7
β=100.0
N=100.0
τ = β/N

function H_TFIM(g, L)
    H = Array{Complex{Float64}, 2}[]
    id = [1  0;
          0  1]
    σˣ = [0  1;
          1  0]
    σᶻ = [1  0;
          0 -1]
    for i in 1:L-2
        push!(H,-σᶻ⊗σᶻ-g*σˣ⊗id)
    end
    #=L-1=#
    push!(H,-σᶻ⊗σᶻ-g*(σˣ⊗id+ id⊗σˣ))

    return H
end

function H_TFIM_MPO(g, L)
    id = [1  0;
          0  1]
    σˣ = [0  1;
          1  0]
    σᶻ = [1  0;
          0 -1]
    W_tnsr = zeros(Complex{Float64}, 3, 3, 2, 2)
    W_tnsr[1, 1, :, :] = id
    W_tnsr[2, 1, :, :] = -σᶻ
    W_tnsr[3, 1, :, :] = -σᶻ*g
    W_tnsr[3, 2, :, :] = σᶻ
    W_tnsr[3, 3, :, :] = id

    return MPO(W_tnsr, L)
end

H = H_TFIM(g, L)
ψ = randn(MPS{L, Complex{Float64}}, 100, 2)

ϕ = imag_time_evolution(ψ, H[1],H[2],H[L-1],H_TFIM_MPO(g,L), β,N,50)


text:
# Benchmarking the NLsolve.fixedpoint() function in the use case of: https://lectures.quantecon.org/jl/mccall_model_with_separation.html

# Dependencies.
using Distributions # So that some method overwritten error from NLsolve doesn't intermingle with the benchmarks.

# Wages (global since used in constructor)
const n = 60                                   # n possible outcomes for wage
const default_w_vec = linspace(10, 20, n)      # wages between 10 and 20
const a, b = 600, 400                          # shape parameters
const dist = BetaBinomial(n-1, a, b)
const default_p_vec = pdf.(dist, support(dist))

# Model Struct (global since types can't be defined in local scope)
mutable struct McCallModel{TF <: AbstractFloat,
                        TAV <: AbstractVector{TF},
                        TAV2 <: AbstractVector{TF}}
    α::TF         # Job separation rate
    β::TF         # Discount rate
    γ::TF         # Job offer rate
    c::TF         # Unemployment compensation
    σ::TF         # Utility parameter
    w_vec::TAV    # Possible wage values
    p_vec::TAV2   # Probabilities over w_vec

    McCallModel(α::TF=0.2,
                β::TF=0.98,
                γ::TF=0.7,
                c::TF=6.0,
                σ::TF=2.0,
                w_vec::TAV=default_w_vec,
                p_vec::TAV2=default_p_vec) where {TF, TAV, TAV2} =
        new{TF, TAV, TAV2}(α, β, γ, c, σ, w_vec, p_vec)
end

# Implementation from McCall with separation lecture on QuantEcon
function default_quantecon()
    # Utility function.
    function u(c::Real, σ::Real)
        if c > 0
            return (c^(1 - σ) - 1) / (1 - σ)
        else
            return -10e6
        end
    end
    # Instantiate model object.
    mcm = McCallModel()
    # Define the function to be used in the iteration loop.
    """
    A function to update the Bellman equations.  Note that V_new is modified in
    place (i.e, modified by this function).  The new value of U is returned.
    """
    function update_bellman!(mcm::McCallModel,
                            V::AbstractVector,
                            V_new::AbstractVector,
                            U::Real)
        # Simplify notation
        α, β, σ, c, γ = mcm.α, mcm.β, mcm.σ, mcm.c, mcm.γ

        for (w_idx, w) in enumerate(mcm.w_vec)
            # w_idx indexes the vector of possible wages
            V_new[w_idx] = u(w, σ) + β * ((1 - α) * V[w_idx] + α * U)
        end

        U_new = u(c, σ) + β * (1 - γ) * U +
                        β * γ * dot(max.(U, V), mcm.p_vec)

        return U_new
    end
    # Define a function to carry out the iteration loop.
    function solve_mccall_model(mcm::McCallModel;
                                tol::AbstractFloat=1e-5,
                                max_iter::Integer=2000)

        V = ones(length(mcm.w_vec))    # Initial guess of V
        V_new = similar(V)             # To store updates to V
        U = 1.0                        # Initial guess of U
        i = 0
        error = tol + 1

        while error > tol && i < max_iter
            U_new = update_bellman!(mcm, V, V_new, U)
            error_1 = maximum(abs, V_new - V)
            error_2 = abs(U_new - U)
            error = max(error_1, error_2)
            V[:] = V_new
            U = U_new
            i += 1
        end

        return V, U
    end
    # Get results.
    return solve_mccall_model(mcm)
end

# Implementation of the above, but with one vector that holds the state of the calculation [U V]
function stacked_quantecon()
    # Utility function.
    function u(c::Real, σ::Real)
        if c > 0
            return (c^(1 - σ) - 1) / (1 - σ)
        else
            return -10e6
        end
    end
    # Instantiate model object.
    mcm = McCallModel()
    # Function for each loop of the iteration.
    """
    An (in-place) stacked implementation of the Bellman operator for the McCall model with separation. By convention, U is at the top of the vector.
    bellman_operator!(newVec::AbstractVector, oldVec::AbstractVector, model::McCallModel = mcm)
    """
    function bellman_operator!(newVec, oldVec, model = mcm)
        # Unpack parameters.
        α, β, σ, c, γ = mcm.α, mcm.β, mcm.σ, mcm.c, mcm.γ
        # Add new V(w) values to newVec.
        for (w_idx, w) in enumerate(mcm.w_vec)
            # w_idx indexes the vector of possible wages
            newVec[w_idx + 1] = u(w, σ) + β * ((1 - α) * oldVec[1 + w_idx] + α * oldVec[1])
        end
        # Add new U value to newVec.
        newVec[1] = u(c, σ) + β * (1 - γ) * oldVec[1] + β * γ * dot(max.(oldVec[1], oldVec[2:end]), mcm.p_vec)
    end
    # Function to carry out the iteration.
    """
    Modified version of solve_mccall_model(), which uses our bellman_operator! function.
    solve_mccall_model_inplace(mcm::McCallModel; tol::AbstractFloat=1e-5, max_iter::Integer=2000)
    """
    function solve_mccall_model(mcm; tol=1e-5, max_iter=2000)
        oldVec = ones(length(mcm.w_vec) + 1)  # Initial guess for states. Same as those in the QE implementation.
        newVec = similar(oldVec)  # To store updates.
        iter = 0    # Initialize the iteration counter.
        error = tol + 1 # Initialize the error.

        while error > tol && iter < max_iter
            bellman_operator!(newVec, oldVec, mcm)
            error = maximum(abs.(newVec - oldVec))
            iter += 1
            oldVec .= newVec
        end

        return newVec
    end
    # Get results.
    return solve_mccall_model(mcm)
end

# Implementation of stacked_quantecon(), but using the NLsolve fixed point method. m = 0.
function fixedpoint_iteration()
    # Utility function.
    function u(c::Real, σ::Real)
        if c > 0
            return (c^(1 - σ) - 1) / (1 - σ)
        else
            return -10e6
        end
    end
    # Instantiate model object.
    mcm = McCallModel()
    # Function for each loop of the iteration.
    """
    An (in-place) stacked implementation of the Bellman operator for the McCall model with separation. By convention, U is at the top of the state vector.
    bellman_operator!(newVec::AbstractVector, oldVec::AbstractVector, model::McCallModel = mcm)
    """
    function bellman_operator!(newVec, oldVec, model = mcm)
        # Unpack parameters.
        α, β, σ, c, γ = mcm.α, mcm.β, mcm.σ, mcm.c, mcm.γ
        # Add new V(w) values to newVec.
        for (w_idx, w) in enumerate(mcm.w_vec)
            # w_idx indexes the vector of possible wages
            newVec[w_idx + 1] = u(w, σ) + β * ((1 - α) * oldVec[1 + w_idx] + α * oldVec[1])
        end
        # Add new U value to newVec.
        newVec[1] = u(c, σ) + β * (1 - γ) * oldVec[1] + β * γ * dot(max.(oldVec[1], oldVec[2:end]), mcm.p_vec)
    end
    # Solve
    init = ones(length(mcm.w_vec)+1)
    return fixedpoint(bellman_operator!, init; ftol = 1e-5, iterations = 2000)
end

# Using fixedpoint() for variable m.
function fixedpoint_anderson_m(m)
    # Utility function.
    function u(c::Real, σ::Real)
        if c > 0
            return (c^(1 - σ) - 1) / (1 - σ)
        else
            return -10e6
        end
    end
    # Instantiate model object.
    mcm = McCallModel()
    # Function for each loop of the iteration.
    """
    An (in-place) stacked implementation of the Bellman operator for the McCall model with separation. By convention, U is at the top of the state vector.
    bellman_operator!(newVec::AbstractVector, oldVec::AbstractVector, model::McCallModel = mcm)
    """
    function bellman_operator!(newVec, oldVec, model = mcm)
        # Unpack parameters.
        α, β, σ, c, γ = mcm.α, mcm.β, mcm.σ, mcm.c, mcm.γ
        # Add new V(w) values to newVec.
        for (w_idx, w) in enumerate(mcm.w_vec)
            # w_idx indexes the vector of possible wages
            newVec[w_idx + 1] = u(w, σ) + β * ((1 - α) * oldVec[1 + w_idx] + α * oldVec[1])
        end
        # Add new U value to newVec.
        newVec[1] = u(c, σ) + β * (1 - γ) * oldVec[1] + β * γ * dot(max.(oldVec[1], oldVec[2:end]), mcm.p_vec)
    end
    # Solve
    init = ones(length(mcm.w_vec)+1)
    return fixedpoint(bellman_operator!, init; m = m, ftol = 1e-5, iterations = 2000)
end

function fixedpoint_newton()
    # Utility function.
    function u(c::Real, σ::Real)
        if c > 0
            return (c^(1 - σ) - 1) / (1 - σ)
        else
            return -10e6
        end
    end
    # Instantiate model object.
    mcm = McCallModel()
    # Function for each loop of the iteration.
    """
    An (in-place) stacked implementation of the Bellman operator for the McCall model with separation. By convention, U is at the top of the state vector.
    bellman_operator!(newVec::AbstractVector, oldVec::AbstractVector, model::McCallModel = mcm)
    """
    function bellman_operator!(newVec, oldVec, model = mcm)
        # Unpack parameters.
        α, β, σ, c, γ = mcm.α, mcm.β, mcm.σ, mcm.c, mcm.γ
        # Add new V(w) values to newVec.
        for (w_idx, w) in enumerate(mcm.w_vec)
            # w_idx indexes the vector of possible wages
            newVec[w_idx + 1] = u(w, σ) + β * ((1 - α) * oldVec[1 + w_idx] + α * oldVec[1])
        end
        # Add new U value to newVec.
        newVec[1] = u(c, σ) + β * (1 - γ) * oldVec[1] + β * γ * dot(max.(oldVec[1], oldVec[2:end]), mcm.p_vec)
    end
    # Solve
    init = ones(length(mcm.w_vec)+1)
    return fixedpoint(bellman_operator!, init; method = :newton, ftol = 1e-5, iterations = 2000)
end


text:
# Test operations between 2 different variables
@testset "Variable--Variable" begin
    # initialize model and references
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    # test addition
    @testset "Add" begin
        # test infinite + measure
        @test isa(@expression(m, inf + meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, inf + meas).constant == 0.0
        @test @expression(m, inf + meas).terms[meas] == 1
        @test @expression(m, inf + meas).terms[inf] == 1
        # test point + hold
        @test isa(@expression(m, pt + hold), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, pt + hold).terms[hold] == 1
        # test other combos
        @test isa(@expression(m, pt + meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + pt), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + inf), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + meas), GenericAffExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, meas + meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, meas + meas).terms[meas] == 2
        @test isa(@expression(m, inf + inf), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, pt + pt), GenericAffExpr{Float64, GeneralVariableRef})
    end
    # test subtraction
    @testset "Subtract" begin
        # test hold - measure
        @test isa(@expression(m, hold - meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, hold - meas).constant == 0.0
        @test @expression(m, hold - meas).terms[meas] == -1
        @test @expression(m, hold - meas).terms[hold] == 1
        # test point - hold
        @test isa(@expression(m, pt - hold), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, pt - hold).terms[hold] == -1
        # test other combos
        @test isa(@expression(m, pt - meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - pt), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - inf), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - meas), GenericAffExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, meas - meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, meas - meas) == zero(GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, inf - inf), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, pt - pt), GenericAffExpr{Float64, GeneralVariableRef})
    end
    # test multiplication
    @testset "Multiply" begin
        # test point * measure
        @test isa(@expression(m, pt * meas), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, pt * meas).aff.constant == 0.0
        pair = UnorderedPair{GeneralVariableRef}(pt, meas)
        @test @expression(m, pt * meas).terms[pair] == 1
        # test point * hold
        @test isa(@expression(m, pt * hold), GenericQuadExpr{Float64, GeneralVariableRef})
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, pt * hold).terms[pair] == 1
        # test other combos
        @test isa(@expression(m, pt * meas), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par * pt), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par * inf), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par * meas), GenericQuadExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, meas * meas), GenericQuadExpr{Float64, GeneralVariableRef})
        pair = UnorderedPair{GeneralVariableRef}(meas, meas)
        @test @expression(m, meas * meas).terms[pair] == 1
        @test isa(@expression(m, inf * inf), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, pt * pt), GenericQuadExpr{Float64, GeneralVariableRef})
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, inf / par)
        @test_throws ErrorException @expression(m, inf / inf)
        @test_throws ErrorException @expression(m, pt / hold)
    end
end

# Test operations between variables and GenericAffExpr
@testset "Variable--AffExpr" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    # test addition
    @testset "Add" begin
        # test infinite + aff1
        @test isa(@expression(m, inf + aff1), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, inf + aff1).constant == -2
        @test @expression(m, inf + aff1).terms[pt] == 1
        @test @expression(m, inf + aff1).terms[inf] == 2
        # test meas + aff2
        @test isa(@expression(m, meas + aff2), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, meas + aff2).terms[hold] == 1
        # test other combos
        @test isa(@expression(m, hold + aff3), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + aff2), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + aff1), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + aff3), GenericAffExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, meas + (meas + meas)), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, meas + (meas + meas)).terms[meas] == 3
    end
    # test subtraction
    @testset "Subtract" begin
        # test infinite - aff1
        @test isa(@expression(m, inf - aff1), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, inf - aff1).constant == 2
        @test @expression(m, inf - aff1).terms[pt] == -1
        @test @expression(m, inf - aff1).terms[inf] == 0
        # test meas + aff2
        @test isa(@expression(m, meas - aff2), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, meas - aff2).terms[hold] == -1
        # test other combos
        @test isa(@expression(m, hold - aff3), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - aff2), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - aff1), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - aff3), GenericAffExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, meas - (meas + meas)), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, meas - (meas + meas)).terms[meas] == -1
    end
    # test multiplication
    @testset "Multiply" begin
        # test infinite * aff1
        @test isa(@expression(m, inf * aff1), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, inf * aff1).aff.constant == 0
        @test @expression(m, inf * aff1).aff.terms[inf] == -2
        pair = UnorderedPair{GeneralVariableRef}(inf, pt)
        @test @expression(m, inf * aff1).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, inf)
        @test @expression(m, inf * aff1).terms[pair] == 1
        # test meas * aff2
        @test isa(@expression(m, meas * aff2), GenericQuadExpr{Float64, GeneralVariableRef})
        pair = UnorderedPair{GeneralVariableRef}(meas, hold)
        @test @expression(m, meas * aff2).terms[pair] == 1
        @test length(@expression(m, meas * aff2).aff.terms) == 0
        # test other combos
        @test isa(@expression(m, hold * aff3), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par * aff2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par * aff1), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par * aff3), GenericQuadExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, meas * (meas + meas)), GenericQuadExpr{Float64, GeneralVariableRef})
        pair = UnorderedPair{GeneralVariableRef}(meas, meas)
        @test @expression(m, meas * (meas + meas)).terms[pair] == 2
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, inf / aff1)
        @test_throws ErrorException @expression(m, inf / aff2)
        @test_throws ErrorException @expression(m, pt / aff3)
    end
end

# Test operations between GenericAffExpr and variable
@testset "AffExpr--Variable" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    # test addition
    @testset "Add" begin
        # test aff1 + inf
        @test isa(copy(aff1) + inf, GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff1) + inf).constant == -2
        @test @expression(m, copy(aff1) + inf).terms[pt] == 1
        @test @expression(m, copy(aff1) + inf).terms[inf] == 2
        # test aff2 + meas
        @test isa(copy(aff2) + meas, GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff2) + meas).terms[hold] == 1
        # test other combos
        @test isa(aff3 + hold, GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(aff2 + par, GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(aff1 + par, GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(aff3 + par, GenericAffExpr{Float64, GeneralVariableRef})
        # test same
        @test isa((meas + meas) + meas, GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, (meas + meas) + meas).terms[meas] == 3
    end
    # test subtraction
    @testset "Subtract" begin
        # test aff1 - inf
        @test isa(@expression(m, copy(aff1) - inf), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff1) - inf).constant == -2
        @test @expression(m, copy(aff1) - inf).terms[pt] == 1
        @test @expression(m, copy(aff1) - inf).terms[inf] == 0
        # test aff2 - meas
        @test isa(@expression(m, copy(aff2) - meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff2) - meas).terms[hold] == 1
        # test other combos
        @test isa(@expression(m, aff3 - hold), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff2 - par), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff1 - par), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff3 - par), GenericAffExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, (meas + meas) - meas), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, (meas + meas) - meas).terms[meas] == 1
    end
    # test multiplication
    @testset "Multiply" begin
        # test aff1 * inf
        @test isa(@expression(m, aff1 * inf), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, aff1 * inf).aff.constant == 0
        @test @expression(m, aff1 * inf).aff.terms[inf] == -2
        pair = UnorderedPair{GeneralVariableRef}(inf, pt)
        @test @expression(m, aff1 * inf).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, inf)
        @test @expression(m, aff1 * inf).terms[pair] == 1
        # test aff2 * meas
        @test isa(@expression(m, aff2 * meas), GenericQuadExpr{Float64, GeneralVariableRef})
        pair = UnorderedPair{GeneralVariableRef}(meas, hold)
        @test @expression(m, aff2 * meas).terms[pair] == 1
        @test length((aff2 * meas).aff.terms) == 0
        # test other combos
        @test isa(@expression(m, aff3 * hold), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff2 * par), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff1 * par), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff3 * par), GenericQuadExpr{Float64, GeneralVariableRef})
        # test same
        @test isa(@expression(m, (meas + meas * meas)), GenericQuadExpr{Float64, GeneralVariableRef})
        pair = UnorderedPair{GeneralVariableRef}(meas, meas)
        @test @expression(m, (meas + meas) * meas).terms[pair] == 2
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, aff1 / inf)
        @test_throws ErrorException @expression(m, aff2 / inf)
        @test_throws ErrorException @expression(m, aff3 / pt)
    end
end

# Test operators on GenericAffExpr and GenericAffExpr
@testset "AffExpr--AffExpr" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    @hold_variable(m, test[1:51])
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    aff4 = (inf + inf) + 3
    aff5 = 4pt - 42
    aff_big = sum(test[i] for i = 1:length(test))
    # test addition
    @testset "Add" begin
        # test large input
        @test isa(@expression(m, aff_big + aff1), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff1) + aff_big), GenericAffExpr{Float64, GeneralVariableRef})
        # test various mixed combination types
        @test isa(@expression(m, copy(aff1) + aff2), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff2 + aff3), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff2) + aff5), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff4 + aff1), GenericAffExpr{Float64, GeneralVariableRef})
        # test results of general combinations
        @test @expression(m, copy(aff1) + aff2).constant == -2
        @test @expression(m, copy(aff1) + aff2).terms[inf] == 1
        @test @expression(m, copy(aff1) + aff2).terms[pt] == 0
        @test @expression(m, aff4 + aff1).constant == 1
        @test @expression(m, aff4 + aff1).terms[inf] == 3
        @test @expression(m, aff4 + aff1).terms[pt] == 1
        # test results of measurefinite combinations
        @test @expression(m, aff2 + aff3).constant == 1
        @test @expression(m, aff2 + aff3).terms[meas] == -1
        @test @expression(m, aff2 + aff3).terms[hold] == 2
        @test @expression(m, copy(aff3) + aff5).constant == -41
        @test @expression(m, copy(aff3) + aff5).terms[meas] == -1
        @test @expression(m, copy(aff3) + aff5).terms[pt] == 4
        # test results of fininite combinations
        @test @expression(m, copy(aff2) + aff5).constant == -42
        @test @expression(m, copy(aff2) + aff5).terms[pt] == 3
        @test @expression(m, copy(aff2) + aff5).terms[hold] == 1
        # test pure additions
        @test isa(@expression(m, copy(aff4) + aff4), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff4) + aff4).constant == 6
        @test @expression(m, copy(aff4) + aff4).terms[inf] == 4
        @test isa(@expression(m, copy(aff5) + aff5), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff5) + aff5).constant == -84
        @test @expression(m, copy(aff5) + aff5).terms[pt] == 8
    end
    # test subtraction
    @testset "Subtract" begin
        # test various mixed combination types
        @test isa(@expression(m, copy(aff1) - aff2), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff2 - aff3), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff2) - aff5), GenericAffExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff4 - aff1), GenericAffExpr{Float64, GeneralVariableRef})
        # test results of general combinations
        @test @expression(m, copy(aff1) - aff2).constant == -2
        @test @expression(m, copy(aff1) - aff2).terms[inf] == 1
        @test @expression(m, copy(aff1) - aff2).terms[pt] == 2
        @test @expression(m, aff4 - aff1).constant == 5
        @test @expression(m, aff4 - aff1).terms[inf] == 1
        @test @expression(m, aff4 - aff1).terms[pt] == -1
        # test results of measurefinite combinations
        @test @expression(m, aff2 - aff3).constant == -1
        @test @expression(m, aff2 - aff3).terms[meas] == 1
        @test @expression(m, aff2 - aff3).terms[hold] == 0
        @test @expression(m, copy(aff3) - aff5).constant == 43
        @test @expression(m, copy(aff3) - aff5).terms[meas] == -1
        @test @expression(m, copy(aff3) - aff5).terms[pt] == -4
        # test results of fininite combinations
        @test @expression(m, copy(aff2) - aff5).constant == 42
        @test @expression(m, copy(aff2) - aff5).terms[pt] == -5
        @test @expression(m, copy(aff2) - aff5).terms[hold] == 1
        # test pure additions
        @test isa(@expression(m, copy(aff4) - aff4), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff4) - aff4).constant == 0
        @test @expression(m, copy(aff4) - aff4).terms[inf] == 0
        @test isa(@expression(m, copy(aff5) - aff5), GenericAffExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff5) - aff5).constant == 0
        @test @expression(m, copy(aff5) - aff5).terms[pt] == 0
    end
    # test multiplication
    @testset "Multiply" begin
        # test various mixed combination types
        @test isa(@expression(m, aff1 * aff2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff2 * aff3), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff2 * aff5), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, aff4 * aff1), GenericQuadExpr{Float64, GeneralVariableRef})
        # test results of general combinations
        @test @expression(m, aff1 * aff2).aff.constant == 0
        @test @expression(m, aff1 * aff2).aff.terms[hold] == -2
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, aff1 * aff2).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, aff1 * aff2).terms[pair] == -1
        @test @expression(m, aff4 * aff1).aff.constant == -6
        @test @expression(m, aff4 * aff1).aff.terms[inf] == -1
        pair = UnorderedPair{GeneralVariableRef}(inf, inf)
        @test @expression(m, aff4 * aff1).terms[pair] == 2
        pair = UnorderedPair{GeneralVariableRef}(inf, pt)
        @test @expression(m, aff4 * aff1).terms[pair] == 2
        # test results of measurefinite combinations
        @test @expression(m, aff2 * aff3).aff.constant == 0
        @test @expression(m, aff2 * aff3).aff.terms[pt] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, meas)
        @test @expression(m, aff2 * aff3).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, hold)
        @test @expression(m, aff2 * aff3).terms[pair] == 1
        @test @expression(m, aff3 * aff5).aff.constant == -42
        @test @expression(m, aff3 * aff5).aff.terms[meas] == 42
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, aff3 * aff5).terms[pair] == 4
        pair = UnorderedPair{GeneralVariableRef}(meas, pt)
        @test @expression(m, aff3 * aff5).terms[pair] == -4
        # test results of fininite combinations
        @test @expression(m, aff2 * aff5).aff.constant == 0
        @test @expression(m, aff2 * aff5).aff.terms[hold] == -42
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, aff2 * aff5).terms[pair] == 4
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, aff2 * aff5).terms[pair] == -4
        # test pure additions
        @test isa(@expression(m, aff4 * aff4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, aff4 * aff4).aff.constant == 9
        @test @expression(m, aff4 * aff4).aff.terms[inf] == 12
        pair = UnorderedPair{GeneralVariableRef}(inf, inf)
        @test @expression(m, aff4 * aff4).terms[pair] == 4
        @test isa(@expression(m, aff5 * aff5), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, aff5 * aff5).aff.constant == 42 * 42
        @test @expression(m, aff5 * aff5).aff.terms[pt] == 2 * 4 * -42
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, aff5 * aff5).terms[pair] == 16
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, aff1 / aff2)
        @test_throws ErrorException @expression(m, aff2 / aff4)
        @test_throws ErrorException @expression(m, aff3 / aff5)
        @test_throws ErrorException @expression(m, aff5 / aff5)
    end
end

# Test operations for GenericQuadExpr--GeneralVariableRef
@testset "QuadExpr--Variable" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    @hold_variable(m, test[1:51])
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    aff5 = 4pt - 42
    quad1 = copy(aff1) * aff2 #(inf*hold - inf*pt + pt*hold - pt^2 - 2hold + 2pt)
    quad2 = (pt * aff3) - 3   #(pt*hold - pt*meas + pt - 3)
    quad3 = hold * aff2       #(hold^2 - hold*pt)
    quad4 = pt * aff5         #(4pt^2 - 42pt)
    # test addition
    @testset "Add" begin
        # test mixed combo types
        @test isa(@expression(m, copy(quad1) + inf), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad2) + meas), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad3) + pt), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad4) + par), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(quad1) + inf).aff.constant == 0
        @test @expression(m, copy(quad1) + inf).aff.terms[pt] == 2
        @test @expression(m, copy(quad1) + inf).aff.terms[inf] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) + inf).terms[pair] == 1
        @test @expression(m, copy(quad4) + par).aff.constant == 0
        @test @expression(m, copy(quad4) + par).aff.terms[pt] == -42
        @test @expression(m, copy(quad4) + par).aff.terms[par] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) + par).terms[pair] == 4
        # test measurefinite combos
        @test @expression(m, copy(quad2) + meas).aff.constant == -3
        @test @expression(m, copy(quad2) + meas).aff.terms[pt] == 1
        @test @expression(m, copy(quad2) + meas).aff.terms[meas] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad2) + meas).terms[pair] == 1
        @test @expression(m, copy(quad2) + pt).aff.constant == -3
        @test @expression(m, copy(quad2) + pt).aff.terms[pt] == 2
        # test finite combos
        @test @expression(m, copy(quad3) + pt).aff.constant == 0
        @test @expression(m, copy(quad3) + pt).aff.terms[pt] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(quad3) + pt).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(quad4) + pt), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(quad4) + pt).aff.constant == 0
        @test @expression(m, copy(quad4) + pt).aff.terms[pt] == -41
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) + pt).terms[pair] == 4
    end
    # test subtraction
    @testset "Subtract" begin
        # test mixed combo types
        @test isa(@expression(m, copy(quad1) - inf), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad2) - meas), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad3) - pt), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad4) - par), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(quad1) - inf).aff.constant == 0
        @test @expression(m, copy(quad1) - inf).aff.terms[pt] == 2
        @test @expression(m, copy(quad1) - inf).aff.terms[inf] == -1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) - inf).terms[pair] == 1
        @test @expression(m, copy(quad4) - par).aff.constant == 0
        @test @expression(m, copy(quad4) - par).aff.terms[pt] == -42
        @test @expression(m, copy(quad4) - par).aff.terms[par] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) - par).terms[pair] == 4
        # test measurefinite combos
        @test @expression(m, copy(quad2) - meas).aff.constant == -3
        @test @expression(m, copy(quad2) - meas).aff.terms[pt] == 1
        @test @expression(m, copy(quad2) - meas).aff.terms[meas] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad2) - meas).terms[pair] == 1
        @test @expression(m, copy(quad2) - pt).aff.constant == -3
        @test @expression(m, copy(quad2) - pt).aff.terms[pt] == 0
        # test finite combos
        @test @expression(m, copy(quad3) - pt).aff.constant == 0
        @test @expression(m, copy(quad3) - pt).aff.terms[pt] == -1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(quad3) - pt).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(quad4) - pt), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(quad4) - pt).aff.constant == 0
        @test @expression(m, copy(quad4) - pt).aff.terms[pt] == -43
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) - pt).terms[pair] == 4
    end
    # test multiplication
    @testset "Multiply" begin
        # test some combos
        @test_throws ErrorException @expression(m, quad1 * inf)
        @test_throws ErrorException @expression(m, quad2 * meas)
        @test_throws ErrorException @expression(m, quad3 * par)
        @test_throws ErrorException @expression(m, quad4 * pt)
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, quad1 / inf)
        @test_throws ErrorException @expression(m, quad2 / meas)
        @test_throws ErrorException @expression(m, quad3 / par)
        @test_throws ErrorException @expression(m, quad4 / pt)
    end
end

# Test operations for GeneralVariableRef--GenericQuadExpr
@testset "Variable--QuadExpr" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    @hold_variable(m, test[1:51])
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    aff5 = 4pt - 42
    quad1 = copy(aff1) * aff2 #(inf*hold - inf*pt + pt*hold - pt^2 - 2hold + 2pt)
    quad2 = (pt * aff3) - 3   #(pt*hold - pt*meas + pt - 3)
    quad3 = hold * aff2       #(hold^2 - hold*pt)
    quad4 = pt * aff5         #(4pt^2 - 42pt)
    # test addition
    @testset "Add" begin
        # test mixed combo types
        @test isa(@expression(m, inf + copy(quad1)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, meas + copy(quad2)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, pt + copy(quad3)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par + copy(quad4)), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, inf + copy(quad1)).aff.constant == 0
        @test @expression(m, inf + copy(quad1)).aff.terms[pt] == 2
        @test @expression(m, inf + copy(quad1)).aff.terms[inf] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, inf + copy(quad1)).terms[pair] == 1
        @test @expression(m, par + copy(quad4)).aff.constant == 0
        @test @expression(m, par + copy(quad4)).aff.terms[pt] == -42
        @test @expression(m, par + copy(quad4)).aff.terms[par] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, par + copy(quad4)).terms[pair] == 4
        # test measurefinite combos
        @test @expression(m, meas + copy(quad2)).aff.constant == -3
        @test @expression(m, meas + copy(quad2)).aff.terms[pt] == 1
        @test @expression(m, meas + copy(quad2)).aff.terms[meas] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, meas + copy(quad2)).terms[pair] == 1
        @test @expression(m, pt + copy(quad2)).aff.constant == -3
        @test @expression(m, pt + copy(quad2)).aff.terms[pt] == 2
        # test finite combos
        @test @expression(m, pt + copy(quad3)).aff.constant == 0
        @test @expression(m, pt + copy(quad3)).aff.terms[pt] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, pt + copy(quad3)).terms[pair] == -1
        # test same types
        @test isa(@expression(m, pt + copy(quad4)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, pt + copy(quad4)).aff.constant == 0
        @test @expression(m, pt + copy(quad4)).aff.terms[pt] == -41
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, pt + copy(quad4)).terms[pair] == 4
    end
    # test subtraction
    @testset "Subtract" begin
        # test mixed combo types
        @test isa(@expression(m, inf - copy(quad1)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, meas - copy(quad2)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, pt - copy(quad3)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, par - copy(quad4)), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, inf - copy(quad1)).aff.constant == 0
        @test @expression(m, inf - copy(quad1)).aff.terms[pt] == -2
        @test @expression(m, inf - copy(quad1)).aff.terms[inf] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, inf - copy(quad1)).terms[pair] == -1
        @test @expression(m, par - copy(quad4)).aff.constant == 0
        @test @expression(m, par - copy(quad4)).aff.terms[pt] == 42
        @test @expression(m, par - copy(quad4)).aff.terms[par] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, par - copy(quad4)).terms[pair] == -4
        # test measurefinite combos
        @test @expression(m, meas - copy(quad2)).aff.constant == 3
        @test @expression(m, meas - copy(quad2)).aff.terms[pt] == -1
        @test @expression(m, meas - copy(quad2)).aff.terms[meas] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, meas - copy(quad2)).terms[pair] == -1
        @test @expression(m, pt - copy(quad2)).aff.constant == 3
        @test @expression(m, pt - copy(quad2)).aff.terms[pt] == 0
        # test finite combos
        @test @expression(m, pt - copy(quad3)).aff.constant == 0
        @test @expression(m, pt - copy(quad3)).aff.terms[pt] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, pt - copy(quad3)).terms[pair] == 1
        # test same types
        @test isa(@expression(m, pt - copy(quad4)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, pt - copy(quad4)).aff.constant == 0
        @test @expression(m, pt - copy(quad4)).aff.terms[pt] == 43
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, pt - copy(quad4)).terms[pair] == -4
    end
    # test multiplication
    @testset "Multiply" begin
        # test some combos
        @test_throws ErrorException @expression(m, inf * quad1)
        @test_throws ErrorException @expression(m, meas * quad2)
        @test_throws ErrorException @expression(m, par * quad3)
        @test_throws ErrorException @expression(m, pt * quad4)
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, inf / quad1)
        @test_throws ErrorException @expression(m, meas / quad2)
        @test_throws ErrorException @expression(m, par / quad3)
        @test_throws ErrorException @expression(m, pt / quad4)
    end
end

# Test operators on GenericAffExpr--GenericQuadExpr
@testset "AffExpr--QuadExpr" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    @hold_variable(m, test[1:51])
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    aff5 = 4pt - 42
    quad1 = copy(aff1) * aff2 #(inf*hold - inf*pt + pt*hold - pt^2 - 2hold + 2pt)
    quad2 = (pt * aff3) - 3   #(pt*hold - pt*meas + pt - 3)
    quad3 = hold * aff2       #(hold^2 - hold*pt)
    quad4 = pt * aff5         #(4pt^2 - 42pt)
    # test addition
    @testset "Add" begin
        # test mixed combo types
        @test isa(@expression(m, copy(aff1) + quad1), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff2) + quad2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff5) + quad3), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff3) + quad1), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(aff1) + quad1).aff.constant == -2
        @test @expression(m, copy(aff1) + quad1).aff.terms[pt] == 3
        @test @expression(m, copy(aff1) + quad1).aff.terms[inf] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(aff1) + quad1).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(aff1) + quad1).terms[pair] == -1
        @test @expression(m, copy(aff3) + quad1).aff.constant == 1
        @test @expression(m, copy(aff3) + quad1).aff.terms[meas] == -1
        @test @expression(m, copy(aff3) + quad1).aff.terms[hold] == -1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(aff3) + quad1).terms[pair] == 1
        # test measurefinite combos
        @test @expression(m, copy(aff3) + quad2).aff.constant == -2
        @test @expression(m, copy(aff3) + quad2).aff.terms[pt] == 1
        @test @expression(m, copy(aff3) + quad2).aff.terms[hold] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(aff3) + quad2).terms[pair] == 1
        @test @expression(m, copy(aff3) + quad4).aff.constant == 1
        @test @expression(m, copy(aff3) + quad4).aff.terms[pt] == -42
        @test @expression(m, copy(aff3) + quad4).aff.terms[meas] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(aff3) + quad4).terms[pair] == 4
        # test finite combos
        @test @expression(m, copy(aff2) + quad3).aff.constant == 0
        @test @expression(m, copy(aff2) + quad3).aff.terms[pt] == -1
        @test @expression(m, copy(aff2) + quad3).aff.terms[hold] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(aff2) + quad3).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(aff5) + quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff5) + quad4).aff.constant == -42
        @test @expression(m, copy(aff5) + quad4).aff.terms[pt] == -38
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(aff5) + quad4).terms[pair] == 4
    end
    # test subtraction
    @testset "Subtract" begin
        # test mixed combo types
        @test isa(@expression(m, copy(aff1) - quad1), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff2) - quad2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff5) - quad3), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(aff3) - quad1), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(aff1) - quad1).aff.constant == -2
        @test @expression(m, copy(aff1) - quad1).aff.terms[pt] == -1
        @test @expression(m, copy(aff1) - quad1).aff.terms[inf] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(aff1) - quad1).terms[pair] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(aff1) - quad1).terms[pair] == 1
        @test @expression(m, copy(aff3) - quad1).aff.constant == 1
        @test @expression(m, copy(aff3) - quad1).aff.terms[meas] == -1
        @test @expression(m, copy(aff3) - quad1).aff.terms[hold] == 3
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(aff3) - quad1).terms[pair] == -1
        # test measurefinite combos
        @test @expression(m, copy(aff3) - quad2).aff.constant == 4
        @test @expression(m, copy(aff3) - quad2).aff.terms[pt] == -1
        @test @expression(m, copy(aff3) - quad2).aff.terms[hold] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(aff3) - quad2).terms[pair] == -1
        @test @expression(m, copy(aff3) - quad4).aff.constant == 1
        @test @expression(m, copy(aff3) - quad4).aff.terms[pt] == 42
        @test @expression(m, copy(aff3) - quad4).aff.terms[meas] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(aff3) - quad4).terms[pair] == -4
        # test finite combos
        @test @expression(m, copy(aff2) - quad3).aff.constant == 0
        @test @expression(m, copy(aff2) - quad3).aff.terms[pt] == -1
        @test @expression(m, copy(aff2) - quad3).aff.terms[hold] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(aff2) - quad3).terms[pair] == 1
        # test same types
        @test isa(@expression(m, copy(aff5) - quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(aff5) - quad4).aff.constant == -42
        @test @expression(m, copy(aff5) - quad4).aff.terms[pt] == 46
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(aff5) - quad4).terms[pair] == -4
    end
    # test multiplication
    @testset "Multiply" begin
        # test some combos
        @test_throws ErrorException @expression(m, aff1 * quad1)
        @test_throws ErrorException @expression(m, aff2 * quad3)
        @test_throws ErrorException @expression(m, aff3 * quad4)
        @test_throws ErrorException @expression(m, aff5 * quad2)
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, aff1 / quad1)
        @test_throws ErrorException @expression(m, aff2 / quad3)
        @test_throws ErrorException @expression(m, aff3 / quad4)
        @test_throws ErrorException @expression(m, aff5 / quad2)
    end
end

# Test operators on GenericQuadExpr--GenericAffExpr
@testset "QuadExpr--AffExpr" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    @hold_variable(m, test[1:51])
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    aff5 = 4pt - 42
    quad1 = copy(aff1) * aff2 #(inf*hold - inf*pt + pt*hold - pt^2 - 2hold + 2pt)
    quad2 = (pt * aff3) - 3   #(pt*hold - pt*meas + pt - 3)
    quad3 = hold * aff2       #(hold^2 - hold*pt)
    quad4 = pt * aff5         #(4pt^2 - 42pt)
    # test addition
    @testset "Add" begin
        # test mixed combo types
        @test isa(@expression(m, copy(quad1) + copy(aff1)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad2) + copy(aff2) + quad2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad3) + copy(aff5)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad1) + copy(aff3)), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(quad1) + copy(aff1)).aff.constant == -2
        @test @expression(m, copy(quad1) + copy(aff1)).aff.terms[pt] == 3
        @test @expression(m, copy(quad1) + copy(aff1)).aff.terms[inf] == 1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) + copy(aff1)).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad1) + copy(aff3)).terms[pair] == -1
        @test @expression(m, copy(quad1) + copy(aff3)).aff.constant == 1
        @test @expression(m, copy(quad1) + copy(aff3)).aff.terms[meas] == -1
        @test @expression(m, copy(quad1) + copy(aff3)).aff.terms[hold] == -1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) + copy(aff3)).terms[pair] == 1
        # test measurefinite combos
        @test @expression(m, copy(quad2) + copy(aff3)).aff.constant == -2
        @test @expression(m, copy(quad2) + copy(aff3)).aff.terms[pt] == 1
        @test @expression(m, copy(quad2) + copy(aff3)).aff.terms[hold] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad2) + copy(aff3)).terms[pair] == 1
        @test @expression(m, copy(quad4) + copy(aff3)).aff.constant == 1
        @test @expression(m, copy(quad4) + copy(aff3)).aff.terms[pt] == -42
        @test @expression(m, copy(quad4) + copy(aff3)).aff.terms[meas] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) + copy(aff3)).terms[pair] == 4
        # test finite combos
        @test @expression(m, copy(quad3) + copy(aff2)).aff.constant == 0
        @test @expression(m, copy(quad3) + copy(aff2)).aff.terms[pt] == -1
        @test @expression(m, copy(quad3) + copy(aff2)).aff.terms[hold] == 1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(quad3) + copy(aff2)).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(quad4) + copy(aff5)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(quad4) + copy(aff5)).aff.constant == -42
        @test @expression(m, copy(quad4) + copy(aff5)).aff.terms[pt] == -38
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) + copy(aff5)).terms[pair] == 4
    end
    # test subtraction
    @testset "Subtract" begin
        # test mixed combo types
        @test isa(@expression(m, copy(quad1) - copy(aff1)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad2) - copy(aff2)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad3) - copy(aff5)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad1) - copy(aff3)), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(quad1) - copy(aff1)).aff.constant == 2
        @test @expression(m, copy(quad1) - copy(aff1)).aff.terms[pt] == 1
        @test @expression(m, copy(quad1) - copy(aff1)).aff.terms[inf] == -1
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) - copy(aff1)).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad1) - copy(aff1)).terms[pair] == -1
        @test @expression(m, copy(quad1) - copy(aff3)).aff.constant == -1
        @test @expression(m, copy(quad1) - copy(aff3)).aff.terms[meas] == 1
        @test @expression(m, copy(quad1) - copy(aff3)).aff.terms[hold] == -3
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) - copy(aff3)).terms[pair] == 1
        # test measurefinite combos
        @test @expression(m, copy(quad2) - copy(aff3)).aff.constant == -4
        @test @expression(m, copy(quad2) - copy(aff3)).aff.terms[pt] == 1
        @test @expression(m, copy(quad2) - copy(aff3)).aff.terms[hold] == -1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad2) - copy(aff3)).terms[pair] == 1
        @test @expression(m, copy(quad4) - copy(aff3)).aff.constant == -1
        @test @expression(m, copy(quad4) - copy(aff3)).aff.terms[pt] == -42
        @test @expression(m, copy(quad4) - copy(aff3)).aff.terms[meas] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4)- copy(aff3)).terms[pair] == 4
        # test finite combos
        @test @expression(m, copy(quad3) - copy(aff2)).aff.constant == 0
        @test @expression(m, copy(quad3) - copy(aff2)).aff.terms[pt] == 1
        @test @expression(m, copy(quad3) - copy(aff2)).aff.terms[hold] == -1
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(quad3) - copy(aff2)).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(quad4) - copy(aff5)), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(quad4) - copy(aff5)).aff.constant == 42
        @test @expression(m, copy(quad4) - copy(aff5)).aff.terms[pt] == -46
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) - copy(aff5)).terms[pair] == 4
    end
    # test multiplication
    @testset "Multiply" begin
        # test some combos
        @test_throws ErrorException @expression(m, quad1 * aff1)
        @test_throws ErrorException @expression(m, quad3 * aff2)
        @test_throws ErrorException @expression(m, quad4 * aff3)
        @test_throws ErrorException @expression(m, quad2 * aff5)
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, quad1 / aff1)
        @test_throws ErrorException @expression(m, quad3 / aff2)
        @test_throws ErrorException @expression(m, quad4 / aff3)
        @test_throws ErrorException @expression(m, quad2 / aff5)
    end
end

# Test operators on GenericQuadExpr--GenericQuadExpr
@testset "QuadExpr--QuadExpr" begin
    # initialize model and references and expressions
    m = InfiniteModel()
    @infinite_parameter(m, 0 <= par <= 1)
    @infinite_variable(m, inf(par))
    @point_variable(m, inf(0), pt)
    @hold_variable(m, hold)
    @hold_variable(m, test[1:51])
    data = TestData(par, 0, 5)
    meas = Measure(hold, data, Int[], Int[], true)
    object = MeasureData(meas, "test")
    mindex = MeasureIndex(1)
    @test InfiniteOpt._add_data_object(m, object) == mindex
    meas = InfiniteOpt._make_variable_ref(m, mindex)
    aff1 = (inf + pt) - 2
    aff2 = hold - pt
    aff3 = (hold - meas) + 1
    aff5 = 4pt - 42
    quad1 = copy(aff1) * aff2 #(inf*hold - inf*pt + pt*hold - pt^2 - 2hold + 2pt)
    quad2 = (pt * aff3) - 3   #(pt*hold - pt*meas + pt - 3)
    quad3 = hold * aff2       #(hold^2 - hold*pt)
    quad4 = pt * aff5         #(4pt^2 - 42pt)
    # test addition
    @testset "Add" begin
        # test mixed combo types
        @test isa(@expression(m, copy(quad1) + quad2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad2) + quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad3) + quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad4) + quad1), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(quad1) + quad2).aff.constant == -3
        @test @expression(m, copy(quad1) + quad2).aff.terms[pt] == 3
        @test @expression(m, copy(quad1) + quad2).aff.terms[hold] == -2
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) + quad2).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad1) + quad2).terms[pair] == 2
        @test @expression(m, copy(quad4) + quad1).aff.constant == 0
        @test @expression(m, copy(quad4) + quad1).aff.terms[pt] == -40
        @test @expression(m, copy(quad4) + quad1).aff.terms[hold] == -2
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) + quad1).terms[pair] == 3
        # test measurefinite combos
        @test @expression(m, copy(quad2) + quad4).aff.constant == -3
        @test @expression(m, copy(quad2) + quad4).aff.terms[pt] == -41
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad2) + quad4).terms[pair] == 1
        # test finite combos
        @test @expression(m, copy(quad3) + quad4).aff.constant == 0
        @test @expression(m, copy(quad3) + quad4).aff.terms[pt] == -42
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(quad3) + quad4).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(quad4) + quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(quad4) + quad4).aff.constant == 0
        @test @expression(m, copy(quad4) + quad4).aff.terms[pt] == -84
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) + quad4).terms[pair] == 8
    end
    # test subtraction
    @testset "Subtract" begin
        # test mixed combo types
        @test isa(@expression(m, copy(quad1) - quad2), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad2) - quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad3) - quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test isa(@expression(m, copy(quad4) - quad1), GenericQuadExpr{Float64, GeneralVariableRef})
        # test general combos
        @test @expression(m, copy(quad1) - quad2).aff.constant == 3
        @test @expression(m, copy(quad1) - quad2).aff.terms[pt] == 1
        @test @expression(m, copy(quad1) - quad2).aff.terms[hold] == -2
        pair = UnorderedPair{GeneralVariableRef}(inf, hold)
        @test @expression(m, copy(quad1) - quad2).terms[pair] == 1
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad1) - quad2).terms[pair] == 0
        @test @expression(m, copy(quad4) - quad1).aff.constant == 0
        @test @expression(m, copy(quad4) - quad1).aff.terms[pt] == -44
        @test @expression(m, copy(quad4) - quad1).aff.terms[hold] == 2
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) - quad1).terms[pair] == 5
        # test measurefinite combos
        @test @expression(m, copy(quad2) - quad4).aff.constant == -3
        @test @expression(m, copy(quad2) - quad4).aff.terms[pt] == 43
        pair = UnorderedPair{GeneralVariableRef}(pt, hold)
        @test @expression(m, copy(quad2) - quad4).terms[pair] == 1
        # test finite combos
        @test @expression(m, copy(quad3) - quad4).aff.constant == 0
        @test @expression(m, copy(quad3) - quad4).aff.terms[pt] == 42
        pair = UnorderedPair{GeneralVariableRef}(hold, pt)
        @test @expression(m, copy(quad3) - quad4).terms[pair] == -1
        # test same types
        @test isa(@expression(m, copy(quad4) - quad4), GenericQuadExpr{Float64, GeneralVariableRef})
        @test @expression(m, copy(quad4) - quad4).aff.constant == 0
        @test @expression(m, copy(quad4) - quad4).aff.terms[pt] == 0
        pair = UnorderedPair{GeneralVariableRef}(pt, pt)
        @test @expression(m, copy(quad4) - quad4).terms[pair] == 0
    end
    # test multiplication
    @testset "Multiply" begin
        # test some combos
        @test_throws ErrorException @expression(m, quad1 * quad2)
        @test_throws ErrorException @expression(m, quad3 * quad1)
        @test_throws ErrorException @expression(m, quad4 * quad4)
        @test_throws ErrorException @expression(m, quad2 * quad3)
    end
    # test division
    @testset "Divide" begin
        # test some combos
        @test_throws ErrorException @expression(m, quad1 / quad2)
        @test_throws ErrorException @expression(m, quad3 / quad1)
        @test_throws ErrorException @expression(m, quad4 / quad4)
        @test_throws ErrorException @expression(m, quad2 / quad3)
    end
end


text:
# Autogenerated wrapper script for LibPQ_jll for aarch64-apple-darwin
export LIBPQ_HANDLE

using OpenSSL_jll
JLLWrappers.@generate_wrapper_header("LibPQ")
JLLWrappers.@declare_library_product(LIBPQ_HANDLE, "@rpath/libpq.5.dylib")
function __init__()
    JLLWrappers.@generate_init_header(OpenSSL_jll)
    JLLWrappers.@init_library_product(
        LIBPQ_HANDLE,
        "lib/libpq.5.14.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
module VegaDatasets

using JSON, TextParse, DataValues, TableShowUtils, DataStructures,
    TableTraits, IteratorInterfaceExtensions, TableTraitsUtils, FilePaths
import IterableTables

export dataset

struct VegaDataset
    data::Union{OrderedDict{Symbol,Any},Nothing}
    path::AbstractPath
end

struct VegaJSONDataset
    data::OrderedDict
    path::AbstractPath
end

JSON.lower(x::VegaJSONDataset) = x.data

function Base.show(io::IO, source::VegaJSONDataset)
    print(io, "Vega JSON Dataset")
end

IteratorInterfaceExtensions.isiterable(x::VegaDataset) = true
TableTraits.isiterabletable(x::VegaDataset) = true

function Base.collect(vd::VegaDataset)
    return collect(getiterator(vd))
end

function IteratorInterfaceExtensions.getiterator(file::VegaDataset)
    it = TableTraitsUtils.create_tableiterator(collect(values(file.data)), collect(keys(file.data)))
    return it
end

function Base.show(io::IO, source::VegaDataset)
    if source.data !== nothing
        TableShowUtils.printtable(io, getiterator(source), "Vega dataset")
    end
end

function Base.show(io::IO, ::MIME"text/html", source::VegaDataset)
    if source.data !== nothing
        TableShowUtils.printHTMLtable(io, getiterator(source))
    end
end

Base.Multimedia.showable(::MIME"text/html", source::VegaDataset) = true

function Base.show(io::IO, ::MIME"application/vnd.dataresource+json", source::VegaDataset)
    if source.data !== nothing
        TableShowUtils.printdataresource(io, getiterator(source))
    end
end

Base.Multimedia.showable(::MIME"application/vnd.dataresource+json", source::VegaDataset) = true

function load_json(filename)
    json_data = JSON.parsefile(filename, dicttype=DataStructures.OrderedDict)

    nrows = length(json_data)

    # Iterate over all JSON elements, get keys, then take distinct keys
    colnames = unique(vcat([collect(keys(d)) for d in json_data]...))

    ncols = length(colnames)

    # Get column types
    coltypes = Type[]
    for col in colnames
        curr_T = typeof(get(json_data[1], col, DataValues.NA))
        if curr_T <: Nothing
            curr_T = typeof(DataValues.NA)
        end
        col_type = curr_T

        for row in 2:length(json_data)
            curr_T = typeof(get(json_data[row], col, DataValues.NA))
            if curr_T <: Nothing
                curr_T = typeof(DataValues.NA)
            end
            col_type = Base.promote_type(col_type, curr_T)
        end
        push!(coltypes, col_type)
    end

    coldata = [ct <: DataValue ? DataValueVector{eltype(ct)}(nrows) : Vector{ct}(undef, nrows) for ct in coltypes]

    for (i, row) in enumerate(json_data)
        for colindx in 1:ncols
            v = get(row, colnames[colindx], DataValues.NA)
            if v === nothing
                v = DataValues.NA
            end
            coldata[colindx][i] = v
        end
    end

    return VegaDataset(OrderedDict{Symbol,Any}(Symbol(i[1]) => i[2] for i in zip(colnames, coldata)), Path(normpath(filename)))
end

function load_csv(filename; delim=',')
    data, header = csvread(filename, delim)

    return VegaDataset(OrderedDict{Symbol,Any}(Symbol(i[1]) => i[2] for i in zip(header, data)), Path(normpath(filename)))
end

function dataset(name::AbstractString)
    if in(name, ["sf-temps"])
        error("This dataset is currently not supported.")
    elseif in(name, ["earthquakes", "graticule", "londonBoroughs", "londonTubeLines", "miserables", "us-10m", "world-110m"])
        json_filename = normpath(joinpath(@__DIR__, "..", "data", "data", "$name.json"))
        return VegaJSONDataset(JSON.parsefile(json_filename, dicttype=DataStructures.OrderedDict), Path(json_filename))
    elseif isfile(joinpath(@__DIR__, "..", "data", "data", name))
        if splitext(name)[2] == ".csv"
            return load_csv(joinpath(@__DIR__, "..", "data", "data", name))
        elseif splitext(name)[2] == ".tsv"
            return load_csv(joinpath(@__DIR__, "..", "data", "data", name), delim='\t')
        elseif splitext(name)[2] == ".json"
            return load_json(joinpath(@__DIR__, "..", "data", "data", name))
        else
            error("Unknown dataset.")
        end
    else
        json_filename = joinpath(@__DIR__, "..", "data", "data", "$name.json")
        csv_filename = joinpath(@__DIR__, "..", "data", "data", "$name.csv")
        tsv_filename = normpath(joinpath(@__DIR__, "..", "data", "data", "$name.tsv"))
        if isfile(json_filename)
            return load_json(json_filename)
        elseif isfile(csv_filename)
            return load_csv(csv_filename)
        elseif isfile(tsv_filename)
            return load_csv(tsv_filename, delim='\t')
        else
            error("Unknown dataset.")
        end
    end
end

end # module