text:
function update_constant!(s::MOI.LessThan{T}, val) where {T}
    return MOI.LessThan{T}(s.upper - val)
end

function update_constant!(s::MOI.GreaterThan{T}, val) where {T}
    return MOI.GreaterThan{T}(s.lower - val)
end

function update_constant!(s::MOI.EqualTo{T}, val) where {T}
    return MOI.EqualTo{T}(s.value - val)
end

# Affine
function update_parameter_in_affine_constraints!(model::Optimizer)
    for S in SUPPORTED_SETS
        affine_constraint_cache_inner =
            model.affine_constraint_cache[MOI.ScalarAffineFunction{Float64}, S]
        if !isempty(affine_constraint_cache_inner)
            update_parameter_in_affine_constraints!(
                model.optimizer,
                model.parameters,
                model.updated_parameters,
                affine_constraint_cache_inner,
            )
        end
    end
    return model
end

function update_parameter_in_affine_constraints!(
    optimizer::OT,
    parameters::Dict{MOI.VariableIndex,T},
    updated_parameters::Dict{MOI.VariableIndex,T},
    affine_constraint_cache_inner::MOI.Utilities.DoubleDicts.WithType{F,S},
) where {OT,T,F,S}
    for (ci, param_array) in affine_constraint_cache_inner
        update_parameter_in_affine_constraints!(
            optimizer,
            ci,
            param_array,
            parameters,
            updated_parameters,
        )
    end
    return optimizer
end

function update_parameter_in_affine_constraints!(
    optimizer::OT,
    ci::CI,
    param_array::Vector{MOI.ScalarAffineTerm{T}},
    parameters::Dict{MOI.VariableIndex,T},
    updated_parameters::Dict{MOI.VariableIndex,T},
) where {OT,T,CI}
    param_constant = zero(T)
    for term in param_array
        if haskey(updated_parameters, term.variable_index) # TODO This haskey can be slow
            param_constant +=
                term.coefficient * (
                    updated_parameters[term.variable_index] -
                    parameters[term.variable_index]
                )
        end
    end
    if param_constant != 0
        set = MOI.get(optimizer, MOI.ConstraintSet(), ci)
        set = update_constant!(set, param_constant)
        MOI.set(optimizer, MOI.ConstraintSet(), ci, set)
    end
    return ci
end

function update_parameters_in_affine_objective!(model::Optimizer)
    if !isempty(model.affine_objective_cache)
        objective_constant = 0
        for j in model.affine_objective_cache
            if haskey(model.updated_parameters, j.variable_index)
                param_old = model.parameters[j.variable_index]
                param_new = model.updated_parameters[j.variable_index]
                aux = param_new - param_old
                objective_constant += j.coefficient * aux
            end
        end
        if objective_constant != 0
            F = MOI.get(model.optimizer, MOI.ObjectiveFunctionType())
            f = MOI.get(model.optimizer, MOI.ObjectiveFunction{F}())
            fvar = MOI.ScalarAffineFunction(
                f.terms,
                f.constant + objective_constant,
            )
            MOI.set(model.optimizer, MOI.ObjectiveFunction{F}(), fvar)
        end
    end
    return model
end

function update_parameter_in_quadratic_constraints_pc!(model::Optimizer)
    for (ci, fparam) in model.quadratic_constraint_cache_pc
        param_constant = 0
        for j in fparam
            if haskey(model.updated_parameters, j.variable_index)
                param_old = model.parameters[j.variable_index]
                param_new = model.updated_parameters[j.variable_index]
                aux = param_new - param_old
                param_constant += j.coefficient * aux
            end
        end
        if param_constant != 0
            set = MOI.get(
                model.optimizer,
                MOI.ConstraintSet(),
                model.quadratic_added_cache[ci],
            )
            set = update_constant!(set, param_constant)
            MOI.set(
                model.optimizer,
                MOI.ConstraintSet(),
                model.quadratic_added_cache[ci],
                set,
            )
        end
    end
end

function update_parameter_in_quadratic_objective_pc!(model::Optimizer)
    if !isempty(model.quadratic_objective_cache_pc)
        objective_constant = 0
        for j in model.quadratic_objective_cache_pc
            if haskey(model.updated_parameters, j.variable_index)
                param_old = model.parameters[j.variable_index]
                param_new = model.updated_parameters[j.variable_index]
                aux = param_new - param_old
                objective_constant += j.coefficient * aux
            end
        end
        if objective_constant != 0
            F = MOI.get(model.optimizer, MOI.ObjectiveFunctionType())
            f = MOI.get(model.optimizer, MOI.ObjectiveFunction{F}())

            # TODO
            # Is there another way to verify the Type of F without expliciting {Float64}?
            # Something like isa(F, MOI.ScalarAffineFunction)
            fvar = if F == MathOptInterface.ScalarAffineFunction{Float64}
                MOI.ScalarAffineFunction(f.terms, f.constant + objective_constant)
            else
                MOI.ScalarQuadraticFunction(
                    f.affine_terms,
                    f.quadratic_terms,
                    f.constant + objective_constant,
                )
            end
            MOI.set(model.optimizer, MOI.ObjectiveFunction{F}(), fvar)
        end
    end
    return model
end

function update_parameter_in_quadratic_constraints_pp!(model::Optimizer)
    for (ci, fparam) in model.quadratic_constraint_cache_pp
        param_constant = 0
        for j in fparam
            if haskey(model.updated_parameters, j.variable_index_1) &&
               haskey(model.updated_parameters, j.variable_index_2)
                param_new_1 = model.updated_parameters[j.variable_index_1]
                param_new_2 = model.updated_parameters[j.variable_index_2]
                param_old_1 = model.parameters[j.variable_index_1]
                param_old_2 = model.parameters[j.variable_index_2]
                param_constant +=
                    j.coefficient *
                    ((param_new_1 * param_new_2) - (param_old_1 * param_old_2))
            elseif haskey(model.updated_parameters, j.variable_index_1)
                param_new_1 = model.updated_parameters[j.variable_index_1]
                param_old_1 = model.parameters[j.variable_index_1]
                param_old_2 = model.parameters[j.variable_index_2]
                param_constant +=
                    j.coefficient * param_old_2 * (param_new_1 - param_old_1)
            elseif haskey(model.updated_parameters, j.variable_index_2)
                param_new_2 = model.updated_parameters[j.variable_index_2]
                param_old_1 = model.parameters[j.variable_index_1]
                param_old_2 = model.parameters[j.variable_index_2]
                param_constant +=
                    j.coefficient * param_old_1 * (param_new_2 - param_old_2)
            end
        end
        if param_constant != 0
            set = MOI.get(
                model.optimizer,
                MOI.ConstraintSet(),
                model.quadratic_added_cache[ci],
            )
            set = update_constant!(set, param_constant)
            MOI.set(
                model.optimizer,
                MOI.ConstraintSet(),
                model.quadratic_added_cache[ci],
                set,
            )
        end
    end
end

function update_parameter_in_quadratic_objective_pp!(model::Optimizer)
    if !isempty(model.quadratic_objective_cache_pp)
        objective_constant = 0
        for j in model.quadratic_objective_cache_pp
            if haskey(model.updated_parameters, j.variable_index_1) &&
               haskey(model.updated_parameters, j.variable_index_2)
                param_new_1 = model.updated_parameters[j.variable_index_1]
                param_new_2 = model.updated_parameters[j.variable_index_2]
                param_old_1 = model.parameters[j.variable_index_1]
                param_old_2 = model.parameters[j.variable_index_2]
                objective_constant +=
                    j.coefficient *
                    ((param_new_1 * param_new_2) - (param_old_1 * param_old_2))
            elseif haskey(model.updated_parameters, j.variable_index_1)
                param_new_1 = model.updated_parameters[j.variable_index_1]
                param_old_1 = model.parameters[j.variable_index_1]
                param_old_2 = model.parameters[j.variable_index_2]
                objective_constant +=
                    j.coefficient * param_old_2 * (param_new_1 - param_old_1)
            elseif haskey(model.updated_parameters, j.variable_index_2)
                param_new_2 = model.updated_parameters[j.variable_index_2]
                param_old_1 = model.parameters[j.variable_index_1]
                param_old_2 = model.parameters[j.variable_index_2]
                objective_constant +=
                    j.coefficient * param_old_1 * (param_new_2 - param_old_2)
            end
        end
        if objective_constant != 0
            F = MOI.get(model.optimizer, MOI.ObjectiveFunctionType())
            f = MOI.get(model.optimizer, MOI.ObjectiveFunction{F}())

            # TODO
            # Is there another way to verify the Type of F without expliciting {Float64}?
            # Something like isa(F, MOI.ScalarAffineFunction)
            fvar = if F == MathOptInterface.ScalarAffineFunction{Float64}
                MOI.ScalarAffineFunction(f.terms, f.constant + objective_constant)
            else
                MOI.ScalarQuadraticFunction(
                    f.affine_terms,
                    f.quadratic_terms,
                    f.constant + objective_constant,
                )
            end
            MOI.set(model.optimizer, MOI.ObjectiveFunction{F}(), fvar)
        end
    end
end

# Vector Affine
function update_parameter_in_vector_affine_constraints!(model::Optimizer)
    for S in SUPPORTED_VECTOR_SETS
        vector_constraint_cache_inner =
            model.vector_constraint_cache[MOI.VectorAffineFunction{Float64}, S]
        if !isempty(vector_constraint_cache_inner)
            update_parameter_in_vector_affine_constraints!(
                model.optimizer,
                model.parameters,
                model.updated_parameters,
                vector_constraint_cache_inner,
            )
        end
    end
    return model
end

function update_parameter_in_vector_affine_constraints!(
    optimizer::OT,
    parameters::Dict{MOI.VariableIndex,T},
    updated_parameters::Dict{MOI.VariableIndex,T},
    vector_constraint_cache_inner::MOI.Utilities.DoubleDicts.WithType{F,S},
) where {OT,T,F,S}
    for (ci, param_array) in vector_constraint_cache_inner
        update_parameter_in_vector_affine_constraints!(
            optimizer,
            ci,
            param_array,
            parameters,
            updated_parameters,
        )
    end

    return optimizer
end

function update_parameter_in_vector_affine_constraints!(
    optimizer::OT,
    ci::CI,
    param_array::Vector{MOI.VectorAffineTerm{T}},
    parameters::Dict{MOI.VariableIndex,T},
    updated_parameters::Dict{MOI.VariableIndex,T},
) where {OT,T,CI}
    cf = MOI.get(optimizer, MOI.ConstraintFunction(), ci)

    n_dims = length(cf.constants)
    param_constants = zeros(T, n_dims)

    for term in param_array
        vi = term.scalar_term.variable_index

        if haskey(updated_parameters, vi) # TODO This haskey can be slow
            param_constants[term.output_index] =
                term.scalar_term.coefficient *
                (updated_parameters[vi] - parameters[vi])
        end
    end

    if param_constants != zeros(T, n_dims)
        MOI.modify(
            optimizer,
            ci,
            MOI.VectorConstantChange(cf.constants + param_constants),
        )
    end

    return ci
end

function update_parameters!(model::Optimizer)
    update_parameter_in_affine_constraints!(model)
    update_parameters_in_affine_objective!(model)
    update_parameter_in_quadratic_constraints_pc!(model)
    update_parameter_in_quadratic_objective_pc!(model)
    update_parameter_in_quadratic_constraints_pp!(model)
    update_parameter_in_quadratic_objective_pp!(model)
    update_parameter_in_vector_affine_constraints!(model)

    # TODO make this part better
    constraint_aux_dict = Dict{Any,Any}()

    for (ci, fparam) in model.quadratic_constraint_cache_pv
        for j in fparam
            if haskey(model.updated_parameters, j.variable_index_1)
                coef = j.coefficient
                param_new = model.updated_parameters[j.variable_index_1]
                if haskey(constraint_aux_dict, (ci, j.variable_index_2))
                    constraint_aux_dict[(ci, j.variable_index_2)] +=
                        param_new * coef
                else
                    constraint_aux_dict[(ci, j.variable_index_2)] =
                        param_new * coef
                end
            end
        end
    end

    for (ci, fparam) in
        model.quadratic_constraint_variables_associated_to_parameters_cache
        for j in fparam
            coef = j.coefficient
            if haskey(constraint_aux_dict, (ci, j.variable_index))#
                constraint_aux_dict[(ci, j.variable_index)] += coef
            else
                constraint_aux_dict[(ci, j.variable_index)] = coef
            end
        end
    end

    for ((ci, vi), value) in constraint_aux_dict
        old_ci = model.quadratic_added_cache[ci]
        MOI.modify(
            model.optimizer,
            old_ci,
            MOI.ScalarCoefficientChange(vi, value),
        )
    end

    objective_aux_dict = Dict{Any,Any}()

    if !isempty(model.quadratic_objective_cache_pv)
        for j in model.quadratic_objective_cache_pv
            if haskey(model.updated_parameters, j.variable_index_1)
                coef = j.coefficient
                param_new = model.updated_parameters[j.variable_index_1]
                if haskey(objective_aux_dict, (j.variable_index_2))
                    objective_aux_dict[(j.variable_index_2)] += param_new * coef
                else
                    objective_aux_dict[(j.variable_index_2)] = param_new * coef
                end
            end
        end
    end

    for j in model.quadratic_objective_variables_associated_to_parameters_cache
        coef = j.coefficient
        if haskey(objective_aux_dict, j.variable_index)
            objective_aux_dict[j.variable_index] += coef
        else
            objective_aux_dict[j.variable_index] = coef
        end
    end

    F_pv = MOI.get(model.optimizer, MOI.ObjectiveFunctionType())

    for (key, value) in objective_aux_dict
        MOI.modify(
            model.optimizer,
            MOI.ObjectiveFunction{F_pv}(),
            MOI.ScalarCoefficientChange(key, value),
        )
    end

    for (i, val) in model.updated_parameters
        model.parameters[i] = val
    end

    empty!(model.updated_parameters)

    return model
end


text:
"""
```
smc(m::AbstractDSGEModel, data::Matrix; verbose::Symbol, old_data::Matrix)
smc(m::AbstractDSGEModel, data::DataFrame)
smc(m::AbstractDSGEModel)
```

### Arguments:

- `m`: A model object, which stores parameter values, prior dists, bounds, and various
    other settings that will be referenced
- `data`: A matrix or dataframe containing the time series of the observables used in
    the calculation of the posterior/likelihood
- `old_data`: A matrix containing the time series of observables of previous data
    (with `data` being the new data) for the purposes of a time tempered estimation
    (that is, using the posterior draws from a previous estimation as the initial set
    of draws for an estimation with new data)

### Keyword Arguments:
- `verbose`: Desired frequency of function progress messages printed to standard out.
	- `:none`: No status updates will be reported.
	- `:low`: Status updates for SMC initialization and recursion will be included.
	- `:high`: Status updates for every iteration of SMC is output, which includes
    the mean and standard deviation of each parameter draw after each iteration,
    as well as calculated acceptance rate, ESS, and number of times resampled.

### Outputs

- `cloud`: The ParticleCloud object containing all of the information about the
    parameter values from the sample, their respective log-likelihoods, the ESS
    schedule, tempering schedule etc., which is saved in the saveroot.

### Overview

These are wrapper functions to ensure simplicity of estimation of DSGE models while
navigating the DSGE package.
"""
function smc2(m::AbstractDSGEModel, data::Matrix{Float64};
             verbose::Symbol = :low,
             old_data::Matrix{Float64} = Matrix{Float64}(undef, size(data, 1), 0),
             old_cloud::ParticleCloud  = DSGE.ParticleCloud(m, 0),
             run_test::Bool = false,
             filestring_addl::Vector{String} = Vector{String}(),
             continue_intermediate::Bool = false, intermediate_stage_start::Int = 0,
             save_intermediate::Bool = false, intermediate_stage_increment::Int = 10)

    parallel    = get_setting(m, :use_parallel_workers)
    n_parts     = get_setting(m, :n_particles)
    n_blocks    = get_setting(m, :n_smc_blocks)
    n_mh_steps  = get_setting(m, :n_mh_steps_smc)
    old_vintage = get_setting(m, :previous_data_vintage) ##

    smc_iteration = get_setting(m, :smc_iteration)

    λ    = get_setting(m, :λ)
    n_Φ  = get_setting(m, :n_Φ)

    tempering_target   = get_setting(m, :adaptive_tempering_target_smc)
    use_fixed_schedule = tempering_target == 0.0

    # Step 2 (Correction) settings
    resampling_method = get_setting(m, :resampler_smc)
    threshold_ratio   = get_setting(m, :resampling_threshold)
    threshold         = threshold_ratio * n_parts

    # Step 3 (Mutation) settings
    c      = get_setting(m, :step_size_smc)
    α      = get_setting(m, :mixture_proportion)
    target = get_setting(m, :target_accept)

    use_chand_recursion = get_setting(m, :use_chand_recursion)

    function my_likelihood(parameters::ParameterVector, data::Matrix{Float64})::Float64
        update!(m, parameters)
        likelihood(m, data; sampler = false, catch_errors = true,
                   use_chand_recursion = use_chand_recursion, verbose = verbose)
    end

    tempered_update = !isempty(old_data)

    # This step is purely for backwards compatibility purposes
    old_cloud_conv = isempty(old_cloud) ? SMC.Cloud(0,0) : SMC.Cloud(old_cloud)

    # Initialize Paths
    loadpath = ""
    if tempered_update
        if isempty(old_cloud)
            loadpath = rawpath(m, "estimate", "smc_cloud.jld2", filestring_addl)
            loadpath = replace(loadpath, r"vint=[0-9]{6}", "vint=" * old_vintage)
        end
    elseif continue_intermediate
        loadpath = rawpath(m, "estimate",
                           "smc_cloud_stage=$(intermediate_stage_start).jld2",
                           filestring_addl)
    end
    savepath = rawpath(m, "estimate", "smc_cloud.jld2", filestring_addl)
    particle_store_path = rawpath(m, "estimate", "smcsave.h5", filestring_addl)

    # Calls SMC package's generic SMC
    SMC.smc(my_likelihood, m.parameters, data;
            verbose = verbose,
            testing = m.testing,
            data_vintage = data_vintage(m),

            parallel = parallel,
            n_parts  = n_parts,
            n_blocks = n_blocks,
            n_mh_steps = n_mh_steps,

            λ = λ, n_Φ = n_Φ,

            resampling_method = resampling_method,
            threshold_ratio = threshold_ratio,

            c = c, α = α, target = target,

            use_fixed_schedule = use_fixed_schedule,
            tempering_target = tempering_target,

            old_data = old_data,
            old_cloud = old_cloud_conv,
            old_vintage = old_vintage,
            smc_iteration = smc_iteration,

            run_test = run_test,
            filestring_addl = filestring_addl,
            loadpath = loadpath,
            savepath = savepath,
            particle_store_path = particle_store_path,

            continue_intermediate = continue_intermediate,
            intermediate_stage_start = intermediate_stage_start,
            save_intermediate = save_intermediate,
            intermediate_stage_increment = intermediate_stage_increment)
end

function smc(m::AbstractDSGEModel, data::DataFrame; verbose::Symbol = :low,
             save_intermediate::Bool = false, intermediate_stage_increment::Int = 10,
             filestring_addl::Vector{String} = Vector{String}(undef, 0))
    data_mat = df_to_matrix(m, data)
    return smc(m, data_mat, verbose = verbose, save_intermediate = save_intermediate,
               filestring_addl = filestring_addl)
end

function smc(m::AbstractDSGEModel; verbose::Symbol = :low,
             save_intermediate::Bool = false, intermediate_stage_increment::Int = 10,
             filestring_addl::Vector{String} = Vector{String}(undef, 0))
    data = load_data(m)
    data_mat = df_to_matrix(m, data)
    return smc(m, data_mat, verbose=verbose, save_intermediate = save_intermediate,
               filestring_addl = filestring_addl)
end

ext: jl


text:
#=
This file is auto-generated. Do not edit.
=#
"""
    mutable struct AverageConverter <: Converter
        v_rated::Float64
        s_rated::Float64
        ext::Dict{String, Any}
        states::Vector{Symbol}
        n_states::Int64
    end

Parameters of an average converter model

# Arguments
- `v_rated::Float64`: rated voltage, validation range: (0, nothing)
- `s_rated::Float64`: rated VA, validation range: (0, nothing)
- `ext::Dict{String, Any}`
- `states::Vector{Symbol}`
- `n_states::Int64`
"""
mutable struct AverageConverter <: Converter
    "rated voltage"
    v_rated::Float64
    "rated VA"
    s_rated::Float64
    ext::Dict{String, Any}
    states::Vector{Symbol}
    n_states::Int64
end

function AverageConverter(v_rated, s_rated, ext=Dict{String, Any}(), )
    AverageConverter(v_rated, s_rated, ext, Vector{Symbol}(), 0, )
end

function AverageConverter(; v_rated, s_rated, ext=Dict{String, Any}(), )
    AverageConverter(v_rated, s_rated, ext, )
end

# Constructor for demo purposes; non-functional.
function AverageConverter(::Nothing)
    AverageConverter(;
        v_rated=0,
        s_rated=0,
        ext=Dict{String, Any}(),
    )
end

"""Get AverageConverter v_rated."""
get_v_rated(value::AverageConverter) = value.v_rated
"""Get AverageConverter s_rated."""
get_s_rated(value::AverageConverter) = value.s_rated
"""Get AverageConverter ext."""
get_ext(value::AverageConverter) = value.ext
"""Get AverageConverter states."""
get_states(value::AverageConverter) = value.states
"""Get AverageConverter n_states."""
get_n_states(value::AverageConverter) = value.n_states

"""Set AverageConverter v_rated."""
set_v_rated!(value::AverageConverter, val::Float64) = value.v_rated = val
"""Set AverageConverter s_rated."""
set_s_rated!(value::AverageConverter, val::Float64) = value.s_rated = val
"""Set AverageConverter ext."""
set_ext!(value::AverageConverter, val::Dict{String, Any}) = value.ext = val
"""Set AverageConverter states."""
set_states!(value::AverageConverter, val::Vector{Symbol}) = value.states = val
"""Set AverageConverter n_states."""
set_n_states!(value::AverageConverter, val::Int64) = value.n_states = val


text:
# TODO: Implement methods for processing space groups by notation
# This includes automatic detection of origin, unique axis, and other data


text:
import Base.show
using Base64: base64encode

struct WAVArray{T,N}
    Fs::Number
    data::AbstractArray{T,N}
    caption::AbstractString
end

WAVArray(Fs::Number, data::AbstractArray) = WAVArray(Fs, data, "")

function WAVArray(io::IO, caption::AbstractString; subrange=(:), format="double")
    data, Fs = wavread(io, subrange=subrange, format=format)
    return WAVArray(Fs, data, caption)
end

WAVArray(io::IO; subrange=(:), format="double") = WAVArray(io::IO, ""; subrange=subrange, format=format)

function WAVArray(filename::AbstractString, caption::AbstractString; subrange=(:), format="double")
    data, Fs = wavread(filename, subrange=subrange, format=format)
    return WAVArray(Fs, data, caption)
end

WAVArray(filename::AbstractString; subrange=(:), format="double") = WAVArray(filename, ""; subrange=subrange, format=format)

wavwrite(x::WAVArray, io::IO) = wavwrite(x.data, io; Fs=x.Fs)

function show(io::IO, ::MIME"text/html", x::WAVArray)
    buf = IOBuffer()
    wavwrite(x, buf)
    data = base64encode(String(take!(copy(buf))))
    markup = """<figure>
                $(ifelse(x.caption !== "", "<figcaption>$(x.caption)</figcaption>", ""))
                    <audio controls="controls" {autoplay}>
                    <source src="data:audio/wav;base64,$data" type="audio/wav" />
                    Your browser does not support the audio element.
                    </audio>
                </figure>"""
    print(io, markup)
end


text:
@testset "Filtering basic for initial functionality with $scheduling_config" for scheduling_config in
                                                                                [
    "default scheduling",
    "parallelism encouraged",
    "parallelism and batches encouraged",
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_basic_data()

        bucket = get_cluster_s3_bucket_name()

        for i = 1:2
            for path in [
                "s3://$(bucket)/iris_large.csv",
                "s3://$(bucket)/iris_large.parquet",
                "s3://$(bucket)/iris_large.arrow",
                "s3://$(bucket)/iris_large_dir.csv",
            ]
                df = read_file(path)

                # Assert that exception gets thrown for parameters that aren't supported
                @test_throws ArgumentError filter(
                    row -> row.sepal_length > 5.0,
                    df;
                    view = true,
                )

                # Filter based on one column
                sub_save_path = get_save_path(bucket, "sub", path)
                sub2_save_path = get_save_path(bucket, "sub2", path)
                if i == 1
                    sub = filter(row -> row.sepal_width == 3.3, df)
                    sub2 = filter(row -> endswith(row.species, "8"), sub)
                    write_file(sub_save_path, sub)
                    invalidate_source(sub2)
                    invalidate_sample(sub2)
                    write_file(sub2_save_path, sub2)
                else
                    sub = read_file(sub_save_path)
                    sub2 = read_file(sub2_save_path)
                end

                @show i
                @show sample(sub2)

                # Collect results
                sub_nrow = nrow(sub)
                sub2_nrow = nrow(sub2)
                @show sample(sub2)
                sepal_length_sub_sum = round(collect(reduce(+, sub[:, :sepal_length])))
                sepal_length_sub2_sum = round(collect((reduce(+, sub2[:, :sepal_length]))))
                @show sample(sub2)
                @show sample(df)
                @show collect(sub2)
                @show collect(sub2)
                @show sample(sub2)
                @show collect(sub2[:, [:species]])
                sub2_species = Set(collect(sub2[:, [:species]])[:, :species])

                # Assert
                @test sub_nrow == 36
                @test sepal_length_sub_sum == 217
                @test sub2_nrow == 4
                @test sepal_length_sub2_sum == 26
                @show sample(sub2)
                @test sub2_species == Set(["species_8", "species_18"])

                # Filter based on multiple columns using DataFrameRow syntax
                sub3_save_path = get_save_path(bucket, "sub3", path)
                if i == 1
                    sub3 = filter(
                        row -> (row.species == "versicolor" || row.species == "species_17"),
                        filter(row -> (row.sepal_length > 5.0 && row.sepal_width < 3.0), df),
                    )
                    write_file(sub3_save_path, sub3)
                else
                    sub3 = read_file(sub3_save_path)
                end

                # Collect results
                sub3_nrow = nrow(sub3)
                sub3 = sort(collect(sub3))
                sub3_row8 = collect(sub3[8, :])
                sub3_row62 = collect(sub3[62, :])

                # Assert
                @test sub3_nrow == 62
                @test sub3_row8 == [5.5, 2.4, 3.7, 1.0, "versicolor"]
                @test sub3_row62 == [6.8, 2.8, 4.8, 1.4, "versicolor"]

                # Filter based on multiple columns, using cols
                sub4_save_path = get_save_path(bucket, "sub4", path)
                if i == 1
                    sub4 = filter([:petal_length, :petal_width] => (l, w) -> l * w > 12.0, df)
                    write_file(sub4_save_path, sub4)
                else
                    sub4 = read_file(sub4_save_path)
                end

                # Collect results
                sub4_nrow = nrow(sub4)

                sub4_nrow = nrow(sub4)
                sub4_max_petal_length = collect(reduce(max, sub4[:, :petal_length]))
                sub4_valid = collect(
                    reduce(
                        &,
                        map(
                            (l, w) -> l .* w .> 12,
                            sub4[:, :petal_length],
                            sub4[:, :petal_width],
                        ),
                    ),
                )
                sub4 = sort(collect(sub4))
                sub4_row8 = collect(sub4[8, :])
                sub4_row114 = collect(sub4[114, :])

                # Assert
                @test sub4_nrow == 114
                @test sub4_max_petal_length == 6.9
                @test sub4_valid
                @test sub4_row8 == [6.2, 3.4, 5.4, 2.3, "species_15"]
                @test sub4_row114 == [7.9, 3.8, 6.4, 2.0, "virginica"]

                # Filter based on multiple columns, using AsTable
                sub5_save_path = get_save_path(bucket, "sub5", path)
                if i == 1
                    sub5 = filter(
                        AsTable(:) =>
                            iris ->
                                (iris.petal_length % 2 == 0) &&
                                    (iris.sepal_length > 6.0 || iris.petal_width < 0.5),
                        df,
                    )
                    write_file(sub5_save_path, sub5)
                else
                    sub5 = read_file(sub5_save_path)
                end

                # Collect results
                sub5_nrow = nrow(sub5)
                sub5 = sort(collect(sub5))
                sub5_row1 = collect(sub5[1, :])
                sub5_row18 = collect(sub5[18, :])

                # Assert
                @test sub5_nrow == 18
                @test sub5_row1 == [6.1, 2.8, 4.0, 1.3, "species_11"]
                @test sub5_row18 == [7.2, 3.2, 6.0, 1.8, "virginica"]
            end
        end
    end
end

@testset "Filtering stress for initial functionality with $scheduling_config" for scheduling_config in
                                                                                [
    "default scheduling",
    "parallelism encouraged",
    "parallelism and batches encouraged",
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_basic_data()

        bucket = get_cluster_s3_bucket_name()

        for i = 1:2
            for path in [
                "s3://$(bucket)/tripdata_large_csv.csv",
                "s3://$(bucket)/tripdata_large_parquet.parquet",
                "s3://$(bucket)/tripdata_large_arrow.arrow",
            ]
                df = read_file(path)
                @test nrow(df) == 61577490 * n_repeats

                sub_save_path = get_save_path(bucket, "sub", path)
                if i == 1
                    sub =
                        filter(row -> (row.passenger_count != 1 && row.trip_distance > 1.0), df)
                    write_file(sub_save_path, sub)
                else
                    sub = read_file(sub_save_path)
                end

                # Collect results
                sub_nrow = nrow(sub)
                sub_tripdistance_sum = round(collect(reduce(+, sub[:, :trip_distance])))
                sub_valid = round(collect(reduce(&, map(d -> d > 1.0, sub[:, :trip_distance]))))
                #sub_hour_sum = collect(
                #    reduce(
                #        +,
                #        map(
                #            t -> hour(DateTime(t, "yyyy-mm-dd HH:MM:SS")),
                #            tripdata[:, :pickup_datetime],
                #        ),
                #    )
                #)

                # Assert
                @test sub_nrow == 15109122
                @test sub_tripdistance_sum == 5.3284506e7
                @test sub_valid
                #@test sub_hour_sum == 835932637
            end
        end
    end
end

@testset "Groupby basic for initial functionality with $scheduling_config" for scheduling_config in
                                                                               [
    "default scheduling",
    "parallelism encouraged",
    "parallelism and batches encouraged",
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_basic_data()

        bucket = get_cluster_s3_bucket_name()

        for i = 1:2
            for path in [
                "s3://$(bucket)/iris_large.csv",
                "s3://$(bucket)/iris_large.parquet",
                "s3://$(bucket)/iris_large.arrow",
            ]
                df = read_file(path)

                # Assert that exception gets thrown for parameters that aren't supported
                @test_throws ErrorException groupby(df, :species; sort = false)

                # Groupby all columns
                gdf = groupby(df, :)
                @test length(gdf) == 882

                # Groupby first four columns
                gdf = groupby(df, Cols(Between(1, 4)))
                @test length(gdf) == 147

                # Groupby multiple columns selected using regex
                gdf = groupby(df, r".*width")
                @test length(gdf) == 97

                gdf_select_save_path = "s3://$(bucket)/test-tmp_gdf_select_$(split(path, "/")[end])"
                gdf_transform_save_path = "s3://$(bucket)/test-tmp_gdf_transform_$(split(path, "/")[end])"
                gdf_subset_save_path = "s3://$(bucket)/test-tmp_gdf_subset_$(split(path, "/")[end])"

                gdf = groupby(df, :species)

                # Assert exception gets thrown for parameters that aren't supported
                @test_throws ArgumentError select(
                    gdf,
                    :,
                    [:petal_length] => (pl) -> pl .- mean(pl);
                    ungroup = false,
                )
                @test_throws ArgumentError select(
                    gdf,
                    :,
                    [:petal_length] => (pl) -> pl .- mean(pl);
                    copycols = false,
                )
                @test_throws ArgumentError transform(
                    gdf,
                    :species => x -> "iris-" .* x;
                    ungroup = false,
                )
                @test_throws ArgumentError transform(
                    gdf,
                    :species => x -> "iris-" .* x;
                    copycols = false,
                )
                @test_throws ArgumentError subset(
                    gdf,
                    :petal_length => pl -> pl .>= mean(pl);
                    ungroup = false,
                )
                @test_throws ArgumentError subset(
                    gdf,
                    :petal_length => pl -> pl .>= mean(pl);
                    copycols = false,
                )

                # Groupby species and perform select, transform, subset
                if i == 1
                    gdf_select = select(gdf, :, [:petal_length] => (pl) -> pl .- mean(pl))
                    gdf_transform = transform(gdf, :species => x -> "iris-" .* x)
                    gdf_subset = subset(gdf, :petal_length => pl -> pl .>= mean(pl))
                    write_file(gdf_select_save_path, gdf_select)
                    write_file(gdf_transform_save_path, gdf_transform)
                    write_file(gdf_subset_save_path, gdf_subset)
                else
                    gdf_select = read_file(gdf_select_save_path)
                    gdf_transform = read_file(gdf_transform_save_path)
                    gdf_subset = read_file(gdf_subset_save_path)
                end

                # Collect results
                gdf_select_size = size(gdf_select)
                gdf_transform_size = size(gdf_transform)
                gdf_subset_nrow = nrow(gdf_subset)
                @test gdf_subset_nrow == 474
                gdf_subset_collected = sort(collect(gdf_subset))
                gdf_subset_row474 = collect(gdf_subset_collected[474, :])
                gdf_select_plf_square_add = round(
                    collect(reduce(+, map(l -> l * l, gdf_select[:, :petal_length_function]))),
                    digits = 2,
                )
                gdf_select_filter_length = nrow(
                    collect(
                        gdf_select[:, [:petal_length]][
                            map(l -> l .== 1.3, gdf_select[:, :petal_length]),
                            :,
                        ],
                    ),
                )
                gdf_transform_length =
                    length(groupby(gdf_transform, [:species, :species_function]))
                gdf_subset_collected = sort(collect(gdf_subset))
                gdf_subset_row5 = collect(gdf_subset_collected[5, :])
                gdf_subset_row333 = collect(gdf_subset_collected[333, :])
                gdf_subset_row474 = collect(gdf_subset_collected[474, :])
                # gdf_keepkeys_false_names = names(combine(gdf, nrow, keepkeys = false))
                gdf_keepkeys_true_names = Set(names(combine(gdf, nrow, keepkeys = true)))
                petal_length_mean =
                    sort(collect(combine(gdf, :petal_length => mean)), :petal_length_mean)[
                        :,
                        :petal_length_mean,
                    ]
                petal_length_mean = map(m -> round(m, digits = 2), petal_length_mean)
                temp = combine(gdf, :petal_length => mean, renamecols = false)
                temp_names = Set(names(temp))
                temp_petal_length = sort(collect(temp)[:, :petal_length])
                temp_petal_length = map(l -> round(l, digits = 2), temp_petal_length)

                # Assert
                @test gdf_select_size == (900, 6)
                @test gdf_transform_size == (900, 6)
                @test gdf_subset_nrow == 474
                @test gdf_select_plf_square_add == 163.32
                @test gdf_select_filter_length == 42
                @test gdf_transform_length == 18
                @test gdf_subset_row5 == [4.6, 3.1, 1.5, 0.2, "species_4"]
                @test gdf_subset_row333 == [6.7, 2.5, 5.8, 1.8, "species_18"]
                @test gdf_subset_row474 == [7.9, 3.8, 6.4, 2.0, "virginica"]

                # Combine
                # @test gdf_keepkeys_false_names == ["nrow"]
                @test gdf_keepkeys_true_names == Set(["nrow", "species"])
                @test petal_length_mean == [
                    1.46,
                    1.46,
                    1.46,
                    1.46,
                    1.46,
                    1.46,
                    4.26,
                    4.26,
                    4.26,
                    4.26,
                    4.26,
                    4.26,
                    5.55,
                    5.55,
                    5.55,
                    5.55,
                    5.55,
                    5.55,
                ]
                @test temp_names == Set(["petal_length", "species"])
                @test temp_petal_length == [
                    1.46,
                    1.46,
                    1.46,
                    1.46,
                    1.46,
                    1.46,
                    4.26,
                    4.26,
                    4.26,
                    4.26,
                    4.26,
                    4.26,
                    5.55,
                    5.55,
                    5.55,
                    5.55,
                    5.55,
                    5.55,
                ]
            end
        end
    end
end

@testset "Groupby stress for initial functionality with $scheduling_config" for scheduling_config in
                                                                                [
    "default scheduling",
    "parallelism encouraged",
    "parallelism and batches encouraged",
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_stress_data()

        bucket = get_cluster_s3_bucket_name()

        for i = 1:2
            for path in [
                "s3://$(bucket)/tripdata_large_csv.csv",
                "s3://$(bucket)/tripdata_large_parquet.parquet",
                "s3://$(bucket)/tripdata_large_arrow.arrow",
            ]
                df = read_file(path)
                global n_repeats
                @test nrow(df) == 61577490 * n_repeats

                gdf_subset_save_path = get_save_path(bucket, "gdf_subset", path)
                if i == 1
                    gdf = groupby(df, [:passenger_count, :vendor_id])
                    gdf_subset = subset(gdf, :trip_distance => d -> d .>= round(mean(d)))
                    @test length(gdf) == 21
                else
                    gdf_subset = read_file(gdf_collect_save_path)
                end

                gdf_nrow = sort(collect(combine(gdf, nrow)))
                gdf_subset_nrow = nrow(gdf_subset)
                gdf_tripdistance_sum = round(collect(reduce(+, gdf_subset[:, :trip_distance])))

                @test gdf_nrow == [
                    1,
                    1,
                    1,
                    2,
                    3,
                    3,
                    4,
                    25,
                    142,
                    15371,
                    601461,
                    609517,
                    1116194,
                    1258507,
                    1424146,
                    1453823,
                    4133487,
                    4412096,
                    4510571,
                    18330823,
                    23711312,
                ]
                @test gdf_subset_nrow == 16964379
                @test gdf_tripdistance_sum == 1.09448617e8
            end

        end
    end
end

# Filter from dataset to one row or empty df
@testset "Filter and groupby with $scheduling_config for simple edge cases for $filetype" for scheduling_config in [
    "default scheduling",
    "size exaggurated",
], filetype in [
    "csv",
    "parquet",
    "arrow",
    "directory"
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_empty_data()

        bucket = get_cluster_s3_bucket_name()

        path = ""
        if filetype == "directory"
            path = "s3://$(bucket)/iris_large_dir.csv"
        else
            path = "s3://$(bucket)/iris_large.$(filetype)"
        end

        for i = 1:2
            # Read empty df
            df = read_file(path)

            # Filter which results in an empty df and in a df with a single entry
            filtered_empty_save_path = get_save_path(bucket, "filtered_empty", path)
            filtered_single_save_path = get_save_path(bucket, "filtered_single", path)
            if i == 1
                filtered_empty = filter(row -> row.petal_length > 10, df)
                filtered_single = filter(row -> row.petal_length == 1.4 && row.sepal_length == 4.9 && row.species == "setosa", df)
            else
                filtered_empty = read_file(filtered_empty_save_path)
                filtered_single = read_file(filtered_single_save_path)
            end

            has_schema = i != 2 || filetype == "arrow"
            has_num_cols = i != 2 || filetype != "parquet"

            # Test sizes
            filtered_empty_size = size(filtered_empty)
            filtered_single_size = size(filtered_single)
            @test filtered_empty_size == (has_num_cols ? (0, 5) : (0, 0))
            @test filtered_single_size == (1, 5)

            # Test downloading single row
            filtered_single_sepal_width = collect(filtered_single[:, :sepal_width])
            filtered_single_petal_width = collect(filtered_single[:, :petal_width])
            @test filtered_single_sepal_width == [3.0]
            @test filtered_single_petal_width == [0.2]

            # Only empty Arrow datasets preserve the schema and can be read
            # back in and used in a groupby-subset that references a column
            # from the original schema. Even if the computation were replicated
            # so that no grouping splitting has to be done, we still have to do
            # a groupby-subset on an empty DataFrame with no schema and
            # DataFrames.jl doesn't support that.
            if has_schema
                # Groupby all columns and subset, resulting in empty df
                filtered_empty_sub = subset(groupby(filtered_empty, :species), :petal_length => pl -> pl .>= mean(pl))
                filtered_empty_sub_size = size(filtered_empty_sub)
                @test filtered_empty_sub_size == (0, 5)
            end

            # Test size after filtering single-row dataset
            filtered_single_sub = subset(groupby(filtered_single, :species), :petal_length => pl -> pl .>= mean(pl))
            filtered_single_sub_size = size(filtered_single_sub)
            @test filtered_single_sub_size == (1, 5)

            # If this is round 1, write it out so that it can be read in round
            # 2
            if i == 1
                write_file(filtered_empty_save_path, filtered_empty)
                write_file(filtered_single_save_path, filtered_single)
            end
        end
    end
end

# Complex multi-step filtering to empty dataframes or single-row dataframe
@testset "Groupby and $filter_type with $scheduling_config for complex edge cases for $filetype" for scheduling_config in [
    "default scheduling",
    # We just want to test exaggurating the size so that the scheduler tries
    # to apply batched parallelism. It's okay that we aren't testing simple
    # parallelism because the same splitting functions are being called and
    # cast functions may be called when we do default scheduling so we're still
    # sort of testing that. Basically we just want to test filtering from a
    # really large dataset to an empty or single-row result.
    "size exaggurated",
], filetype in [
    "csv",
    "parquet",
    "arrow",
    "directory"
], filter_type in [
    "filter",
    "subset"
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_basic_data()

        bucket = get_cluster_s3_bucket_name()

        path = ""
        if filetype == "directory"
            path = "s3://$(bucket)/iris_large_dir.csv"
        else
            path = "s3://$(bucket)/iris_large.$(filetype)"
        end

        # Read empty df
        df = read_file(path)

        # Filter/subset to single row
        # Filter/subset to empty
        # Call collect on single row
        # Call collect on empty
        # Filter/subset to empty
        # Call collect on empty
        if filter_type == "filter"
            filt1 = filter(row -> row.petal_length == 1.4 && row.sepal_length == 4.9 && row.species == "setosa", df)
            filt2 = filter([:petal_length, :sepal_length] => (pl, sl) -> pl * sl > 100.0, filt1)
        elseif filter_type == "subset"
            filt1 = subset(groupby(df, :species), [:petal_length, :sepal_length, :species] => (pl, sl, s) -> ((pl .< mean(pl)) .& (sl .== 4.9) .& (s .== "setosa")))
            filt2 = subset(groupby(filt1, :species), :sepal_width => sw -> sw .> 100 * mean(sw))
        end
        df1 = collect(filt1)
        df2 = collect(filt2)
        if filter_type == "filter"
            filt3 = filter(row -> row.petal_width == 100, filt2)
        elseif filter_type == "subset"
            filt3 = subset(groupby(filt1, :petal_width), :petal_width => pw -> pw .== 100)
        end
        df3 = collect(filt3)

        @test size(df1) == (1, 5)
        @test names(df1) == ["sepal_length", "sepal_width", "petal_length", "petal_width", "species"]
        @test collect(df1[1, :]) == [4.9, 3.0, 1.4, 0.2, "setosa"]

        @test size(df2) == (0, 5)
        @test names(df2) == ["sepal_length", "sepal_width", "petal_length", "petal_width", "species"]

        @test size(df3) == (0, 5)
        @test names(df3) == ["sepal_length", "sepal_width", "petal_length", "petal_width", "species"]

        # Filter/subset to single row
        # Filter/subset to empty
        # Filter/subset to empty
        # Call collect
        if filter_type == "filter"
            filt01 = filter(row -> row.sepal_length == row.sepal_width + 1.9 && row.species == "species_10", df)
            filt02 = filter(
                row -> row.petal_length == row.sepal_length * 2,
                filter(row -> row.species == "setosa", filt01)
            )
        elseif filter_type == "subset"
            filt01 = subset(groupby(df, :species), [:petal_length, :sepal_length, :species] => (pl, sl, s) -> ((pl .< mean(pl)) .& (sl .== 4.9) .& (s .== "species_10")))
            filt02 = subset(
                groupby(
                    subset(
                        groupby(filt01, :petal_width),
                        :petal_width => pw -> pw .== 100
                    ),
                    :species
                ),
                :petal_width => pw -> pw .== 100
            )
        end
        df01 = collect(filt01)
        df02 = collect(filt02)

        @test size(df01) == (1, 5)
        @test collect(df01[1, :]) == [4.9, 3.0, 1.4, 0.2, "species_10"]

        @test size(df02) == (0, 5)
        @test names(df02) == ["sepal_length", "sepal_width", "petal_length", "petal_width", "species"]

    end
end

@testset "Slow generated code" begin
    scheduling_config = "size exaggurated"
    filetype = "arrow"
    filter_type = "filter"

    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_basic_data()

        bucket = get_cluster_s3_bucket_name()

        path = ""
        if filetype == "directory"
            path = "s3://$(bucket)/iris_large_dir.csv"
        else
            path = "s3://$(bucket)/iris_large.$(filetype)"
        end

        for _ in 1:1
            # Read empty df
            df = read_file(path)

            if filter_type == "filter"
                filt1 = filter(row -> row.petal_length == 1.4 && row.sepal_length == 4.9 && row.species == "setosa", df)
                filt2 = filter([:petal_length, :sepal_length] => (pl, sl) -> pl * sl > 100.0, filt1)
            elseif filter_type == "subset"
                filt1 = subset(groupby(df, :species), [:petal_length, :sepal_length, :species] => (pl, sl, s) -> ((pl .< mean(pl)) .& (sl .== 4.9) .& (s .== "setosa")))
                filt2 = subset(groupby(filt1, :species), :sepal_width => sw -> sw .> 100 * mean(sw))
            end

            df1 = collect(filt1)
        end
    end
end

# Reading an empty dataset
@testset "Filter and groupby with $scheduling_config for empty $filetype with $headertype" for scheduling_config in [
    "default scheduling",
    # It doesn't make sense to force parallelism or batching for empty data
    # because for the case of an empty dataset, we will actually just use
    # replication because the data size is zero.
    # "parallelism encouraged",
    # "parallelism and batches encouraged",
], (filetype, headertype) in [
    ("csv", "header"),
    ("csv", "no header"),
    ("arrow", "header"),
    ("arrow", "no header"),
    ("directory", "no header"),
]
    use_job_for_testing(scheduling_config_name = scheduling_config) do
        use_empty_data()

        bucket = get_cluster_s3_bucket_name()

        path = ""
        if filetype == "directory"
            # Create empty directory
            path = "s3://$(bucket)/empty_dir.parquet"
            if !ispath(S3Path(path * "/", config = Banyan.get_aws_config()))
                mkpath(S3Path(path * "/", config = Banyan.get_aws_config()))
            end
            headertype = "no header"
        else
            if headertype == "header"
                path = "s3://$(bucket)/empty_df2.$(filetype)"
            elseif headertype == "no header"
                path = "s3://$(bucket)/empty_df.$(filetype)"
            end
        end

        for i = 1:2
            # Read empty df
            df = read_file(path)

            if headertype == "header"
                @test size(df) == (0, 2)
            elseif headertype == "no header"
                @test size(df) == (0, 0)
            end

            # Filter empty df, which should result in empty df
            filtered_save_path = get_save_path(bucket, "filtered", path)
            if i == 1
                if headertype == "header"
                    filtered = filter([:x, :y] => (x, y) -> x == y, df)
                elseif headertype == "no header"
                    filtered = filter(row -> row.x == 0, df)
                end
                write_file(filtered_save_path, filtered)
            else
                filtered = read_file(filtered_save_path)
            end

            # Groupby all columns and aggregrate to count number of rows
            filtered_size = size(filtered)
            filtered_grouped = groupby(filtered, All())
            filtered_grouped_nrow = size(combine(filtered_grouped, nrow))
            filtered_grouped_length = length(groupby(df, All()))

            if headertype == "header"
                @test filtered_size == (0, 2)
                @test filtered_grouped_nrow == (0, 3)
                @test filtered_grouped_length == 0
            elseif headertype == "no header"
                @test filtered_size == (0, 0)
                @test filtered_grouped_nrow == (0, 1)
                @test filtered_grouped_length == 0
            end
        end
    end
end

@testset "NYC Taxi Stress Test" begin
    using Statistics
    use_job_for_testing(scheduling_config_name = "default scheduling", sample_rate=1024) do
        s3_bucket_name = get_cluster_s3_bucket_name()
        df = read_csv(
            "s3://$s3_bucket_name/nyc_tripdata.csv",
            sample_invalid=true,
            source_invalid=true,
            shuffled=true
        )
        # @show sample(df)
        println("Finished reading df")
        @debug Banyan.format_available_memory()

        # Filter all trips with distance longer than 1.0. Group by passenger count
        # and get the average trip distance for each group.
        long_trips = filter(
            row -> row.trip_distance > 1.0,
            df
        )
        println("Finished filtering to long_trips")
        @debug Banyan.format_available_memory()
        # @show sample(long_trips)

        gdf = groupby(long_trips, :passenger_count)
        println("Finished groupby by passenger count to gdf")
        @debug Banyan.format_available_memory()
        trip_means = combine(gdf, :trip_distance => mean)
        println("Finished combining by mean to trip_means")
        @debug Banyan.format_available_memory()

        trip_means = collect(trip_means)
        println("Finished collecting to trip_means")
        @debug Banyan.format_available_memory()
    end
end

max_stars_repo_name: banyan-team/banyan-julia

max_issues_repo_name: banyan-team/banyan-julia

max_forks_repo_name: banyan-team/banyan-julia


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

"""
Contains all complex data used for testing and benchmarking.
"""
module TestData

using OpticSim
using OpticSim: tobeziersegments # try to use only exported functions so this list should stay short
using OpticSim.Geometry
using OpticSim.Emitters
using OpticSim.GlassCat

using StaticArrays
using DataFrames: DataFrame
using Unitful
using Images
using Base: @.
using LinearAlgebra

#define glasses locally rather than relying on GlassCat. These tests used to rely on downloaded glasses which made the tests brittle.
#If a glass didn't download the test would fail.

const Examples_N_BK7 = GlassCat.Glass(GlassCat.GlassID(GlassCat.AGF, 885), 2, 1.03961212, 0.00600069867, 0.231792344, 0.0200179144, 1.01046945, 103.560653, 0.0, 0.0, NaN, NaN, 0.3, 2.5, 1.86e-6, 1.31e-8, -1.37e-11, 4.34e-7, 6.27e-10, 0.17, 20.0, -0.0009, 2.3, 1.0, 7.1, 1.0, 1, 1.0, [(0.3, 0.05, 25.0), (0.31, 0.25, 25.0), (0.32, 0.52, 25.0), (0.334, 0.78, 25.0), (0.35, 0.92, 25.0), (0.365, 0.971, 25.0), (0.37, 0.977, 25.0), (0.38, 0.983, 25.0), (0.39, 0.989, 25.0), (0.4, 0.992, 25.0), (0.405, 0.993, 25.0), (0.42, 0.993, 25.0), (0.436, 0.992, 25.0), (0.46, 0.993, 25.0), (0.5, 0.994, 25.0), (0.546, 0.996, 25.0), (0.58, 0.995, 25.0), (0.62, 0.994, 25.0), (0.66, 0.994, 25.0), (0.7, 0.996, 25.0), (1.06, 0.997, 25.0), (1.53, 0.98, 25.0), (1.97, 0.84, 25.0), (2.325, 0.56, 25.0), (2.5, 0.36, 25.0)], 1.5168, 2.3, 0.0, 0, 64.17, 0, 2.51, 0.0)

const Examples_N_SK16 =GlassCat.Glass(GlassCat.GlassID(GlassCat.AGF, 828), 2, 1.34317774, 0.00704687339, 0.241144399, 0.0229005, 0.994317969, 92.7508526, 0.0, 0.0, NaN, NaN, 0.31, 2.5, -2.37e-8, 1.32e-8, -1.29e-11, 4.09e-7, 5.17e-10, 0.17, 20.0, -0.0011, 3.2, 1.4, 6.3, 4.0, 1, 53.3, [(0.31, 0.02, 25.0), (0.32, 0.11, 25.0), (0.334, 0.4, 25.0), (0.35, 0.7, 25.0), (0.365, 0.86, 25.0), (0.37, 0.89, 25.0), (0.38, 0.93, 25.0), (0.39, 0.956, 25.0), (0.4, 0.97, 25.0), (0.405, 0.974, 25.0), (0.42, 0.979, 25.0), (0.436, 0.981, 25.0), (0.46, 0.984, 25.0), (0.5, 0.991, 25.0), (0.546, 0.994, 25.0), (0.58, 0.994, 25.0), (0.62, 0.993, 25.0), (0.66, 0.994, 25.0), (0.7, 0.996, 25.0), (1.06, 0.995, 25.0), (1.53, 0.973, 25.0), (1.97, 0.88, 25.0), (2.325, 0.54, 25.0), (2.5, 0.26, 25.0)], 1.62041, 3.3, 4.0, 0, 60.32, 0, 3.58, 0.0)

const Examples_N_SF2 = GlassCat.Glass(GlassCat.GlassID(GlassCat.AGF, 815), 2, 1.47343127, 0.0109019098, 0.163681849, 0.0585683687, 1.36920899, 127.404933, 0.0, 0.0, NaN, NaN, 0.365, 2.5, 3.1e-6, 1.75e-8, 6.62e-11, 7.51e-7, 8.99e-10, 0.277, 20.0, 0.0081, 1.0, 1.4, 6.68, 1.0, 1, 1.0, [(0.365, 0.007, 25.0), (0.37, 0.06, 25.0), (0.38, 0.4, 25.0), (0.39, 0.68, 25.0), (0.4, 0.83, 25.0), (0.405, 0.865, 25.0), (0.42, 0.926, 25.0), (0.436, 0.949, 25.0), (0.46, 0.961, 25.0), (0.5, 0.975, 25.0), (0.546, 0.986, 25.0), (0.58, 0.987, 25.0), (0.62, 0.984, 25.0), (0.66, 0.984, 25.0), (0.7, 0.987, 25.0), (1.06, 0.997, 25.0), (1.53, 0.984, 25.0), (1.97, 0.93, 25.0), (2.325, 0.76, 25.0), (2.5, 0.67, 25.0)], 1.64769, 1.2, 0.0, 0, 33.82, 0, 2.718, 0.0)

const Examples_N_SF14 = GlassCat.Glass(GlassCat.GlassID(GlassCat.AGF, 896), 2, 1.69022361, 0.0130512113, 0.288870052, 0.061369188, 1.7045187, 149.517689, 0.0, 0.0, NaN, NaN, 0.365, 2.5, -5.56e-6, 7.09e-9, -1.09e-11, 9.85e-7, 1.39e-9, 0.287, 20.0, 0.013, 1.0, 2.2, 9.41, 1.0, 1, 1.0, [(0.365, 0.004, 25.0), (0.37, 0.04, 25.0), (0.38, 0.33, 25.0), (0.39, 0.61, 25.0), (0.4, 0.75, 25.0), (0.405, 0.79, 25.0), (0.42, 0.87, 25.0), (0.436, 0.91, 25.0), (0.46, 0.938, 25.0), (0.5, 0.964, 25.0), (0.546, 0.983, 25.0), (0.58, 0.987, 25.0), (0.62, 0.987, 25.0), (0.66, 0.987, 25.0), (0.7, 0.985, 25.0), (1.06, 0.998, 25.0), (1.53, 0.98, 25.0), (1.97, 0.88, 25.0), (2.325, 0.64, 25.0), (2.5, 0.57, 25.0)], 1.76182, 1.0, 0.0, 0, 26.53, 0, 3.118, 0.0)

include("curves.jl")
include("surfaces.jl")
include("lenses.jl")
include("systems.jl")
include("other.jl")

end # module TestData
export TestData


text:
module TypesTest

using Test
using Dates
using QDates

@testset "Module" begin

@test_skip QDates._qref(2458487) == Int32[2458487, 2018, 322, 11, 28, 1, 0]
@test_skip QDates._qref(0x0000000000258377) == Int32[2458487, 2018, 322, 11, 28, 1, 0]

end

@testset "daysinmonth" begin

@test QDates.daysinmonth(2001,1) == 30
@test QDates.daysinmonth(2001,2) == 30
@test QDates.daysinmonth(2001,3) == 30
@test QDates.daysinmonth(2001,4) == 29
@test QDates.daysinmonth(2001,4,true) == 29
@test QDates.daysinmonth(2001,5) == 30
@test QDates.daysinmonth(2001,6) == 29
@test QDates.daysinmonth(2001,7) == 29
@test QDates.daysinmonth(2001,8) == 30
@test QDates.daysinmonth(2001,9) == 29
@test QDates.daysinmonth(2001,10) == 30
@test QDates.daysinmonth(2001,11) == 29
@test QDates.daysinmonth(2001,12) == 30
@test QDates.daysinmonth(2002,1) == 30
@test QDates.daysinmonth(2002,1,true) == 0

end

# Create "test" check manually
test = QDates.QDate(Dates.UTD(734909))
# Test Date construction by parts
@testset "QDate construction by parts" begin

@test QDates.QDate(2013) == test
@test QDates.QDate(2013,1) == test
@test QDates.QDate(2013,1,1) == test

end

# Test various input types for Date/DateTime
@testset "various input types for QDate" begin

test = QDates.QDate(2017,1,1)
@test test == QDates.QDate(test) === test
@test QDates.QDate(2017,Int8(1),Int8(1)) == test
@test QDates.QDate(2017,UInt8(1),UInt8(1)) == test
@test QDates.QDate(Int16(2017),Int16(1),Int16(1)) == test
@test QDates.QDate(UInt16(2017),UInt8(1),UInt8(1)) == test
@test QDates.QDate(Int32(2017),Int32(1),Int32(1)) == test
@test QDates.QDate(UInt32(2017),UInt32(1),UInt32(1)) == test
@test QDates.QDate(Int64(2017),Int64(1),Int64(1)) == test
@test QDates.QDate('\u07e1','\x01','\x01') == test
@test QDates.QDate(2017,true,true) == test
@test_throws ArgumentError QDates.QDate(2017,true,false)
@test QDates.QDate(UInt64(2017),UInt64(1),UInt64(1)) == test
@test QDates.QDate(Int128(2017),Int128(1),Int128(1)) == test
# @test_throws InexactError QDates.QDate(170141183460469231731687303715884105727,Int128(1),Int128(1))
@test QDates.QDate(UInt128(2017),UInt128(1),UInt128(1)) == test
@test QDates.QDate(big(2017),big(1),big(1)) == test
# Potentially won't work if can't losslessly convert to Int64
@test QDates.QDate(BigFloat(2017),BigFloat(1),BigFloat(1)) == test
@test QDates.QDate(complex(2017),complex(1),complex(1)) == test
@test QDates.QDate(Float64(2017),Float64(1),Float64(1)) == test
@test QDates.QDate(Float32(2017),Float32(1),Float32(1)) == test
@test QDates.QDate(Float16(2017),Float16(1),Float16(1)) == test
@test QDates.QDate(Rational(2017),Rational(1),Rational(1)) == test

test5l = QDates.QDate(2017,5,true,1)
@test test5l == QDates.QDate(test5l) === test5l
@test QDates.QDate(2017,Int8(5),true,Int8(1)) == test5l
@test QDates.QDate(2017,UInt8(5),true,UInt8(1)) == test5l
@test QDates.QDate(Int16(2017),Int16(5),true,Int16(1)) == test5l
@test QDates.QDate(UInt16(2017),UInt8(5),true,UInt8(1)) == test5l
@test QDates.QDate(Int32(2017),Int32(5),true,Int32(1)) == test5l
@test QDates.QDate(UInt32(2017),UInt32(5),true,UInt32(1)) == test5l
@test QDates.QDate(Int64(2017),Int64(5),true,Int64(1)) == test5l
@test QDates.QDate('\u07e1','\x05',true,'\x01') == test5l
@test QDates.QDate(UInt64(2017),UInt64(5),true,UInt64(1)) == test5l
@test QDates.QDate(Int128(2017),Int128(5),true,Int128(1)) == test5l
@test QDates.QDate(UInt128(2017),UInt128(5),true,UInt128(1)) == test5l
@test QDates.QDate(big(2017),big(5),true,big(1)) == test5l
# Potentially won't work if can't losslessly convert to Int64
@test QDates.QDate(BigFloat(2017),BigFloat(5),true,BigFloat(1)) == test5l
@test QDates.QDate(complex(2017),complex(5),true,complex(1)) == test5l
@test QDates.QDate(Float64(2017),Float64(5),true,Float64(1)) == test5l
@test QDates.QDate(Float32(2017),Float32(5),true,Float32(1)) == test5l
@test QDates.QDate(Float16(2017),Float16(5),true,Float16(1)) == test5l
@test QDates.QDate(Rational(2017),Rational(5),true,Rational(1)) == test5l

@test_throws InexactError QDates.QDate(BigFloat(1.2),BigFloat(1),BigFloat(1))
@test_throws InexactError QDates.QDate(1 + im,complex(1),complex(1))
@test_throws InexactError QDates.QDate(1.2,1.0,1.0)
@test_throws InexactError QDates.QDate(1.2f0,1.f0,1.f0)
@test_throws InexactError QDates.QDate(3//4,Rational(1),Rational(1)) == test
@test_throws InexactError QDates.QDate(2017.1,5.0,true,1.0)

# Value must be in range
@test_throws ArgumentError QDates.QDate(Dates.UTD(QDates.FIRST_VALUE - 1))
@test_throws ArgumentError QDates.QDate(Dates.UTD(QDates.LAST_VALUE + 1))
@test_throws ArgumentError QDates.QDate(170141183460469231731687303715884105727,Int128(1),Int128(1))
# Months and days must be in range
@test_throws ArgumentError QDates.QDate(444,1,1)
# @test_throws ArgumentError QDates.QDate(2100,12,2)
# @test_throws ArgumentError QDates.QDate(2101,1,1)
@test_throws ArgumentError QDates.QDate(2201,1,1)
@test_throws ArgumentError QDates.QDate(2013,0,1)
@test_throws ArgumentError QDates.QDate(2013,13,1)
@test_throws ArgumentError QDates.QDate(2013,1,0)
@test_throws ArgumentError QDates.QDate(2013,1,32)
@test_throws ArgumentError QDates.QDate(2017,6,true,1)

end

# Test DateTime traits
@testset "DateTime traits" begin

a = Dates.Date(2000, 2, 5)
b = QDates.QDate(2000, 1, 1)
# @test Dates.calendar(a) == Dates.ISOCalendar
# @test Dates.calendar(b) == ???
@test eps(b) == Dates.Day(1)
@test string(typemax(QDates.QDate)) == "旧2200年12月29日"
@test string(typemin(QDates.QDate)) == "旧0445年01月01日"
@test typemax(QDates.QDate) - typemin(QDates.QDate) == Dates.Day(641374)

end

# Date-QDate conversion
@testset "Date-QDate conversion/promotion" begin

a = Dates.Date(2000, 2, 5)
b = QDates.QDate(2000, 1, 1)
@test QDates.QDate(a) == b
@test Dates.Date(b) == a
@test a == b  # promote

c = QDates.QDate(2001)
@test b < c

y = Dates.Year(2017)
m = Dates.Month(5)
d = Dates.Day(1)
l = true  # Leap-Month
@test QDates.QDate(y) == QDates.QDate(2017)
@test QDates.QDate(y,m) == QDates.QDate(2017,5)
@test QDates.QDate(y,m,d) == QDates.QDate(2017,5,1)
@test QDates.QDate(y,m,l,d) == QDates.QDate(2017,5,true,1)
@test QDates.QDate(d,y) == QDates.QDate(2017,1,1)
@test QDates.QDate(m,y) == QDates.QDate(2017,5,1)
@test QDates.QDate(m,l,y) == QDates.QDate(2017,5,true,1)
@test_throws ArgumentError QDates.QDate(m)
@test_throws ArgumentError QDates.QDate(d,m)
@test_throws ArgumentError QDates.QDate(d,m,l)
@test_throws ArgumentError QDates.QDate(y,l)

@test isfinite(QDates.QDate)

end

end


text:
# Boundary Padded Arrays
abstract type AbstractBoundaryPaddedArray{T, N} <: AbstractArray{T, N} end
abstract type AbstractDirectionalBoundaryPaddedArray{T, N, D} <: AbstractBoundaryPaddedArray{T, N} end
abstract type AbstractComposedBoundaryPaddedArray{T, N} <: AbstractBoundaryPaddedArray{T,N} end
"""
A vector type that extends a vector u with ghost points at either end
"""
struct BoundaryPaddedVector{T,T2 <: AbstractVector{T}} <: AbstractBoundaryPaddedArray{T, 1}
    l::T
    r::T
    u::T2
end

Base.length(Q::BoundaryPaddedVector) = length(Q.u) + 2
Base.size(Q::BoundaryPaddedVector) = (length(Q.u) + 2,)
Base.lastindex(Q::BoundaryPaddedVector) = Base.length(Q)

function Base.getindex(Q::BoundaryPaddedVector,i)
    if i == 1
        return Q.l
    elseif i == length(Q)
        return Q.r
    else
        return Q.u[i-1]
    end
end

"""
Higher dimensional generalization of BoundaryPaddedVector, pads an array of dimension N along the dimension D with 2 Arrays of dimension N-1, stored in lower and upper

"""
struct BoundaryPaddedArray{T, D, N, M, V<:AbstractArray{T, N}, B<: AbstractArray{T, M}} <: AbstractDirectionalBoundaryPaddedArray{T,N, D}
    lower::B #an array of dimension M = N-1, used to extend the lower index boundary
    upper::B #Ditto for the upper index boundary
    u::V
end

getaxis(Q::BoundaryPaddedArray{T,D,N,M,V,B}) where {T,D,N,M,V,B} = D

function Base.size(Q::BoundaryPaddedArray)
    S = [size(Q.u)...]
    S[getaxis(Q)] += 2
    return Tuple(S)
end

"""
A = compose(padded_arrays::BoundaryPaddedArray...)

-------------------------------------------------------------------------------------

Example:
A = compose(Ax, Ay, Az) # 3D domain
A = compose(Ax, Ay) # 2D Domain

Composes BoundaryPaddedArrays that extend the same u for each different dimension that u has in to a ComposedBoundaryPaddedArray

Ax Ay and Az can be passed in any order, as long as there is exactly one BoundaryPaddedArray that extends each dimension.
"""
function compose(padded_arrays::BoundaryPaddedArray...)
    N = ndims(padded_arrays[1])
    Ds = getaxis.(padded_arrays)
    (length(padded_arrays) == N) || throw(ArgumentError("The padded_arrays must cover every dimension - make sure that the number of padded_arrays is equal to ndims(u)."))
    for D in Ds
        length(setdiff(Ds, D)) < N || throw(ArgumentError("There are multiple Arrays that extend along dimension $D - make sure every dimension has a unique extension"))
    end
    any(fill(padded_arrays[1].u, (length(padded_arrays),)) .== getfield.(padded_arrays, :u)) || throw(ArgumentError("The padded_arrays do not all extend the same u!"))
    padded_arrays = padded_arrays[sortperm([Ds...])]
    lower = [padded_array.lower for padded_array in padded_arrays]
    upper = [padded_array.upper for padded_array in padded_arrays]

    ComposedBoundaryPaddedArray{gettype(padded_arrays[1]),N,N-1,typeof(padded_arrays[1].u),typeof(lower[1])}(lower, upper, padded_arrays[1].u)
end

# Composed BoundaryPaddedArray

struct ComposedBoundaryPaddedArray{T, N, M, V<:AbstractArray{T, N}, B<: AbstractArray{T, M}} <: AbstractComposedBoundaryPaddedArray{T, N}
    lower::Vector{B}
    upper::Vector{B}
    u::V
end

# Aliases
AbstractBoundaryPaddedMatrix{T} = AbstractBoundaryPaddedArray{T,2}
AbstractBoundaryPadded3Tensor{T} = AbstractBoundaryPaddedArray{T,3}

BoundaryPaddedMatrix{T, D, V, B} = BoundaryPaddedArray{T, D, 2, 1, V, B}
BoundaryPadded3Tensor{T, D, V, B} = BoundaryPaddedArray{T, D, 3, 2, V, B}

ComposedBoundaryPaddedMatrix{T,V,B} = ComposedBoundaryPaddedArray{T,2,1,V,B}
ComposedBoundaryPadded3Tensor{T,V,B} = ComposedBoundaryPaddedArray{T,3,2,V,B}

Base.size(Q::ComposedBoundaryPaddedArray) = size(Q.u).+2

"""
Ax, Ay,... = decompose(A::ComposedBoundaryPaddedArray)

-------------------------------------------------------------------------------------

Decomposes a ComposedBoundaryPaddedArray in to components that extend along each dimension individually
"""
decompose(A::ComposedBoundaryPaddedArray) = Tuple([BoundaryPaddedArray{gettype(A), i, ndims(A), ndims(A)-1, typeof(lower[1])}(A.lower[i], A.upper[i], A.u) for i in 1:ndims(A)])

Base.length(Q::AbstractBoundaryPaddedArray) = reduce((*), size(Q))
Base.firstindex(Q::AbstractBoundaryPaddedArray, d::Int) = 1
Base.lastindex(Q::AbstractBoundaryPaddedArray) = length(Q)
Base.lastindex(Q::AbstractBoundaryPaddedArray, d::Int) = size(Q)[d]
gettype(Q::AbstractBoundaryPaddedArray{T,N}) where {T,N} = T
Base.ndims(Q::AbstractBoundaryPaddedArray{T,N}) where {T,N} = N

function Base.getindex(Q::BoundaryPaddedArray{T,D,N,M,V,B}, _inds::Vararg{Int,N}) where {T,D,N,M,V,B} #supports range and colon indexing!
    inds = [_inds...]
    S = size(Q)
    dim = D
    otherinds = inds[setdiff(1:N, dim)]
    @assert length(S) == N
    if inds[dim] == 1
        return Q.lower[otherinds...]
    elseif inds[dim] == S[dim]
        return Q.upper[otherinds...]
    elseif typeof(inds[dim]) <: Integer
        inds[dim] = inds[dim] - 1
        return Q.u[inds...]
    elseif typeof(inds[dim]) == Colon
        if mapreduce(x -> typeof(x) != Colon, (|), otherinds)
            return vcat(Q.lower[otherinds...],  Q.u[inds...], Q.upper[otherinds...])
        else
            throw("A colon on the extended dim is as yet incompatible with additional colons")
        end
    elseif typeof(inds[dim]) <: AbstractArray
        throw("Range indexing not yet supported!")
    end
end

function Base.getindex(Q::ComposedBoundaryPaddedArray{T, N, M, V, B} , inds::Vararg{Int, N}) where {T, N, M, V, B} #as yet no support for range indexing or colon indexing
    S = size(Q)
    @assert reduce((&), inds .<= S)
    for (dim, index) in enumerate(inds)
        if index == 1
            _inds = inds[setdiff(1:N, dim)]
            if (1 ∈ _inds) | any(S[setdiff(1:N, dim)] .== _inds)
                return zero(T)
            else
                return Q.lower[dim][(_inds.-1)...]
            end
        elseif index == S[dim]
            _inds = inds[setdiff(1:N, dim)]
            if (1 ∈ _inds) | any(S[setdiff(1:N, dim)] .== _inds)
                return zero(T)
            else
                return Q.upper[dim][(_inds.-1)...]
            end
        end
     end
    return Q.u[(inds.-1)...]
end


text:
__precompile__(false)

module Brainstorm

using Reexport: @reexport

include("lint.jl")

include("Meta/meta.jl")
include("DataStructure/datastructure.jl")
include("Algorithm/algorithm.jl")
include("Math/math.jl")
include("Finance/finance.jl")

include("../test/test.jl")

end # module


text:
function testDescriptorSetLayout(device)
    descCreateInfo = lava.DescriptorSetLayoutCreateInfo()
    lava.addUniformBuffer(descCreateInfo, vk.VK_SHADER_STAGE_VERTEX_BIT);
    lava.addCombinedImageSampler(descCreateInfo, vk.VK_SHADER_STAGE_FRAGMENT_BIT);
    descLayout = lava.createDescriptorSetLayout(device, descCreateInfo)
    return true
end

function testLoadShader(device, path, shaderRef::Ref)
    shaderRef[] = device.createShaderFromFile(path)
    return true
end

function testShaderStages(device, stagesRef::Ref)
    shaderFolder = String(@__DIR__) * "/../../HelloCube/shaders/"
    vertShaderRef = Ref{lava.ShaderModule}()
    @test testLoadShader(device, shaderFolder * "cube_vert.spv", vertShaderRef)
    fragShaderRef = Ref{lava.ShaderModule}()
    @test testLoadShader(device, shaderFolder * "cube_frag.spv", fragShaderRef)

    stageVert = lava.defaults(lava.PipelineShaderStageCreateInfo, _module = vertShaderRef[])
    stages = [stageVert.handleRef()[]]
    lava.addStage(stages, fragShaderRef[])
    @test length(stages) == 2
    stagesRef[] = stages
    return true
end


text:
@discord_object struct Guild
    id::Snowflake
    name::String
    icon::String
    splash::String
    discovery_splash::String
    owner::Bool
    owner_id::Snowflake
    permissions::Int64
    region::String
    afk_channel_id::Snowflake
    afk_timeout::Int
    embed_enabled::Bool
    embed_channel_id::Snowflake
    verification_level::VerificationLevel.VerificationLevelEnum
    default_message_notifications::MessageNotificationsLevel.MessageNotificationsLevelEnum
    explicit_content_filter::ExplicitContentFilter.ExplicitContentFilterEnum
    roles::Vector{Role}
    emojis::Vector{Emoji}
    features::Vector{String}
    mfa_level::MFALevel.MFALevelEnum
    application_id::Snowflake
    widget_enabled::Bool
    widget_channel_id::Snowflake
    system_channel_id::Snowflake
    system_channel_flags::Int
    rules_channel_id::Snowflake
    joined_at::Union{String, DateTime}
    large::Bool
    unavailable::Bool
    member_count::Int
    voice_states::Vector{VoiceState}
    members::Vector{GuildMember}
    channels::Vector{DiscordChannel}
    presences::Vector{Presence}
    max_presences::Int
    max_members::Int
    vanity_url_code::String
    description::String
    banner::String
    premium_tier::PremiumTier.PremiumTierEnum
    premium_subscription_count::Int
    preferred_locale::String
    public_updates_channel_id::Snowflake
    max_video_channel_users::Int
    approximate_member_count::Int
    approximate_presence_count::Int
end


text:
solve(i::BudgetedMSetInstance, ::DynamicProgramming; kwargs...) = budgeted_msets_dp(i; kwargs...)

function budgeted_msets_dp(i::BudgetedMSetInstance)
  # Consider items above δ.
  # Recurrence relation:
  #  V[m, t, δ] = max { V[m, t, δ + 1],  v_δ + V[m - 1, t - w_δ, δ + 1] }
  #                     \_ no item δ _/ \________ take item δ ________/
  V = Array{Float64, 3}(undef, m(i), dimension(i) + 1, budget(i) + 1)
  S = Dict{Tuple{Int, Int, Int}, Vector{Int}}()

  # Initialise: µ == 1, just take the best element among [δ+1, d], as long as
  # the budget constraint is satisfied; δ == d, take nothing, whatever the
  # budget; β == 0, a simple m-set problem.
  for µ in 1:m(i) # β == 0
    for δ in (dimension(i) - 1):-1:0 # δ < d
      # Due to δ, there may be too few items (with respect to µ).
      # i.e., there are d - δ items to consider.
      m_ = min(dimension(i) - δ, µ)

      items = collect(partialsortperm(values(i, (δ + 1):dimension(i)), 1:m_, rev=true)) .+ δ
      V[µ, δ + 1, 0 + 1] = sum(value(i, o) for o in items)
      S[µ, δ, 0] = items
    end
  end

  for β in 0:budget(i)
    # δ == d
    for µ in 1:m(i)
      if β == 0
        # Optimum solution: take no object.
        V[µ, dimension(i) + 1, β + 1] = 0.0
        S[µ, dimension(i), β] = Int[]
      else
        # No solution.
        V[µ, dimension(i) + 1, β + 1] = -Inf
        S[µ, dimension(i), β] = [-1] # Nonsensical solution.
      end
    end

    # δ < d
    for δ in (dimension(i) - 1):-1:0
      # Filter the available objects (i.e. above δ+1 and satisfying the
      # budget constraint) and retrieve just their indices.
      all_objects = collect(enumerate(weights(i)))
      all_objects = collect(filter(o -> o[1] >= δ + 1, all_objects))
      all_objects = collect(filter(o -> o[2] >= β, all_objects))
      all_objects = collect(t[1] for t in all_objects)

      # No feasible solution?
      if length(all_objects) == 0
        V[1, δ + 1, β + 1] = -Inf
        S[1, δ, β] = [-1] # Nonsensical solution.
        continue
      end

      # Take the best one available object.
      v, x = findmax(collect(value(i, t) for t in all_objects))
      x = all_objects[x]

      V[1, δ + 1, β + 1] = v
      S[1, δ, β] = [x]
    end
  end

  # Dynamic part.
  for β in 1:budget(i)
    for µ in 2:m(i)
      for δ in (dimension(i) - 1):-1:0
        remaining_budget_with_δ = max(0, β - weights(i)[δ + 1])
        take_δ = value(i, δ + 1) + V[µ - 1, δ + 1 + 1, remaining_budget_with_δ + 1]
        dont_take_δ = V[µ, δ + 1 + 1, β + 1]

        both_subproblems_infeasible = -Inf == take_δ && -Inf == dont_take_δ

        if both_subproblems_infeasible
          V[µ, δ + 1, β + 1] = -Inf
          S[µ, δ, β] = [-1]
        elseif take_δ >= dont_take_δ
          V[µ, δ + 1, β + 1] = take_δ
          S[µ, δ, β] = vcat(δ + 1, S[µ - 1, δ + 1, remaining_budget_with_δ])
        else
          V[µ, δ + 1, β + 1] = dont_take_δ
          S[µ, δ, β] = S[µ, δ + 1, β]
        end
      end
    end
  end

  return BudgetedMSetSolution(i, S[m(i), 0, budget(i)], V, S)
end


text:
function greet()
    println("hello world")
end
# greet (generic function with 1 method)

greet()
# hello world

function calculator(x, y, operation)
    if operation == "+"
        x+y
    elseif operation == "-"
        x-y
    elseif operation == "*"
        x*y
    elseif operation == "/"
        x/y
    else
        println("Incorrect operation")
        return 0
    end
end
# calculator (generic function with 1 method)

println(calculator(10,20, "+"))
# 30
println(calculator(10,20, "-"))
# -10
println(calculator(10,20, "*"))
# 200
println(calculator(10,20, "/"))
# 0.5

# passing arguments
function say_hello(name)
    println("hello $name")
end
# say_hello (generic function with 1 method)

say_hello("rahul")
# hello rahul

# explicitly defining the type
function say_hello(name::String)
    println("hello $name")
end
# say_hello (generic function with 1 method)

say_hello("rahul")
# hello rahul

# Variable arguments
function letsplay(x,y...)
    println(x)
    println(y)
end
# letsplay (generic function with 1 method)

letsplay("cricket","hockey","tennis")
# cricket
# ("hockey","tennis")

max_stars_repo_name: PacktPublishing/Learning-Julia

max_issues_repo_name: PacktPublishing/Learning-Julia

max_forks_repo_name: PacktPublishing/Learning-Julia


text:
struct DisjointPartitioner{Mtd, Mtd′}
    mtd::Mtd
    mtd′::Mtd′
end

function partition_plaid(A::SparseMatrixCSC, K, method::DisjointPartitioner; kwargs...)
    Φ = partition_stripe(A, K, method.mtd; kwargs...)
    Π = partition_stripe(PermutedDimsArray(A, (2, 1)), K, method.mtd′, Φ; kwargs...)
    return (Π, Φ)
end

struct AlternatingPartitioner{Mtds}
    mtds::Mtds
end

AlternatingPartitioner(mtds...) = AlternatingPartitioner{typeof(mtds)}(mtds)

function partition_plaid(A::SparseMatrixCSC, K, method::AlternatingPartitioner; adj_A = nothing, kwargs...)
    if adj_A === nothing
        adj_A = adjointpattern(A)
    end
    Φ = partition_stripe(A, K, method.mtds[1]; adj_A=adj_A, kwargs...)
    Π = partition_stripe(adj_A, K, method.mtds[2], Φ; adj_A=A, kwargs...)
    for (i, mtd) in enumerate(method.mtds[3:end])
        if isodd(i)
            Φ = partition_stripe(A, K, mtd, Π; adj_A=adj_A, kwargs...)
        else
            Π = partition_stripe(adj_A, K, mtd, Φ; adj_A=A, kwargs...)
        end
    end
    return (Π, Φ)
end

struct AlternatingNetPartitioner{Hint, Mtds}
    hint::Hint
    mtds::Mtds
end

AlternatingNetPartitioner(mtds...) = AlternatingNetPartitioner{NoHint, typeof(mtds)}(NoHint(), mtds)
AlternatingNetPartitioner(hint::Hint, mtds...) where {Hint<:AbstractHint} = AlternatingNetPartitioner{Hint, typeof(mtds)}(hint, mtds)

function partition_plaid(A::SparseMatrixCSC, K, method::AlternatingNetPartitioner; adj_A = nothing, net = nothing, kwargs...)
    if adj_A === nothing
        adj_A = adjointpattern(A)
    end
    if net === nothing
        net = netcount(method.hint, A; kwargs...)
    end
    Φ = partition_stripe(A, K, method.mtds[1]; net=net, adj_A=adj_A, kwargs...)
    Π = partition_stripe(adj_A, K, method.mtds[2], Φ; adj_A=A, kwargs...)
    for (i, mtd) in enumerate(method.mtds[3:end])
        if isodd(i)
            Φ = partition_stripe(A, K, mtd, Π; net=net, adj_A=adj_A, kwargs...)
        else
            Π = partition_stripe(adj_A, K, mtd, Φ; adj_A=A, adj_net = net, kwargs...)
        end
    end
    return (Π, Φ)
end

struct SymmetricPartitioner{Mtds}
    mtds::Mtds
    function SymmetricPartitioner{Mtds}(mtds::Mtds) where {Mtds}
        return new{Mtds}(mtds)
    end
end

SymmetricPartitioner(mtd) = SymmetricPartitioner{Tuple{typeof(mtd)}}((mtd,))
SymmetricPartitioner(mtds...) = SymmetricPartitioner{typeof(mtds)}(mtds)

function partition_plaid(A::SparseMatrixCSC, K, method::SymmetricPartitioner; adj_A = nothing, kwargs...)
    if length(method.mtds) > 1
        if adj_A === nothing
            adj_A = adjointpattern(A)
        end
        Π = partition_stripe(A, K, method.mtds[1]; adj_A=adj_A, kwargs...)
        for (i, mtd) in enumerate(method.mtds[2:end])
            if isodd(i)
                Π = partition_stripe(A, K, mtd, Π; adj_A=adj_A, kwargs...)
            else
                Π = partition_stripe(adj_A, K, mtd, Π; adj_A=A, kwargs...)
            end
        end
    else
        Π = partition_stripe(A, K, method.mtds[1]; kwargs...)
    end
    return (Π, Π)
end


text:
Base.:(==)(x::InfExtendedTime, y::InfExtendedTime) = (isfinite(x) && isfinite(y)) ? x.finitevalue == y.finitevalue : x.flag == y.flag
Base.:(==)(x::Infinite, y::T) where {T<:InfExtendedTime} = T(x) == y
Base.:(==)(x::T, y::Infinite) where {T<:InfExtendedTime} = x == T(y)

Base.hash(x::InfExtendedTime, h::UInt) = isfinite(x) ? hash(x.finitevalue, h) : hash(x.flag, h)

function Base.isless(x::InfExtendedTime, y::InfExtendedTime)
    if isinf(x)
        return isneginf(x) && !isneginf(y)
    elseif isinf(y)
        return isposinf(y)
    else
        return x.finitevalue < y.finitevalue
    end
end
Base.isless(x::Infinite, y::T) where {T<:InfExtendedTime} = isless(T(x), y)
Base.isless(x::T, y::Infinite) where {T<:InfExtendedTime} = isless(x, T(y))

Base.:≤(x::Infinite, y::T) where {T<:InfExtendedTime} = T(x) ≤ y
Base.:≤(x::T, y::Infinite) where {T<:InfExtendedTime} = x ≤ T(y)


text:
export lorenzdiffusive

import StaticArrays: SVector
import DynamicalSystemsBase: ContinuousDynamicalSystem

export lorenzdiffusive

@inline @inbounds function eom_lorenzdiffusive(u, p, t)

    C₁₂, C₂₁, R, ϵ₁, ϵ₂ = p[1], p[2], p[3], p[4], p[5]
    x₁, x₂, x₃ = u[1], u[2], u[3]
    y₁, y₂, y₃ = u[4], u[5], u[6]

    dx1 = 10*(x₂ - x₁) + C₂₁*(y₁-x₁)
    dx2 = x₁*((R+ϵ₁) - x₃) - x₂
    dx3 = x₁*x₂ - 8/3*x₃

    dy1 = 10*(y₂ - y₁) + C₁₂*(x₁-y₁)
    dy2 = y₁*((R+ϵ₂) - y₃) - y₂
    dy3 = y₁*y₂ - 8/3*y₃
    return SVector{6}(dx1, dx2, dx3, dy1, dy2, dy3)
end

"""
    lorenzdiffusive(; ui = rand(6), C₁₂::Real = 5, C₂₁::Real = 0,
        R::Real = 28.0, ϵ₁::Real = -0.02, ϵ₂::Real = 0.03)

A dynamical system consisting of two diffusively coupled 3D Lorenz systems[^Martini2011].

The coupling magnitude from subsystem 1 to subsystem 2 is controlled by `C₁₂`, and the
coupling from subsystem 2 to subsystem 1 is controlled by `C₂₁`. The parameters `ϵ₁` and `ϵ₂`
add small deviations to the control parameter `R`.

## Equations of motion

```math
\\begin{aligned}
\\dot{x_1} &= 10(x_2 - x_1) + C_{21}*(y_1-x_1) \\\\
\\dot{x_2} &= x_1((R+ϵ₁) - x_3) - x_2 \\\\
\\dot{x_3} &= x_1x_2 - 8/3x_3 \\\\
\\dot{y_1} &= 10(y_2 - y_1) + C_{12}(x_1-y_1) \\\\
\\dot{y_2} &= y_1((R+\\epsilon_2) - y_3) - y_2 \\\\
\\dot{y_3} &= y_1y_2 - 8/3y_3
\\end{aligned}
```

[^Martini2011]: Martini, M., Kranz, T. A., Wagner, T., & Lehnertz, K. (2011). Inferring directional interactions from transient signals with symbolic transfer entropy. Physical review E, 83(1), 011919.
"""
function lorenzdiffusive(; ui = rand(6), C₁₂::Real = 5, C₂₁::Real = 0,
        R::Real = 28.0, ϵ₁::Real = -0.02, ϵ₂::Real = 0.03)

    p = [C₁₂, C₂₁, R, ϵ₁, ϵ₂]
    ContinuousDynamicalSystem(eom_lorenzdiffusive, ui, p)
end


text:
using Base.Test
using CorpusLoaders

files = ["types",
         "wikicorpus",
         "SemCor",
         "Senseval3"
        ]

@testset "$file" for file in files
    include("test_" * file * ".jl")
end


text:
println("Hello, World!")
println("Hello, World!")

max_stars_repo_name: pkeugine/multilingual

max_issues_repo_name: pkeugine/multilingual

max_forks_repo_name: pkeugine/multilingual


text:
RecipesBase.@recipe function f(fi::Fitted)
    layout := (2, 2)
    acf = autocor(fi.quantile_residuals)[2:end]
    @series begin
        seriestype := :path
        label := ""
        seriescolor := "black"
        subplot := 1
        marker := :circle
        fi.quantile_residuals
    end
    @series begin
        seriestype := :bar
        label := ""
        seriescolor := "black"
        subplot := 2
        acf
    end
    ub = ones(length(acf)) * 1.96 / sqrt(fi.num_obs)
    lb = ones(length(acf)) * -1.96 / sqrt(fi.num_obs)
    @series begin
        seriestype := :path
        linestyle := :dash
        seriescolor := "red"
        label := ""
        subplot := 2
        ub
    end
    @series begin
        seriestype := :path
        linestyle := :dash
        label := ""
        seriescolor := "red"
        subplot := 2
        lb
    end
    @series begin
        seriestype := :histogram
        label := ""
        seriescolor := "black"
        subplot := 3
        fi.quantile_residuals
    end
    qqpair = qqbuild(Normal(), fi.quantile_residuals)
    @series begin
        seriestype := :scatter
        label := ""
        seriescolor := "black"
        subplot := 4
        qqpair.qx, qqpair.qy
    end
    @series begin
        seriestype := :path
        seriescolor := "red"
        label := ""
        subplot := 4
        collect(-3:0.01:3), collect(-3:0.01:3)
    end
end


text:
module button_

using Printf
using IUP

export
    button

function btn_exit_cb end
function btn_on_off_cb end
function btn_big_button_cb end
function btn_image_button_cb end

#------------------ example html/examples/C/button.c --------------------------------------------
function button()
    IupOpen()    #Initializes IUP

    IupSetGlobal("UTF8MODE", "Yes")

    text = IupText("")    # Creates a text

    # Turns on read-only mode
    IupSetAttribute(text, "READONLY", "YES")

    # Associates text with handle "text"
    IupSetHandle("text", text)

    pixmap_release = UInt8.([
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,4,4,3,3,3,3,2,2,
        1,1,3,3,3,3,3,4,4,4,4,3,3,3,2,2,
        1,1,3,3,3,3,3,4,4,4,4,3,3,3,2,2,
        1,1,3,3,3,3,3,3,4,4,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
        2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2])

    pixmap_press = UInt8.([
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,4,4,3,3,3,3,3,2,2,
        1,1,3,3,3,3,4,4,4,4,3,3,3,3,2,2,
        1,1,3,3,3,3,4,4,4,4,3,3,3,3,2,2,
        1,1,3,3,3,3,3,4,4,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
        2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2])

    pixmap_inactive = UInt8.([
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,4,4,3,3,3,3,2,2,
        1,1,3,3,3,3,3,4,4,4,4,3,3,3,2,2,
        1,1,3,3,3,3,3,4,4,4,4,3,3,3,2,2,
        1,1,3,3,3,3,3,3,4,4,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2,
        1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
        2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2])

    #Defines released button's image size
    img_release = IupImage(16, 16, pixmap_release);

    # Defines released button's image colors
    IupSetAttribute(img_release, "1", "215 215 215")
    IupSetAttribute(img_release, "2", "40 40 40")
    IupSetAttribute(img_release, "3", "30 50 210")
    IupSetAttribute(img_release, "4", "240 0 0")

    # Associates img_release with handle "img_release"
    IupSetHandle("img_release", img_release)

    # Defines pressed button's image size
    img_press = IupImage(16, 16, pixmap_press)

    # Defines pressed button's image colors
    IupSetAttribute(img_press, "1", "40 40 40")
    IupSetAttribute(img_press, "2", "215 215 215")
    IupSetAttribute(img_press, "3", "0 20 180")
    IupSetAttribute(img_press, "4", "210 0 0")

    # Associates img_press with handle "img_press"
    IupSetHandle("img_press", img_press)

    # Defines inactive button's image size
    img_inactive = IupImage(16, 16, pixmap_inactive)

    # Defines inactive button's image colors
    IupSetAttribute(img_inactive, "1", "215 215 215")
    IupSetAttribute(img_inactive, "2", "40 40 40")
    IupSetAttribute(img_inactive, "3", "100 100 100")
    IupSetAttribute(img_inactive, "4", "200 200 200")

    # Associates img_inactive with handle "img_inactive"
    IupSetHandle("img_inactive", img_inactive)

    # Creates a button
    btn_image = IupButton("Button with image", "btn_image")

    # Sets released, pressed and inactive button images
    IupSetAttribute(btn_image, "IMAGE", "img_release")
    IupSetAttribute(btn_image, "IMPRESS", "img_press")
    IupSetAttribute(btn_image, "IMINACTIVE", "img_inactive")

    # Associates button callback with action bti_button_act
    IupSetAttribute(btn_image, "BUTTON_CB", "btn_image_button")

    # Associates btn_image with handle "btn_image"
    IupSetHandle("btn_image", btn_image)

    # Creates a button
    btn_big = IupButton("Big useless button", "")

    # Sets big button size
    IupSetAttribute(btn_big, "SIZE", "EIGHTHxEIGHTH")

    # Creates a button entitled Exit associated with action exit_act
    btn_exit = IupButton("Exit", "btn_exit")

    # Creates a button entitled on/off associated with action onoff_act
    btn_on_off = IupButton("on/off", "btn_on_off")

    # Creates dialog with the four buttons and the text
    dlg = IupDialog(IupVbox(
                            IupHbox(btn_image, btn_on_off, btn_exit, C_NULL),
                            text,
                            btn_big,
                            C_NULL
                            )
                    );

    # Sets dialogs title to IupButton turns resize, maximize, minimize and menubox off
    IupSetAttributes(dlg, "EXPAND = YES, TITLE = IupButton, RESIZE = NO")
    IupSetAttributes(dlg, "MENUBOX = NO, MAXBOX = NO, MINBOX = NO")

    # Registers callbacks
    IupSetCallback(btn_exit,   "ACTION", @cfunction(btn_exit_cb,   Int, (),))
    IupSetCallback(btn_on_off, "ACTION", @cfunction(btn_on_off_cb, Int, (),))
    IupSetCallback(btn_image,  "ACTION", @cfunction(btn_image_button_cb, Int, (Ptr{Ihandle}, Cint, Cint, Cint, Cint, Ptr{UInt8})))

    IupSetAttribute(btn_big, "BUTTON_CB", "bigtest");
    IupSetFunction("bigtest", @cfunction(btn_big_button_cb, Int, (Ptr{Ihandle}, Cint, Cint)))

    # Shows dialog on the center of the screen
    IupShowXY(dlg, IUP_CENTER, IUP_CENTER)

    IupMainLoop()        # Initializes IUP main loop
    IupClose()            # And close it when ready
end

function btn_image_button_cb(self::Ptr{Ihandle}, b::Cint, e::Cint, x::Cint, y::Cint, t::Ptr{UInt8})
    # If the left button changed its state...
    if (b == IUP_BUTTON1)

        # Recovers "text" handle
        text = IupGetHandle("text")

        # If the button was pressed...
        if (e == 1)
            IupSetAttribute(text, "VALUE", "Red button pressed")
        else        # the button was released
            IupSetAttribute(text, "VALUE", "Red button released")
        end
    end

    # Executed function successfully
    return IUP_DEFAULT
end

function btn_on_off_cb()
    btn_image = IupGetHandle("btn_image")        #Recovers "btn_image" handle

    # If the button with with image is active...
    if (unsafe_string(IupGetAttribute(btn_image, "ACTIVE")) == "YES")
        # Deactivates the button with image */
        IupSetAttribute(btn_image, "ACTIVE","NO")
    else
        # Activates the button with image */
        IupSetAttribute(btn_image, "ACTIVE", "YES")
    end

    return IUP_DEFAULT
end

function btn_exit_cb()
      return IUP_CLOSE        # Exits the program
end

function btn_big_button_cb(self::Ptr{Ihandle}, button::Cint, press::Cint)
    @printf("BUTTON_CB(button=%d, press=%d)\n", button, press)
    return IUP_DEFAULT
end

end     # module


text:
function make_null(gpT::GPE, gpC::GPE, kNull::Kernel, mNull::Mean, logNoise::Float64)
    yNull = [gpT.y; gpC.y]
    xNull = [gpT.x gpC.x]
    gpNull = GPE(xNull, yNull, mNull, kNull, logNoise)
end
function make_null(gpT::GPE, gpC::GPE)
    # copy parameters from treatment GP
    kNull = gpT.kernel
    mNull = gpT.mean
    logNoise = convert(Float64, gpT.logNoise)
    return make_null(gpT, gpC, kNull, mNull, logNoise)
end
function prior_rand(gp::GPE)
    μ = mean(gp.mean, gp.x)
    mvn = MultivariateNormal(μ, gp.cK)
    Ysim = rand(mvn)
    return Ysim
end