text:
################################################################################
#                               BASE EXTENSIONS
################################################################################
# Basic extensions
for VarType in (:GeneralVariableRef, :DispatchVariableRef)
    @eval begin
        Base.copy(v::$VarType) = v
        Base.broadcastable(v::$VarType) = Ref(v)
        Base.length(v::$VarType) = 1
        JuMP.isequal_canonical(v::$VarType, w::$VarType) = isequal(v, w)
    end
end

# Extend Base.isequal for GeneralVariableRef (== is for nlp expressions)
function Base.isequal(v::T, w::T) where {T <: GeneralVariableRef}
    return v.model === w.model && v.raw_index == w.raw_index &&
        v.index_type == w.index_type && v.param_index == w.param_index
end

# Extend Base.isequal for DispatchVariableRef
function Base.isequal(v::T, w::U) where {T <: DispatchVariableRef,
                                         U <: DispatchVariableRef}
    return v.model === w.model && v.index == w.index
end

# Extract the root name of a variable reference (removes the bracketed container indices)
function _remove_name_index(vref::GeneralVariableRef)
    name = JuMP.name(vref)
    first_bracket = findfirst(isequal('['), name)
    if isnothing(first_bracket)
        return name
    else
        return name[1:prevind(name, first_bracket, 1)] # can handle unicode
    end
end

# Define basic attribute getters
_index_type(vref::GeneralVariableRef) = vref.index_type
_raw_index(vref::GeneralVariableRef) = vref.raw_index
_param_index(vref::GeneralVariableRef) = vref.param_index

################################################################################
#                          BASIC REFERENCE ACCESSERS
################################################################################
"""
    JuMP.index(vref::GeneralVariableRef)::AbstractInfOptIndex

Extend `JuMP.index` to return the appropriate index of `vref`.

**Example**
```julia-repl
julia> index(vref)
FiniteVariableIndex(1)
```
"""
function JuMP.index(vref::GeneralVariableRef)::AbstractInfOptIndex
    index_type = _index_type(vref)
    if index_type == DependentParameterIndex
        return index_type(DependentParametersIndex(_raw_index(vref)),
                           _param_index(vref))
    else
        return index_type(_raw_index(vref))
    end
end

"""
    JuMP.index(vref::DispatchVariableRef)::AbstractInfOptIndex

Extend `JuMP.index` to return the appropriate index of `vref`.
"""
function JuMP.index(vref::DispatchVariableRef)::AbstractInfOptIndex
    return vref.index
end

"""
    JuMP.owner_model(vref::GeneralVariableRef)::InfiniteModel

Extend `JuMP.owner_model` to return the model where `vref` is stored.

**Example**
```julia-repl
julia> owner_model(vref)
An InfiniteOpt Model
Feasibility problem with:
Finite Parameters: 0
Infinite Parameters: 0
Variable: 1
Derivatives: 0
Measures: 0
`FiniteVariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 1 constraint
`FiniteVariableRef`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint
Names registered in the model: vref
Optimizer model backend information:
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.
```
"""
function JuMP.owner_model(vref::GeneralVariableRef)::InfiniteModel
    return vref.model
end

"""
    JuMP.owner_model(vref::DispatchVariableRef)::InfiniteModel

Extend `JuMP.owner_model` to return the model where `vref` is stored.
"""
function JuMP.owner_model(vref::DispatchVariableRef)::InfiniteModel
    return vref.model
end

################################################################################
#                          DISPATCH VARIABLE MAKERS
################################################################################
"""
    dispatch_variable_ref(model::InfiniteModel, index::AbstractInfOptIndex)

Return the variable reference associated the type of `index`. This needs to be
defined for each variable reference type.
"""
function dispatch_variable_ref end

"""
    dispatch_variable_ref(vef::GeneralVariableRef)::DispatchVariableRef

Return the concrete [`DispatchVariableRef`](@ref) this associated with `vref`.
This relies on `dispatch_variable_ref` being extended for the index type,
otherwise an `MethodError` is thrown.
"""
function dispatch_variable_ref(vref::GeneralVariableRef)::DispatchVariableRef
    model = JuMP.owner_model(vref)
    idx = JuMP.index(vref)
    return dispatch_variable_ref(model, idx)
end

################################################################################
#                          CORE DATA METHODS
################################################################################
"""
    _add_data_object(model::InfiniteModel, object::AbstractDataObject)::ObjectIndex

Add `object` to the appropriate `CleverDict` in `model` and return the its
index. This needs to be defined for the type of `object`. These definitions
need to use `MOIUC.add_item` to add the object to the `CleverDict`.
"""
function _add_data_object end

"""
    _data_dictionary(vref::DispatchVariableRef)::MOIUC.CleverDict

Return the `CleverDict` that stores data objects for the type of `vref`. This
needs to be defined for the type of `vref`.
"""
function _data_dictionary end

"""
    _data_dictionary(vref::GeneralVariableRef)::MOIUC.CleverDict

Return the `CleverDict` that stores data objects for the type of `vref`. It
relies on `_data_dictionary` being defined
for the underlying `DispatchVariableRef`, otherwise an `MethodError` is thrown.
"""
function _data_dictionary(vref::GeneralVariableRef)::MOIUC.CleverDict
    return _data_dictionary(dispatch_variable_ref(vref))
end

"""
    _data_object(vref::DispatchVariableRef)::AbstractDataObject

Return the data object associated with `vref`, in other words the object its
index points to in the `InfiniteModel`. This needs to be defined for the type
of `vref`. This should use `_data_dictionary` to access the `CleverDict` that
the object is stored in.
"""
function _data_object end

"""
    _data_object(vref::GeneralVariableRef)::AbstractDataObject

Return the data object associated with `vref`, in other words the object its
index points to in the `InfiniteModel`. It relies on `_data_object` being defined
for the underlying `DispatchVariableRef`, otherwise an `MethodError` is thrown.
"""
function _data_object(vref::GeneralVariableRef)::AbstractDataObject
    return _data_object(dispatch_variable_ref(vref))
end

"""
    _delete_data_object(vref::DispatchVariableRef)::Nothing

Delete the concrete `AbstractDataObject` associated with `vref`.
"""
function _delete_data_object(vref::DispatchVariableRef)::Nothing
    delete!(_data_dictionary(vref), JuMP.index(vref))
    return
end

################################################################################
#                                 NAME METHODS
################################################################################
# Dispatch fallback
function JuMP.name(vref::DispatchVariableRef)
    throw(ArgumentError("`JuMP.name` not defined for variable reference type " *
                        "`$(typeof(vref))`."))
end

"""
    JuMP.name(vref::GeneralVariableRef)::String

Extend `JuMP.name` to return the name of `vref`. It relies on `JuMP.name` being
defined for the underlying `DispatchVariableRef`, otherwise an `ArgumentError`
is thrown.
"""
function JuMP.name(vref::GeneralVariableRef)::String
    return JuMP.name(dispatch_variable_ref(vref))
end

# Dispatch fallback
function JuMP.set_name(vref::DispatchVariableRef, name::String)
    throw(ArgumentError("`JuMP.set_name` not defined for variable reference type " *
                        "`$(typeof(vref))`."))
end

"""
    JuMP.set_name(vref::GeneralVariableRef, name::String)::Nothing

Extend `JuMP.set_name` to set the name of `vref`. It relies on `JuMP.set_name`
being defined for the underlying `DispatchVariableRef`, otherwise an
`ArgumentError` is thrown.
"""
function JuMP.set_name(vref::GeneralVariableRef, name::String)::Nothing
    return JuMP.set_name(dispatch_variable_ref(vref), name)
end

################################################################################
#                             VALIDITY METHODS
################################################################################
"""
    JuMP.is_valid(model::InfiniteModel, vref::DispatchVariableRef)::Bool

Extend `JuMP.is_valid` to return `Bool` if `vref` is a valid reference.
"""
function JuMP.is_valid(model::InfiniteModel, vref::DispatchVariableRef)::Bool
    return model === JuMP.owner_model(vref) &&
           haskey(_data_dictionary(vref), JuMP.index(vref))
end

# DependentParameterRef
function JuMP.is_valid(model::InfiniteModel, vref::DependentParameterRef)::Bool
    return model === JuMP.owner_model(vref) &&
           haskey(_data_dictionary(vref), JuMP.index(vref).object_index)
end

"""
    JuMP.is_valid(model::InfiniteModel, vref::GeneralVariableRef)::Bool

Extend `JuMP.is_valid` to return `Bool` if `vref` is a valid reference.

**Example**
```julia-repl
julia> is_valid(model, vref)
true
```
"""
function JuMP.is_valid(model::InfiniteModel, vref::GeneralVariableRef)::Bool
    return JuMP.is_valid(model, dispatch_variable_ref(vref))
end

################################################################################
#                             CORE OBJECT METHODS
################################################################################
"""
    _core_variable_object(vref::DispatchVariableRef)::Union{InfOptParameter, InfOptVariable, Measure}

Return the core object that `vref` points to. This needs to be extended for type
of `vref`. This should use `_data_object` to access the data object where the
variable object is stored.
"""
function _core_variable_object end

"""
    _core_variable_object(vref::GeneralVariableRef)::Union{InfOptParameter, InfOptVariable, Measure}

Return the core object that `vref` points to. This is enabled
with appropriate definitions of `_core_variable_object` for the
underlying `DispatchVariableRef`, otherwise an `MethodError` is thrown.
"""
function _core_variable_object(vref::GeneralVariableRef)
    return _core_variable_object(dispatch_variable_ref(vref))
end

"""
    _set_core_variable_object(vref::DispatchVariableRef, object)::Nothing

Sets the core object that `vref` points to `object`. This needs to be extended
for types of `vref` and `object`. This should use `_data_object` to access the
data object where the variable object is stored.
"""
function _set_core_variable_object end

################################################################################
#                               DEPENDENCY METHODS
################################################################################
# Define wrappers for internal usage methods and their templates
for op = (:_infinite_variable_dependencies, :_semi_infinite_variable_dependencies,
          :_point_variable_dependencies, :_measure_dependencies,
          :_constraint_dependencies, :_derivative_dependencies,
          :_derivative_constraint_dependencies, :_parameter_function_dependencies,
          :_generative_measures)
    @eval begin
        @doc """
            $($op)(vref::DispatchVariableRef)::Vector{AbstractInfOptIndex}

        Return the indices of these entities that depend on `vref`. This needs to
        be extended for type of `vref`. This should use `_data_object` to access the
        data object where the name is stored if appropriate.
        """
        function $(op) end

        # define the dispatch version
        @doc """
            $($op)(vref::GeneralVariableRef)::Vector{AbstractInfOptIndex}

        Return the indices of these entities that depend on `vref`. This is enabled
        with appropriate definitions of `$($op)` for the
        underlying `DispatchVariableRef`, otherwise an `MethodError` is thrown.
        """
        function $op(vref::GeneralVariableRef)
            return $op(dispatch_variable_ref(vref))
        end
    end
end

################################################################################
#                              USED BY METHODS
################################################################################
# Define the usage method wrappers and their fallbacks
for op = (:used_by_infinite_variable, :used_by_semi_infinite_variable,
          :used_by_point_variable, :used_by_measure, :used_by_constraint,
          :used_by_objective, :used_by_derivative, :is_used,
          :has_derivative_constraints, :used_by_parameter_function)
    @eval begin
        # define the fallback method
        function $op(vref::DispatchVariableRef)
            str = string("`$($op)` not defined for variable reference type ",
                         "`$(typeof(vref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            $($op)(vref::GeneralVariableRef)::Bool

        Define `$($op)` for general variable references. It relies on `$($op)`
        being defined for the underlying `DispatchVariableRef`, otherwise an
        `ArgumentError` is thrown. See the underlying docstrings for more
        information.
        """
        function $op(vref::GeneralVariableRef)::Bool
            return $op(dispatch_variable_ref(vref))
        end
    end
end

################################################################################
#                              DELETE METHODS
################################################################################
# Dispatch fallback
function JuMP.delete(model::InfiniteModel, vref)
    throw(ArgumentError("`JuMP.delete` not defined for variable reference type " *
                        "`$(typeof(vref))`."))
end

"""
    JuMP.delete(model::InfiniteModel, vref::GeneralVariableRef)::Nothing

Extend `JuMP.delete` to delete `vref` and its dependencies. It relies on
`JuMP.delete` being defined for the underlying `DispatchVariableRef`, otherwise
an `ArgumentError` is thrown.
"""
function JuMP.delete(model::InfiniteModel, vref::GeneralVariableRef)::Nothing
    return JuMP.delete(model, dispatch_variable_ref(vref))
end

"""
    JuMP.delete(model::InfiniteModel,
                prefs::AbstractArray{<:GeneralVariableRef})::Nothing

Extend `JuMP.delete` to delete a group of dependent infinite parameters and
their dependencies. An `ArgumentError` is thrown if `prefs` are not dependent
infinite parameters.
"""
function JuMP.delete(model::InfiniteModel,
                     prefs::AbstractArray{<:GeneralVariableRef})::Nothing
    return JuMP.delete(model, dispatch_variable_ref.(prefs))
end

################################################################################
#                              FUNCTIONAL CALLS
################################################################################
# Dispatch fallback
function _functional_reference_call(vref, idx_type, args...; kwargs...)
    arg_str = join(args, ", ")
    kwarg_str = join(Tuple(string(k, " = ", v) for (k, v) in kwargs), ", ")
    kwarg_str = isempty(kwarg_str) ? "" : "; " * kwarg_str
    error("Functional call `$(vref)($(arg_str)$(kwarg_str))` is unrecognized ",
          "syntax.")
end

# Function calls made directly on variable references
# Unfortunetly, this is not readily compatible with docstrings...
function (vref::GeneralVariableRef)(args...; kwargs...)
    return _functional_reference_call(vref, _index_type(vref), args...;
                                      kwargs...)
end

################################################################################
#                              PARAMETER METHODS
################################################################################
"""
    _parameter_number(pref::DispatchVariableRef)::Int

Return the parameter creation number for `pref` assuming it is an infinite
parameter. This needs to be defined for the type of `pref`. This should use
the `_data_object` to get the number.
"""
function _parameter_number end

"""
    _parameter_number(pref::GeneralVariableRef)::Int

Return the parameter creation number for `pref` assuming it is an infinite
parameter. It relies on `_parameter_number` being properly defined for the
underlying `DispatchVariableRef`, otherwise an `MethodError` is thrown.
"""
function _parameter_number(pref::GeneralVariableRef)::Int
    return _parameter_number(dispatch_variable_ref(pref))
end

"""
    _object_number(pref::DispatchVariableRef)::Int

Return the object number for `pref` assuming it is an infinite
parameter. This needs to be defined for the type of `pref`. This should use
the `_data_object` to get the number.
"""
function _object_number end

"""
    _object_number(pref::GeneralVariableRef)::Int

Return the object number for `pref` assuming it is an infinite
parameter. It relies on `_object_number` being properly defined for the
underlying `DispatchVariableRef`, otherwise an `MethodError` is thrown.
"""
function _object_number(pref::GeneralVariableRef)::Int
    return _object_number(dispatch_variable_ref(pref))
end

# Define 1 argument user method wrappers and their fallbacks
for op = (:infinite_domain, :num_supports, :significant_digits, :has_supports,
          :supports, :delete_supports, :fill_in_supports!, :parameter_value,
          :derivative_method, :has_generative_supports, :has_internal_supports,
          :add_generative_supports, :raw_function, :generative_support_info)
    @eval begin
        # define the fallback method
        function $op(pref; kwargs...)
            str = string("`$($op)` not defined for variable reference type ",
                         "`$(typeof(pref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            $($op)(prefs; [kwargs...])

        Define `$($op)` for general variable references. It relies on `$($op)`
        being defined for the underlying `DispatchVariableRef`, otherwise an
        `ArgumentError` is thrown. See the underlying docstrings for more
        information. Note that this is a auto generated wrapper and the underlying
        method may or may not use `kwargs`.
        """
        function $op(prefs::Union{GeneralVariableRef, AbstractArray{<:GeneralVariableRef}}; kwargs...)
            return $op(dispatch_variable_ref.(prefs); kwargs...)
        end
    end
end

# Dispatch fallback
function set_infinite_domain(pref, domain::AbstractInfiniteDomain)
    throw(ArgumentError("`set_infinite_domain` not defined for variable reference type(s) " *
                        "`$(typeof(pref))`."))
end

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

Specify the scalar infinite domain of the infinite parameter `pref` to `domain`. Note
this will reset/delete all the supports contained in the
underlying parameter object. Also, errors if `pref` is used
by a measure. An `ArgumentError` is thrown if `pref` is not an infinite parameter.
"""
function set_infinite_domain(
    pref::GeneralVariableRef,
    domain::InfiniteScalarDomain
    )::Nothing
    return set_infinite_domain(dispatch_variable_ref(pref), domain)
end

"""
    set_infinite_domain(prefs::AbstractArray{<:GeneralVariableRef},
                     domain::InfiniteArrayDomain)::Nothing

Specify the multi-dimensional infinite domain of the dependent infinite parameters
`prefs` to `domain`. Note this will reset/delete all the supports contained in the
underlying [`DependentParameters`](@ref) object. This will error if the not all
of the dependent infinite parameters are included or if any of them are used by
measures. An `ArgumentError` is thrown if `prefs` are not dependent infinite
parameters.
"""
function set_infinite_domain(
    prefs::AbstractArray{<:GeneralVariableRef},
    domain::InfiniteArrayDomain
    )::Nothing
    return set_infinite_domain(dispatch_variable_ref.(prefs), domain)
end

# Better fallbacks for supports
function supports(pref::DispatchVariableRef; kwargs...)
    throw(ArgumentError("`supports` not defined for variable reference type(s) " *
                        "`$(typeof(pref))`."))
end
function supports(prefs::AbstractArray; kwargs...)
    throw(ArgumentError("`supports` not defined for variable reference type(s) " *
                        "`$(typeof(prefs))`."))
end

# Dispatch fallback
function set_supports(pref, supports; kwargs...)
    throw(ArgumentError("`set_supports` not defined for variable reference type(s) " *
                        "`$(typeof(pref))`."))
end

"""
    set_supports(pref::GeneralVariableRef, supports::Union{Real, Vector{<:Real}};
                 [force::Bool = false])::Nothing

Set the support points associated with a single infinite
parameter `pref`. An `ArgumentError` is thrown if `pref` is not an independent
infinite parameter.
"""
function set_supports(
    pref::GeneralVariableRef,
    supports::Union{Real, Vector{<:Real}};
    force::Bool = false,
    label::Type{<:AbstractSupportLabel} = UserDefined
    )::Nothing
    return set_supports(dispatch_variable_ref(pref), supports,
                        force = force, label = label)
end

"""
    set_supports(
        prefs::Union{Vector{GeneralVariableRef}, AbstractArray{<:GeneralVariableRef}},
        supports::Union{Array{<:Real, 2}, Vector{<:AbstractArray{<:Real}}};
        [force::Bool = false]
        )::Nothing

Set the support points associated with dependent infinite
parameters `prefs`. An `ArgumentError` is thrown if `prefs` is are not
dependent infinite parameters.
"""
function set_supports(
    prefs::AbstractArray{<:GeneralVariableRef},
    supports::Union{Array{<:Real, 2}, Vector{<:AbstractArray{<:Real}}};
    label::Type{<:AbstractSupportLabel} = UserDefined,
    force::Bool = false
    )::Nothing
    return set_supports(dispatch_variable_ref.(prefs), supports, label = label,
                        force = force)
end

# Dispatch fallback
function add_supports(pref, supports; kwargs...)
    throw(ArgumentError("`add_supports` not defined for variable reference type(s) " *
                        "`$(typeof(pref))`."))
end

"""
    add_supports(pref::GeneralVariableRef,
                 supports::Union{Real, Vector{<:Real}})::Nothing

Add the support points `supports` to a single infinite
parameter `pref`. An `ArgumentError` is thrown if `pref` is not an independent
infinite parameter.
"""
function add_supports(
    pref::GeneralVariableRef,
    supports::Union{Real, Vector{<:Real}};
    check::Bool = true,
    label::Type{<:AbstractSupportLabel} = UserDefined
    )::Nothing
    return add_supports(dispatch_variable_ref(pref), supports,
                        check = check, label = label)
end

"""
    add_supports(
        prefs::Union{Vector{GeneralVariableRef}, AbstractArray{<:GeneralVariableRef}},
        supports::Union{Array{<:Real, 2}, Vector{<:AbstractArray{<:Real}}}
        )::Nothing

Add the support points `supports` to the dependent infinite
parameters `prefs`. An `ArgumentError` is thrown if `prefs` is are not
dependent infinite parameters.
"""
function add_supports(
    prefs::AbstractArray{<:GeneralVariableRef},
    supports::Union{Array{<:Real, 2}, Vector{<:AbstractArray{<:Real}}};
    label::Type{<:AbstractSupportLabel} = UserDefined,
    check::Bool = true
    )::Nothing
    return add_supports(dispatch_variable_ref.(prefs), supports, label = label,
                        check = check)
end

# Fallback
function JuMP.set_value(vref::DispatchVariableRef, value::Real)
    throw(ArgumentError("`JuMP.set_value` not defined for variable reference type " *
                        "`$(typeof(vref))`."))
end

"""
    JuMP.set_value(vref::DispatchVariableRef, value::Real)::Nothing

Extend `JuMP.set_value` to
set the value of `vref`. It relies on `JuMP.set_value`
being defined for the underlying `DispatchVariableRef`, otherwise an
`ArgumentError` is thrown.
"""
function JuMP.set_value(vref::GeneralVariableRef, value::Real)::Nothing
    return JuMP.set_value(dispatch_variable_ref(vref), value)
end

# Dispatch fallback
function set_derivative_method(pref::DispatchVariableRef, method)
    throw(ArgumentError("`set_derivative_method` not defined for variable reference type(s) " *
                        "`$(typeof(pref))`."))
end

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

Specify the numerical derivative evaluation technique associated with `pref`.
An `ArgumentError` is thrown if `pref` is not an infinite parameter.
"""
function set_derivative_method(
    pref::GeneralVariableRef,
    method::AbstractDerivativeMethod
    )::Nothing
    return set_derivative_method(dispatch_variable_ref(pref), method)
end

# Define parameter status setters
for op = (:_set_has_generative_supports, :_set_has_internal_supports,
          :_set_has_derivative_constraints)
    @eval begin
        # define the fallback method
        function $op(vref::DispatchVariableRef, status)
            str = string("`$($op)` not defined for variable reference type ",
                         "`$(typeof(vref))`.")
            throw(ArgumentError(str))
        end
        # define the dispatch version
        function $op(vref::GeneralVariableRef, status::Bool)::Nothing
            return $op(dispatch_variable_ref(vref), status)
        end
    end
end

# Dispatch fallback
function call_function(fref::DispatchVariableRef, support...)
    throw(ArgumentError("`call_function` not defined for variable reference type(s) " *
                        "`$(typeof(fref))`."))
end

"""
    call_function(fref::GeneralVariableRef, support...)::Float64

Call the parameter function of `fref` at `support`.
An `ArgumentError` is thrown if `fref` is not a parameter function.
"""
function call_function(fref::GeneralVariableRef, support...)::Float64
    return call_function(dispatch_variable_ref(fref), support...)
end

################################################################################
#                              VARIABLE METHODS
################################################################################
# Define single argument variable method wrappers and their fallbacks
for op = (:raw_parameter_refs, :parameter_refs, :parameter_list,
          :start_value_function, :reset_start_value_function,
          :infinite_variable_ref, :eval_supports, :raw_parameter_values,
          :parameter_values)
    @eval begin
        # define the fallback method
        function $op(vref::DispatchVariableRef)
            str = string("`$($op)` not defined for variable reference type ",
                         "`$(typeof(vref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            $($op)c(vref::GeneralVariableRef)

        Define `$($op)` for general variable references. It relies on `$($op)`
        being defined for the underlying `DispatchVariableRef`, otherwise an
        `ArgumentError` is thrown. See the underlying docstrings for more
        information.
        """
        function $op(vref::GeneralVariableRef)
            return $op(dispatch_variable_ref(vref))
        end
    end
end

# Dispatch fallback
function set_start_value_function(vref::DispatchVariableRef, start)
    throw(ArgumentError("`set_start_value_function` not defined for variable reference type " *
                        "`$(typeof(vref))`."))
end

"""
    set_start_value_function(vref::GeneralVariableRef, start::Union{Real, Function})::Nothing

Set the start value function of `vref`. It relies on `set_start_value_function`
being defined for the underlying `DispatchVariableRef`, otherwise an
`ArgumentError` is thrown.
"""
function set_start_value_function(vref::GeneralVariableRef, start)::Nothing
    return set_start_value_function(dispatch_variable_ref(vref), start)
end

################################################################################
#                               MEASURE METHODS
################################################################################
# Define measure queries and their fallbacks
for op = (:measure_function, :measure_data, :is_analytic, :expand)
    @eval begin
        # define the fallback method
        function $op(mref::DispatchVariableRef)
            str = string("`$($op)` not defined for variable reference type ",
                         "`$(typeof(mref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            $($op)(mref::GeneralVariableRef)

        Define `$($op)` for general variable references. Errors if `mref` does
        not correspond to a `MeasureRef`. See the underlying docstrings for more
        information.
        """
        function $op(mref::GeneralVariableRef)
            return $op(dispatch_variable_ref(mref))
        end
    end
end

################################################################################
#                               DERIVATIVE METHODS
################################################################################
# Define measure queries and their fallbacks
for op = (:derivative_argument, :operator_parameter, :evaluate,
          :derivative_constraints, :delete_derivative_constraints)
    @eval begin
        # define the fallback method
        function $op(dref::DispatchVariableRef)
            str = string("`$($op)` not defined for variable reference type ",
                         "`$(typeof(dref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            $($op)(dref::GeneralVariableRef)

        Define `$($op)` for general variable references. Errors if `dref` does
        not correspond to a `DerivativeRef`. See the underlying docstrings for more
        information.
        """
        function $op(dref::GeneralVariableRef)
            return $op(dispatch_variable_ref(dref))
        end
    end
end

################################################################################
#                            VARIABLE INFO METHODS
################################################################################
# Define the 1 argument JuMP variable info methods
for op = (:has_lower_bound, :has_upper_bound, :is_fixed, :is_binary, :is_integer,
          :lower_bound, :upper_bound, :fix_value, :start_value,
          :set_binary, :set_integer,
          :LowerBoundRef, :UpperBoundRef, :FixRef, :BinaryRef, :IntegerRef,
          :delete_lower_bound, :delete_upper_bound, :unfix, :unset_binary,
          :unset_integer)
    @eval begin
        # define the fallback method
        function JuMP.$op(vref::DispatchVariableRef)
            str = string("`JuMP.$($op)` not defined for variable reference type ",
                         "`$(typeof(vref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            JuMP.$($op)(vref::GeneralVariableRef)

        Define `JuMP.$($op)` for general variable references. It relies on `JuMP.$($op)`
        being defined for the underlying `DispatchVariableRef`, otherwise an
        `ArgumentError` is thrown. See the underlying docstrings for more
        information.
        """
        function JuMP.$op(vref::GeneralVariableRef)
            return JuMP.$op(dispatch_variable_ref(vref))
        end
    end
end

# Define the 2 argument setting methods (except for fix)
for op = (:set_lower_bound, :set_upper_bound, :set_start_value)
    @eval begin
        # define the fallback method
        function JuMP.$op(vref::DispatchVariableRef, value)
            str = string("`JuMP.$($op)` not defined for variable reference type ",
                         "`$(typeof(vref))`.")
            throw(ArgumentError(str))
        end

        # define the dispatch version
        @doc """
            JuMP.$($op)(vref::GeneralVariableRef, value::Real)::Nothing

        Define `JuMP.$($op)` for general variable references. It relies on `JuMP.$($op)`
        being defined for the underlying `DispatchVariableRef`, otherwise an
        `ArgumentError` is thrown. See the underlying docstrings for more
        information.
        """
        function JuMP.$op(vref::GeneralVariableRef, value::Real)::Nothing
            return JuMP.$op(dispatch_variable_ref(vref), value)
        end
    end
end

# Dispatch fallback for JuMP.fix
function JuMP.fix(vref::DispatchVariableRef, value::Real; force::Bool = false)
    throw(ArgumentError("`JuMP.fix` not defined for variable reference type " *
                        "`$(typeof(vref))`."))
end

"""
    JuMP.fix(vref::GeneralVariableRef, value::Real; force::Bool = false)::Nothing

Define `JuMP.fix` for general variable references. It relies on `JuMP.fix`
being defined for the underlying `DispatchVariableRef`, otherwise an
`ArgumentError` is thrown. See the underlying docstrings for more
information.
"""
function JuMP.fix(vref::GeneralVariableRef, value::Real;
                  force::Bool = false)::Nothing
    return JuMP.fix(dispatch_variable_ref(vref), value, force = force)
end


text:
#
# nllsq.jl --
#
# Non-linear least squares fit with Mike Powell's NEWUOA method.
#
# ----------------------------------------------------------------------------
#
# This file is part of OptimPackNextGen.jl which is licensed under the MIT
# "Expat" License:
#
# Copyright (C) 2018-2019, Éric Thiébaut.
#
# ----------------------------------------------------------------------------
module NonLinearLeastSquares

export
    nllsq,
    nllsq!

using ArrayTools
using Printf
using ..Powell

"""
```julia
nllsq([w,] y, f, p0, x) -> pmin, cmin
```

performs a non-linear (weighted) least fit of the data `y` by the model
`f(p,x)` where `f` is a function, `p` are the model parameters (a vector of
floating point values) and `x` are the explanatory variables (they are left
unmodifed by the method and are just passed to the function). Argument `p0`
specifies the initial values of the parameters.  The best parameters `pmin`
and the corresponding objective function `cmin` are returned.  The objective
function is defined as:

```
c(p) = ‖y - f(p,x)‖²
```

or as:

```
c(p) = ‖y - f(p,x)‖²_W
```

with `W = diag(w)` if the weights `w` are specified.

Keyword `rho` can be set with `(rhobeg,rhoend)` the initial and final size of
the trust region relative to the scaling of the parameters.

Keyword `scale` can be set with the typical size of the parameters.  By
default, `scale[i] = max(abs(p0[i]), sqrt(eps))`.

Other keywords are passed to `newuoa`.

See also: [`newuoa`](@ref).
"""
function nllsq(y::AbstractArray{<:Real,N},
               f::Function,
               p0::AbstractVector{<:Real},
               x; kwds...) where {N}
    return nllsq!(y, f, copyparameters(p0), x; kwds...)
end

function nllsq(w::AbstractArray{<:Real,N},
               y::AbstractArray{<:Real,N},
               f::Function,
               p0::AbstractVector{<:Real},
               x; kwds...) where {N}
    return nllsq!(w, y, f, copyparameters(p0), x; kwds...)
end

function nllsq!(y::AbstractArray{<:Real,N},
                f::Function,
                p0::DenseVector{Cdouble},
                x;
                rho::NTuple{2,Real} = (0.1, 1e-6),
                scale::AbstractVector{<:Real} = defaultscale(p0),
                kwds...) where {N}
    status, pmin, cmin = Newuoa.minimize!(p -> _nllsq(y, f, p, x),
                                          p0, rho[1], rho[2]; check=false,
                                          scale=scale, kwds...)
    check(status)
    return pmin, cmin
end

function nllsq!(w::AbstractArray{<:Real,N},
                y::AbstractArray{<:Real,N},
                f::Function,
                p0::AbstractVector{Cdouble},
                x;
                rho::NTuple{2,Real} = (0.1, 1e-6),
                scale::AbstractVector{<:Real} = defaultscale(p0),
                kwds...) where {N}
    status, pmin, cmin = Newuoa.minimize!(p -> _nllsq(w, y, f, p, x),
                                          p0, rho[1], rho[2]; check=false,
                                          scale=scale, kwds...)
    check(status)
    return pmin, cmin
end

# Default parameter scaling factors.
function defaultscale(p0::AbstractVector{<:Real})
    n = length(p0)
    scl = Array{Cdouble}(undef, n)
    sml = sqrt(eps(Cdouble))
    @inbounds for i in 1:n
        scl[i] = max(abs(p0[i]), sml)
    end
    return scl
end

# Copy parameters so that they are writable and compatible with NEWUOA.
copyparameters(p0::AbstractVector{<:Real}) =
    copyto!(Array{Cdouble}(undef, length(p0)), p0)

# Check NEWUOA status.
function check(status::Newuoa.Status)
    if status != Newuoa.SUCCESS
        msg = Newuoa.getreason(status)
        if (status == Newuoa.ROUNDING_ERRORS ||
            status == Newuoa.TOO_MANY_EVALUATIONS)
            @warn msg
        else
            error(msg)
        end
    end
end

# Objective functions.

function _nllsq(y::AbstractArray{<:Real,N},
                f::Function,
                p::AbstractVector{<:Real},
                x) where {N}
    m = f(p, x)
    local err::Cdouble = 0
    @inbounds @simd for i in all_indices(m, y)
        mi, yi = m[i], y[i]
        err += (yi - mi)^2
    end
    return err
end

function _nllsq(w::AbstractArray{<:Real,N},
                y::AbstractArray{<:Real,N},
                f::Function,
                p::AbstractVector{<:Real},
                x) where {N}
    m = f(p, x)
    local err::Cdouble = 0
    @inbounds @simd for i in all_indices(m, w, y)
        mi, wi, yi = m[i], w[i], y[i]
        err += wi*(yi - mi)^2
    end
    return err
end

end

ext: jl


text:
"""
Extension of the `Base.show` function in order to print all the `AbstractParameter`s within a `ParameterGroup`, including the description in a readable format.
"""

function Base.show(io::IO, x::ParameterGroup)
    s1 = maximum(length.(string.(fieldnames(typeof(x)), "Variable")))
    vals = Dict{Symbol,Any}()
    desc = Dict{Symbol,Any}()
    for name in fieldnames(typeof(x))
        if typeof(getfield(x, name)) <: AbstractParameter
            if typeof(getfield(getfield(x, name), :value)) <: Real
                vals[name] = round.(getfield(getfield(x, name), :value), digits=3)
                desc[name] = getfield(getfield(x, name), :description)
            else
                vals[name] = getfield(getfield(x, name), :value)
                desc[name] = getfield(getfield(x, name), :description)
            end
        else
            vals[name] = getfield(x, name)
            desc[name] = "No Description"
        end
    end
    s2 = maximum(length.(string.(values(desc))))
    s3 = length.(string.("Values      "))
    s3 = s3 + s2 + s1
    print(io, "$(rpad("Variable",s1," ")) | ")
    print(io, "$(rpad("Description",s2," ")) | ")
    print(io, "Value\n")
    print(io, rpad("-", s3, "-"), "\n")
    for name in fieldnames(typeof(x))
        if typeof(getfield(x, name)) <: AbstractParameter
            print(io, rpad(name, s1, " "), " | ")
            print(io, "$(rpad(getfield(getfield(x,name),:description),s2," ")) | ")
            print(IOContext(io, :compact => true, :displaysize => (1, 2), :limit => true), vals[name], "\n")
        else
            print(io, rpad(name, s1, " "), " | ")
            print(io, "$(rpad("No Description",s2," ")) | ")
            print(IOContext(io, :compact => true, :displaysize => (1, 2), :limit => true), vals[name], "\n")
        end
    end
end
function Base.show(io::IO, ::MIME"text/plain", x::ParameterGroup)
    s1 = maximum(length.(string.(fieldnames(typeof(x)), "Variable")))
    vals = Dict{Symbol,Any}()
    desc = Dict{Symbol,Any}()
    for name in fieldnames(typeof(x))
        if typeof(getfield(x, name)) <: AbstractParameter
            if typeof(getfield(getfield(x, name), :value)) <: Real
                vals[name] = round.(getfield(getfield(x, name), :value), digits=3)
                desc[name] = getfield(getfield(x, name), :description)
            else
                vals[name] = getfield(getfield(x, name), :value)
                desc[name] = getfield(getfield(x, name), :description)
            end
        else
            vals[name] = getfield(x, name)
            desc[name] = "No Description"
        end
    end
    s2 = maximum(length.(string.(values(desc))))
    s3 = length.(string.("Values      "))
    s3 = s3 + s2 + s1
    print(io, "$(rpad("Variable",s1," ")) | ")
    print(io, "$(rpad("Description",s2," ")) | ")
    print(io, "Value\n")
    print(io, rpad("-", s3, "-"), "\n")
    for name in fieldnames(typeof(x))
        if typeof(getfield(x, name)) <: AbstractParameter
            print(io, rpad(name, s1, " "), " | ")
            print(io, "$(rpad(getfield(getfield(x,name),:description),s2," ")) | ")
            print(IOContext(io, :compact => true, :displaysize => (1, 2), :limit => true), vals[name], "\n")
        else
            print(io, rpad(name, s1, " "), " | ")
            print(io, "$(rpad("No Description",s2," ")) | ")
            print(IOContext(io, :compact => true, :displaysize => (1, 2), :limit => true), vals[name], "\n")
        end
    end
end
"""
Extension of the `Base.show` function in order to print all the `ParameterGroup`s, including the description in a readable format.
"""
function Base.show(io::IO, ::MIME"text/plain", x::ParameterSpace)
    for str in fieldnames(typeof(x))
        println(io, "Parameters for ", typeof(getfield(x, str)), ":")
        println(io, getfield(x, str))
    end
end
function Base.show(io::IO, x::ParameterSpace)
    for str in fieldnames(typeof(x))
        println(io, "Parameters for ", typeof(getfield(x, str)), ":")
        println(io, getfield(x, str))
    end
end


text:
using BioinformaticsBISC195
using Test

@testset "BioinformaticsBISC195" begin

@testset "Using Strings" begin

    @testset "normalizeDNA" begin
        @test normalizeDNA("aatgn") == "AATGN"
        @test_throws Exception normalizeDNA("ZCA")
        @test_throws Exception normalizeDNA(42)
        c = normalizeDNA('C')
        @test c == "C"
        @test typeof(c) == String
    end # normalizeDNA

    @testset "composition" begin
        @test composition("BAGGGRRRR") == Dict{Any, Any}('A' => 1, 'G' => 3, 'N' => 5)
        @test composition("BAGGGRRRR") isa Dict
        @test_throws Exception composition("5")
    end # composition

    @testset "gc_content" begin
        @test gc_content("ANTGA") == 0.25 #update method to only count valid bases
        @test gc_content("cccggg") * 100 == 100.0
        @test gc_content("ATta") == 0.0
        @test_throws Exception gc_content("ATtU")
    end # gc_content

    @testset "complement" begin
        @test complement("ATTAN") == "TAATN"
        @test complement("gcta") == "CGAT"
        @test complement("nnnnnnn") == "NNNNNNN"
        @test_throws Exception complement("APP")
    end # complement

    @testset "reverse_complement" begin
        @test reverse_complement("ATTAN") == "NTAAT"
        @test reverse_complement("gcta") == "TAGC"
        @test reverse_complement("nnnnnnn") == "NNNNNNN"
        @test_throws Exception reverse_complement("AEC")
    end # reverse_complement

    @testset "parse_fasta" begin
        testpath = normpath(joinpath(@__DIR__, "..", "data"))
        genomes = joinpath(testpath, "cov2_genomes.fasta")
        ex1_path = joinpath(testpath, "ex1.fasta")
        ex2_path = joinpath(testpath, "ex2.fasta")

        ex1 = parse_fasta(ex1_path)
        @test ex1 isa Tuple
        @test all(x-> x isa String, ex1[1])
        @test all(x-> x isa String, ex1[2])

        @test ex1[1] == ["ex1.1 | easy", "ex1.2 | multiline"]
        @test ex1[2] == ["AATTATAGC", "CGCCCCCCAGTCGGATT"]

        @test_throws Exception parse_fasta(ex2_path)

        cov2 = parse_fasta(genomes)
        @test length(cov2[1]) == 8
        @test length(cov2[2]) == 8
    end #parse_fasta

    @testset "uniqueKmers" begin
        @test uniqueKmers("GCGCAT",2) isa Set{String}

        @test uniqueKmers("ACT", 2) == Set(["AC", "CT"])
        @test uniqueKmers("ATC", 2) == Set(["AT", "TC"])
        @test uniqueKmers("ATGCGATG", 4) ==  Set(["TGCG", "ATGC", "GATG", "CGAT", "GCGA"])

        @test_throws Exception uniqueKmers("XAQ", 2)
    end

    @testset "kmerdist" begin
        @test kmerdist(uniqueKmers("GCGCAT",2), uniqueKmers("ATAT",2)) isa Float64

        @test kmerdist(uniqueKmers("GCGCAT",2), uniqueKmers("ATAT",2)) == 0.8
        @test kmerdist(uniqueKmers("ATCGATG",2), uniqueKmers("GCATACC",2)) == 0.9

        @test_throws Exception kmerdist(uniqueKmers("AZCGLTG",2), uniqueKmers("GCATACC",2))
    end

    @testset "kmertime" begin
        testpath = normpath(joinpath(@__DIR__, "..", "data"))
        genomes = joinpath(testpath, "cov2_genomes.fasta")
        ex1_path = joinpath(testpath, "ex1.fasta")
        ex2_path = joinpath(testpath, "ex2.fasta")
        datatry_path= joinpath(testpath, "datatry.fasta")
        ex1 = kmertime(parse_fasta(ex1_path)[1], parse_fasta(ex1_path)[2], 3)

        @test ex1 isa Tuple
        @test all(x-> x isa String, ex1[1])
        @test all(x-> x isa String, ex1[2])

        datatry= kmertime(parse_fasta(datatry_path)[1], parse_fasta(datatry_path)[2], 2)
        @test datatry== (Any[Set(["AC", "CT"])], Any[Set(["CC", "GC", "GG", "CG", "AT", "CA", "TG", "TA", "GT", "GA", "TT", "AC", "CT", "AA", "AG", "TC"])], Any[Set(["AG", "GG", "GT"])])
    end

    @testset "kmertimes" begin
        testpath = normpath(joinpath(@__DIR__, "..", "data"))
        ex1_path = joinpath(testpath, "ex1.fasta")
        headers, sequences = parse_fasta("../data/refined_data.fasta");

        @test kmertimes(headers, sequences) isa Tuple{Int64, Int64, Int64}
        @test kmertimes(headers, sequences) == (64, 73, 95)
        @test_throws Exception kmertimes(parse_fasta(ex1_path)[1], parse_fasta(ex1_path)[2])
    end

    @testset "pairdist" begin
        testpath = normpath(joinpath(@__DIR__, "..", "data"))
        refine_path = joinpath(testpath, "refined_data.fasta")
        ex2_path = joinpath(testpath, "ex2.fasta")

        @test pairdist(refine_path) isa Matrix{Float64}
        @test pairdist(refine_path)[35, 1] == 0.189873417721519
        @test_throws Exception pairdist(ex2_path)

    end

    @testset "distsort" begin
        testpath = normpath(joinpath(@__DIR__, "..", "data"))
        refine_path = pairdist(joinpath(testpath, "refined_data.fasta"))
        datasort_path = joinpath(testpath, "datasort.fasta")

        @test distsort(refine_path) isa Tuple{Vector{Any}, Vector{Any}, Vector{Any}}
        @test distsort(refine_path)[2][138] == 0.12328767123287676
        @test_throws Exception distsort(datasort_path)
    end
end
end


text:
f(x) = begin
        x2 = x ^ 2
        x * Main.muladd(x2, Main.muladd(x2, 0.008333333333333333, -0.16666666666666666), 1.0)
    end

max_stars_repo_name: genkuroki/public

max_issues_repo_name: genkuroki/public

max_forks_repo_name: genkuroki/public


text:
# using StaticArrays: later package all types as static/mutable fixed size arrays/matrices

"""
Definition of abstract spatial types to be used across the package
"""
# todo: make types parametrized: {N(dims), T(type)}
# todo: look for packages to depend on: GeometryTypes
# todo: quaternions?
abstract type AbstractPoint <: AbstractVector{Float64} end   # coords
abstract type AbstractFrame <: AbstractVector{Float64} end   # location vec, orientation vec
abstract type AbstractPose <: AbstractVector{Float64} end    # transl & rot components

abstract type AbstractTransform <: AbstractMatrix{Float64} end
abstract type SO2 <: AbstractTransform end
abstract type SE2 <: AbstractTransform end
abstract type SO3 <: AbstractTransform end
abstract type SE3 <: AbstractTransform end


text:
# PREAMBLE

# PKG_SETUP

# ## Setup

using RoboDojo

# ## Initial conditions
q1 = nominal_configuration(halfcheetah4)
v1 = zeros(halfcheetah4.nq)
q1[2] += 0.25
q1[3] += 0.0 * π

# ## Time
h = 0.01
T = 1000

# ## policy

# ## Simulator
s = Simulator(halfcheetah4, T, h=h)

# ## Simulate
simulate!(s, q1, v1)

# ## Visualizer
vis = Visualizer()
render(vis)

# ## Visualize
visualize!(vis, s)

# ## simulate policy
d = Simulator(halfcheetah4, 1, h=h)

x_hist = [[q1; q1 - v1 * h]]
u_hist = Vector{Float64}[]

for t = 1:T
    push!(u_hist, [0;0;0; 5.0e1 * randn(6)])
    y = zeros(2 * halfcheetah4.nq)
    dynamics(d, y, x_hist[end], u_hist[end], zeros(0))
    push!(x_hist, y)
end

s = Simulator(halfcheetah4, T, h=h)
for i = 1:T
    q = x_hist[i][1:halfcheetah4.nq]
    v = x_hist[i][halfcheetah4.nq .+ (1:halfcheetah4.nq)]
    set_state!(s, q, v, i)
end
visualize!(vis, s)

u_hist


text:
module OneHot

using Random
using Base: tail, front

export OneHotArray

include("array.jl")
include("util.jl")

end # module


text:
using ADCME
using PyCall
using LinearAlgebra
using PyPlot
using Random
using SparseArrays
Random.seed!(233)

function push_matrices(ii1,jj1,vv1,ii2,jj2,vv2,d)
    push_matrices_ = load_op_and_grad("./build/libPushMatrices","push_matrices")
    ii1,jj1,vv1,ii2,jj2,vv2,d = convert_to_tensor([ii1,jj1,vv1,ii2,jj2,vv2,d], [Int64,Int64,Float64,Int64,Int64,Float64,Int64])
    push_matrices_(ii1,jj1,vv1,ii2,jj2,vv2,d)
end

function visco_solve(rhs,vv, op)
    visco_solve_ = load_op_and_grad("./build/libViscoSolve","visco_solve")
    rhs,vv, op = convert_to_tensor([rhs,vv, op], [Float64,Float64, Int64])
    visco_solve_(rhs,vv, op)
end

A = sprand(10,10,0.7)
B = sprand(10,10,0.2)
A = A + B
rhs = rand(10)
op = push_matrices(find(constant(A))..., find(constant(B))..., 10)
ii, jj, vv = find(constant(B))

sol = visco_solve(rhs, vv, op)

sess = Session(); init(sess)

# @show run(sess, sol)
# @show A\rhs - run(sess, sol)

# uncomment it for testing gradients
# error()

# TODO: change your test parameter to `m`
#       in the case of `multiple=true`, you also need to specify which component you are testings
# gradient check -- v
Al = sprand(10,10,0.7)
function scalar_function(vv)
    A = constant(Al)
    B = SparseTensor(ii, jj, vv, 10, 10)
    A = A + B
    op = independent(push_matrices(find(A)..., find(B)..., 10))

    return sum(visco_solve(rhs, vv, op)^2)
end

# TODO: change `m_` and `v_` to appropriate values
# m_ = constant(rand(length(rhs)))
# v_ = rand(length(rhs))

m_ = constant(rand(length(vv)))
v_ = rand(length(vv))
y_ = scalar_function(m_)
dy_ = constant(run(sess, gradients(y_, m_)))
ms_ = Array{Any}(undef, 5)
ys_ = Array{Any}(undef, 5)
s_ = Array{Any}(undef, 5)
w_ = Array{Any}(undef, 5)
gs_ =  @. 1 / 10^(1:5)

for i = 1:5
    g_ = gs_[i]
    ms_[i] = m_ + g_*v_
    ys_[i] = scalar_function(ms_[i])
    s_[i] = ys_[i] - y_
    w_[i] = s_[i] - g_*sum(v_.*dy_)
end

sess = Session(); init(sess)
sval_ = run(sess, s_)
wval_ = run(sess, w_)
close("all")
loglog(gs_, abs.(sval_), "*-", label="finite difference")
loglog(gs_, abs.(wval_), "+-", label="automatic differentiation")
loglog(gs_, gs_.^2 * 0.5*abs(wval_[1])/gs_[1]^2, "--",label="\$\\mathcal{O}(\\gamma^2)\$")
loglog(gs_, gs_ * 0.5*abs(sval_[1])/gs_[1], "--",label="\$\\mathcal{O}(\\gamma)\$")

plt.gca().invert_xaxis()
legend()
xlabel("\$\\gamma\$")
ylabel("Error")

max_stars_repo_name: ADCMEMarket/ADCMEImages

max_issues_repo_name: ADCMEMarket/ADCMEImages

max_forks_repo_name: ADCMEMarket/ADCMEImages


text:
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

# What is included in the file `FMI3_fmu_to_md.jl` (FMU to model description)?
# - wrappers to call the model description functions from a FMU-instance [exported]

function fmi3GetModelName(fmu::FMU3)
    fmi3GetModelName(fmu.modelDescription)
end

function fmi3GetInstantiationToken(fmu::FMU3)
    fmi3GetInstantiationToken(fmu.modelDescription)
end

function fmi3GetGenerationTool(fmu::FMU3)
    fmi3GetGenerationTool(fmu.modelDescription)
end

function fmi3GetGenerationDateAndTime(fmu::FMU3)
    fmi3GetGenerationDateAndTime(fmu.modelDescription)
end

function fmi3GetVariableNamingConvention(fmu::FMU3)
    fmi3GetVariableNamingConvention(fmu.modelDescription)
end

function fmi3CanGetSetState(fmu::FMU3)
    fmi3CanGetSetState(fmu.modelDescription)
end

function fmi3CanSerializeFMUstate(fmu::FMU3)
    fmi3CanSerializeFMUstate(fmu.modelDescription)
end

function fmi3ProvidesDirectionalDerivative(fmu::FMU3)
    fmi3ProvidesDirectionalDerivative(fmu.modelDescription)
end

function fmi3ProvidesAdjointDerivative(fmu::FMU3)
    fmi3ProvidesAdjointDerivative(fmu.modelDescription)
end

function fmi3IsCoSimulation(fmu::FMU3)
    fmi3IsCoSimulation(fmu.modelDescription)
end

function fmi3IsModelExchange(fmu::FMU3)
    fmi3IsModelExchange(fmu.modelDescription)
end

function fmi3IsScheduledExecution(fmu::FMU3)
    fmi3IsScheduledExecution(fmu.modelDescription)
end


text:
using TensorToolbox
using LinearAlgebra
using Arpack

function HOSVD(T, reqrank)
    X = hosvd(T, reqrank=reqrank)
    g = X.cten
    Us = X.fmat
    X = ttm(g, [Us...])
    return X
end

"""
input is real
"""
function STHOSVD(T, reqrank)
    N = ndims(T)
    tensor_shape = size(T)
    for i = 1 : N
        T_i = tenmat(T, i)
        if reqrank[i] == tensor_shape[i]
            USV = svd(T_i)
        else
            USV = svds(T_i; nsv=reqrank[i] )[1]
        end
        T = ttm( T, USV.U * USV.U', i)
    end
	return T
end

max_stars_repo_name: gkazunii/Legendre-tucker-rank-reduction

max_issues_repo_name: gkazunii/Legendre-tucker-rank-reduction

max_forks_repo_name: gkazunii/Legendre-tucker-rank-reduction