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

module FMI

@debug "Debugging messages enabled for FMI.jl ..."

using Requires

using FMIImport
import FMIImport: fmi2CallbackLogger, fmi2CallbackAllocateMemory, fmi2CallbackFreeMemory, fmi2CallbackStepFinished
import FMIImport: fmi2ComponentState, fmi2ComponentStateInstantiated, fmi2ComponentStateInitializationMode, fmi2ComponentStateEventMode, fmi2ComponentStateContinuousTimeMode, fmi2ComponentStateTerminated, fmi2ComponentStateError, fmi2ComponentStateFatal
import FMIImport: fmi2Instantiate, fmi2FreeInstance!, fmi2GetTypesPlatform, fmi2GetVersion
import FMIImport: fmi2SetDebugLogging, fmi2SetupExperiment, fmi2EnterInitializationMode, fmi2ExitInitializationMode, fmi2Terminate, fmi2Reset
import FMIImport: fmi2GetReal!, fmi2SetReal, fmi2GetInteger!, fmi2SetInteger, fmi2GetBoolean!, fmi2SetBoolean, fmi2GetString!, fmi2SetString
import FMIImport: fmi2GetFMUstate!, fmi2SetFMUstate, fmi2FreeFMUstate!, fmi2SerializedFMUstateSize!, fmi2SerializeFMUstate!, fmi2DeSerializeFMUstate!
import FMIImport: fmi2GetDirectionalDerivative!, fmi2SetRealInputDerivatives, fmi2GetRealOutputDerivatives
import FMIImport: fmi2DoStep, fmi2CancelStep, fmi2GetStatus!, fmi2GetRealStatus!, fmi2GetIntegerStatus!, fmi2GetBooleanStatus!, fmi2GetStringStatus!
import FMIImport: fmi2SetTime, fmi2SetContinuousStates, fmi2EnterEventMode, fmi2NewDiscreteStates, fmi2EnterContinuousTimeMode, fmi2CompletedIntegratorStep!
import FMIImport: fmi2GetDerivatives, fmi2GetEventIndicators, fmi2GetContinuousStates, fmi2GetNominalsOfContinuousStates
import FMIImport: fmi2StringToValueReference, fmi2ValueReferenceToString, fmi2ModelVariablesForValueReference
import FMIImport: fmi2GetReal, fmi2GetInteger, fmi2GetString, fmi2GetBoolean
import FMIImport: fmi2GetFMUstate, fmi2SerializedFMUstateSize, fmi2SerializeFMUstate, fmi2DeSerializeFMUstate
import FMIImport: fmi2GetDirectionalDerivative
import FMIImport: fmi2GetStartValue, fmi2SampleDirectionalDerivative, fmi2CompletedIntegratorStep
import FMIImport: fmi2Unzip, fmi2Load, loadBinary, fmi2Reload, fmi2Unload, fmi2Instantiate!
import FMIImport: fmi2SampleDirectionalDerivative!
import FMIImport: fmi2GetJacobian, fmi2GetJacobian!, fmi2GetFullJacobian, fmi2GetFullJacobian!
import FMIImport: fmi2LoadModelDescription
import FMIImport: fmi2GetDefaultStartTime, fmi2GetDefaultStopTime, fmi2GetDefaultTolerance, fmi2GetDefaultStepSize
import FMIImport: fmi2GetModelName, fmi2GetGUID, fmi2GetGenerationTool, fmi2GetGenerationDateAndTime, fmi2GetVariableNamingConvention, fmi2GetNumberOfEventIndicators, fmi2GetNumberOfStates, fmi2IsCoSimulation, fmi2IsModelExchange
import FMIImport: fmi2DependenciesSupported, fmi2GetModelIdentifier, fmi2CanGetSetState, fmi2CanSerializeFMUstate, fmi2ProvidesDirectionalDerivative
import FMIImport: fmi2Get, fmi2Get!, fmi2Set
import FMIImport: fmi2GetSolutionTime, fmi2GetSolutionState, fmi2GetSolutionValue
export fmi2GetSolutionTime, fmi2GetSolutionState, fmi2GetSolutionValue

using FMIExport
using FMIExport: fmi2Create, fmi2CreateSimple

using FMIImport.FMICore: fmi2ValueReference, fmi3ValueReference
using FMIImport: fmi2ValueReferenceFormat, fmi3ValueReferenceFormat, fmi2StructMD, fmi3StructMD, fmi2Struct, fmi3Struct

using FMIImport.FMICore: FMU2, FMU3, FMU2Component, FMU3Component
export FMU2, FMU3, FMU2Component, FMU3Component

using FMIImport.FMICore: FMU2ExecutionConfiguration, FMU_EXECUTION_CONFIGURATION_RESET, FMU_EXECUTION_CONFIGURATION_NO_RESET, FMU_EXECUTION_CONFIGURATION_NO_FREEING
export FMU2ExecutionConfiguration, FMU_EXECUTION_CONFIGURATION_RESET, FMU_EXECUTION_CONFIGURATION_NO_RESET, FMU_EXECUTION_CONFIGURATION_NO_FREEING

using FMIImport: prepareValue, prepareValueReference

include("FMI1_additional.jl")
include("FMI2_additional.jl")
include("FMI3_additional.jl")
include("assertions.jl")

include("FMI1_comp_wraps.jl")
include("FMI2_comp_wraps.jl")
include("FMI3_comp_wraps.jl")

include("FMI2_sim.jl")
include("FMI3_sim.jl")

function fmiPlot(solution::FMU2Solution; kwargs...)
    @warn "fmiPlot(...) needs `Plots` package. Please do `using Plots` or `import Plots`."
end
function fmiPlot!(fig, solution::FMU2Solution; kwargs...)
    @warn "fmiPlot!(...) needs `Plots` package. Please do `using Plots` or `import Plots`."
end
function __init__()
    @require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" begin
        import .Plots
        include("FMI2_plot.jl")
        include("FMI3_plot.jl")
    end
end
export fmiPlot, fmiPlot!

### EXPORTING LISTS START ###

# FMI.jl
export fmiLoad, fmiReload, fmiSimulate, fmiSimulateCS, fmiSimulateME, fmiUnload
export fmiGetNumberOfStates, fmiGetTypesPlatform, fmiGetVersion, fmiInstantiate!, fmiFreeInstance!
export fmiSetDebugLogging, fmiSetupExperiment, fmiEnterInitializationMode, fmiExitInitializationMode, fmiTerminate , fmiReset
export fmiGetReal, fmiSetReal, fmiGetInteger, fmiSetInteger, fmiGetBoolean, fmiSetBoolean, fmiGetString, fmiSetString, fmiGetReal!, fmiGetInteger!, fmiGetBoolean!, fmiGetString!
export fmiSetRealInputDerivatives, fmiGetRealOutputDerivatives
export fmiGetFMUstate, fmiSetFMUstate, fmiFreeFMUstate!, fmiSerializedFMUstateSize, fmiSerializeFMUstate, fmiDeSerializeFMUstate
export fmiGetDirectionalDerivative, fmiSampleDirectionalDerivative, fmiGetDirectionalDerivative!, fmiSampleDirectionalDerivative!
export fmiDoStep, fmiSetTime, fmiSetContinuousStates, fmiEnterEventMode, fmiNewDiscreteStates
export fmiEnterContinuousTimeMode, fmiCompletedIntegratorStep, fmiGetDerivatives, fmiGetEventIndicators, fmiGetContinuousStates, fmiGetNominalsOfContinuousStates
export fmiInfo
export fmiGetModelName, fmiGetGUID, fmiGetGenerationTool, fmiGetGenerationDateAndTime
export fmiGetVariableNamingConvention, fmiGetNumberOfEventIndicators
export fmiCanGetSetState, fmiCanSerializeFMUstate
export fmiProvidesDirectionalDerivative
export fmiIsCoSimulation, fmiIsModelExchange
export fmiGetDependencies
export fmiGetStartValue
export fmiSimulate, fmiSimulateCS, fmiSimulateME
export fmiGet, fmiGet!, fmiSet
export fmiGetSolutionTime, fmiGetSolutionState, fmiGetSolutionValue

export fmiSetFctGetTypesPlatform, fmiSetFctGetVersion
export fmiSetFctInstantiate, fmiSetFctFreeInstance, fmiSetFctSetDebugLogging, fmiSetFctSetupExperiment, fmiSetEnterInitializationMode, fmiSetFctExitInitializationMode
export fmiSetFctTerminate, fmiSetFctReset
export fmiSetFctGetReal, fmiSetFctGetInteger, fmiSetFctGetBoolean, fmiSetFctGetString, fmiSetFctSetReal, fmiSetFctSetInteger, fmiSetFctSetBoolean, fmiSetFctSetString
export fmiSetFctSetTime, fmiSetFctSetContinuousStates, fmiSetFctEnterEventMode, fmiSetFctNewDiscreteStates, fmiSetFctEnterContinuousTimeMode, fmiSetFctCompletedIntegratorStep
export fmiSetFctGetDerivatives, fmiSetFctGetEventIndicators, fmiSetFctGetContinuousStates, fmiSetFctGetNominalsOfContinuousStates

### EXPORTING LISTS END ###

"""

    fmiGetDependencies(fmu::FMU2)

Building dependency matrix $(dim) x $(dim) for fast look-ups on variable dependencies.

#Arguments
- `fmu::FMU2`: Mutable Struct representing a FMU in [FMI Standard Version 2.0.2](https://fmi-standard.org/).

See also [`fmi2GetDependencies`](@ref), [`FMU2`](@ref).
"""
function fmiGetDependencies(fmu::FMU2)
    fmi2GetDependencies(fmu)
end

"""

    fmiStringToValueReference(dataStruct::Union{FMU2, fmi2ModelDescription, FMU3, fmmi3ModelDescription}, identifier::Union{String, Array{String}})

Returns the ValueReference coresponding to the variable identifier.

# Arguments
- `dataStruct::Union{FMU2, fmi2ModelDescription, FMU3, fmmi3ModelDescription}`: Model of the type FMU2/FMU3 or the Model Description of fmi2/fmi3. Same for Model of type FMU3 or the Model Description of fmi3
- `identifier::Union{String, Array{String}}`: Variable identifier in type String or as a 1-dimensional Array containing elements of type String

See also [`fmi2StringToValueReference`](@ref), [`fmi3StringToValueReference`](@ref).
"""
function fmiStringToValueReference(dataStruct::Union{FMU2, fmi2ModelDescription}, identifier::Union{String, Array{String}})
    fmi2StringToValueReference(dataStruct, identifier)
end
function fmiStringToValueReference(dataStruct::Union{FMU3, fmi3ModelDescription}, identifier::Union{String, Array{String}})
    fmi3StringToValueReference(dataStruct, identifier)
end

# Wrapping modelDescription Functions
"""

    fmiGetModelName(str::Union{fmi2StructMD, fmi3StructMD})

Returns the tag 'modelName' from the model description.

# Arguments
- `str::Union{fmi2StructMD, fmi3StructMD}`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
 `Union{fmi2StructMD, fmi3StructMD} = Union{FMU2, FMU2Component, fmi2ModelDescription, FMU3, FMU3Component, fmi3ModelDescription}`
  - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
  - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
  - `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.
  - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
  - `str::FMU3Component`:  Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
  - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.

See also [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`FMU3`](@ref), [`FMU3Component`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiGetModelName(str::fmi2StructMD)
    fmi2GetModelName(str)
end
function fmiGetModelName(str::fmi3StructMD)
    fmi3GetModelName(str)
end

"""

    fmiGetGUID(str::fmi2StructMD)

Returns the tag 'guid' from the model description.

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/). More detailed:  `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.

See also [`fmi2GetGUID`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiGetGUID(str::fmi2StructMD)
    fmi2GetGUID(str)
end

"""

    fmiGetGenerationTool(str::fmi2StructMD)

Returns the tag 'generationtool' from the model description.

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 More detailed:  `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.

# Returns
- `str.generationtool`: The function `fmi2GetGenerationTool` returns the tag 'generationtool' from the struct, representing a FMU (`str`).

See also [`fmi2GetGenerationTool`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).

"""
function fmiGetGenerationTool(str::fmi2StructMD)
    fmi2GetGenerationTool(str)
end

"""

    fmiGetGenerationDateAndTime(str::fmi2StructMD)

Returns the tag 'generationdateandtime' from the model description.

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 More detailed:  `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
- `str.generationDateAndTime`: The function `fmi2GetGenerationDateAndTime` returns the tag 'generationDateAndTime' from the struct, representing a FMU (`str`).

See also [`fmi2GetGenerationDateAndTime`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).

"""
function fmiGetGenerationDateAndTime(str::fmi2StructMD)
    fmi2GetGenerationDateAndTime(str)
end

"""

    fmiGetVariableNamingConvention(str::fmi2StructMD)

Returns the tag 'varaiblenamingconvention' from the model description.

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 More detailed:  `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
- `str.variableNamingConvention`: The function `fmi2GetVariableNamingConvention` returns the tag 'variableNamingConvention' from the struct, representing a FMU (`str`).

See also [`fmi2GetVariableNamingConvention`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiGetVariableNamingConvention(str::fmi2StructMD)
    fmi2GetVariableNamingConvention(str)
end

"""

    fmiGetNumberOfEventIndicators(str::fmi2StructMD)

Returns the tag 'numberOfEventIndicators' from the model description.

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
- `str.numberOfEventIndicators`: The function `fmi2GetNumberOfEventIndicators` returns the tag 'numberOfEventIndicators' from the struct, representing a FMU (`str`).

See also [`fmi2GetNumberOfEventIndicators`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).

"""
function fmiGetNumberOfEventIndicators(str::fmi2StructMD)
    fmi2GetNumberOfEventIndicators(str)
end

"""

    fmiGetModelIdentifier(fmu::FMU2)

Returns the tag 'modelIdentifier' from CS or ME section.

# Arguments
 - `fmu::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).

# Returns
- `fmu.modelDescription.coSimulation.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'coSimulation.modelIdentifier' from the model description of the FMU2-struct (`fmu.modelDescription`), if the FMU supports co simulation.
- `fmu.modelDescription.modelExchange.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'modelExchange.modelIdentifier'  from the model description of the FMU2-struct (`fmu.modelDescription`), if the FMU supports model exchange

Also see [`fmi2GetModelIdentifier`](@ref), [`FMU2`](@ref).

"""
function fmiGetModelIdentifier(fmu::FMU2)
    fmi2GetModelIdentifier(fmu.modelDescription; type=fmu.type)
end

"""

    fmiCanGetSetState(str::fmi2StructMD)

Returns true, if the FMU supports the getting/setting of states

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
 - `::Bool`: The function `fmi2CanGetSetState` returns True, if the FMU supports the getting/setting of states.

See also [`fmi2CanGetSetState`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiCanGetSetState(str::fmi2StructMD)
    fmi2CanGetSetState(str)
end

"""

    fmiCanSerializeFMUstate(str::fmi2StructMD)

Returns true, if the FMU state can be serialized

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.

# Returns
 - `::Bool`: The function `fmi2CanSerializeFMUstate` returns True, if the FMU state can be serialized.

See also [`fmi2CanSerializeFMUstate`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiCanSerializeFMUstate(str::fmi2StructMD)
    fmi2CanSerializeFMUstate(str)
end

"""

    fmiProvidesDirectionalDerivative(str::fmi2StructMD)

Returns true, if the FMU provides directional derivatives

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
- `::Bool`: The function `fmi2ProvidesDirectionalDerivative` returns True, if the FMU provides directional derivatives.

See also [`fmi2ProvidesDirectionalDerivative`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiProvidesDirectionalDerivative(str::fmi2StructMD)
    fmi2ProvidesDirectionalDerivative(str)
end

"""

    fmiIsCoSimulation(str::fmi2StructMD)

Returns true, if the FMU supports co simulation

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
 - `::Bool`: The function `fmi2IsCoSimulation` returns True, if the FMU supports co simulation

See also [`fmi2IsCoSimulation`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiIsCoSimulation(str::fmi2StructMD)
    fmi2IsCoSimulation(str)
end

"""

    fmiIsModelExchange(str::fmi2StructMD)

Returns true, if the FMU supports model exchange

# Arguments
- `str::fmi2StructMD`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2StructMD =  Union{FMU2, FMU2Component, fmi2ModelDescription}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.

# Returns
 - `::Bool`: The function `fmi2IsCoSimulation` returns True, if the FMU supports model exchange.

See also [`fmi2IsModelExchange`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiIsModelExchange(str::fmi2StructMD)
    fmi2IsModelExchange(str)
end

# Multiple Dispatch variants for FMUs with version 2.0.X

"""

   fmiLoad(pathToFMU::String; unpackPath=nothing, type=nothing)

Load FMUs independent of the FMI version, currently supporting version 2.0.X.

# Arguments
- `pathToFMU::String`: String that contains the paths of ziped and unziped FMU folders.

# Keywords
- `unpackPath=nothing`: Via optional argument ```unpackPath```, a path to unpack the FMU can be specified (default: system temporary directory).
- `type::Union{CS, ME} = nothing`:  Via ```type```, a FMU type can be selected. If none of the unified type set is used, the default value `type = nothing` will be used.

# Returns
- Returns the instance of the FMU struct.

See also [`fmi2Load`](@ref).
"""
function fmiLoad(args...; kwargs...)
    fmi2Load(args...; kwargs...)
end

"""

    fmiReload(fmu::FMU2)

Reloads the FMU-binary. This is useful, if the FMU does not support a clean reset implementation.

# Arguments
- `fmu::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).

See also [`fmi2Reload`](@ref).
"""
function fmiReload(fmu::FMU2, args...; kwargs...)
    fmi2Reload(fmu, args...; kwargs...)
end

"""

    fmiSimulate(str::fmi2Struct, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
                tolerance::Union{Real, Nothing} = nothing,
                dt::Union{Real, Nothing} = nothing,
                solver = nothing,
                customFx = nothing,
                recordValues::fmi2ValueReferenceFormat = nothing,
                saveat = [],
                setup::Bool = true,
                reset::Union{Bool, Nothing} = nothing, # nothing = auto
                inputValueReferences::fmi2ValueReferenceFormat = nothing,
                inputFunction = nothing,
                parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing,
                dtmax::Union{Real, Nothing} = nothing,
                kwargs...)

Starts a simulation of the FMU instance for the matching FMU type, if both types are available, CS is preferred.

# Arguments
- - `str::fmi2Struct`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
- `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.

# Keywords
- `tolerance::Union{Real, Nothing} = nothing`: Real number to set the tolerance for any OED-solver
- `dt::Union{Real, Nothing} = nothing`: Real number to set the step size of the OED-solver. Defaults to an automatic choice if the method is adaptive. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control)
- `solver = nothing`: Any Julia-supported OED-solver  (default is Tsit5). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/solvers/ode_solve/#ode_solve)
- `customFx = nothing`: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t)
- `recordValues::fmi2ValueReferenceFormat = nothing`: Array of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues`
- `saveat = []`: Time points to save values at (interpolated). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Output-Control)
- `setup::Bool = true`: Boolean, if FMU should be setup (default: setup=true)
- `reset::Union{Bool, Nothing} = nothing`: Boolean, if FMU should be reset before simulation (default: reset:=auto)
- `inputValueReferences::fmi2ValueReferenceFormat = nothing`: Array of input variables (strings or variableIdentifiers) to set at every simulation step
- `inputFunction = nothing`: Function to retrieve the values to set the inputs to
- `parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing`: Dictionary of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization
- `dtmax::Union{Real, Nothing} = nothing`: Real number for setting maximum dt for adaptive timestepping for the ODE solver. The default values are package dependent. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control)
- `kwargs...`: Further parameters of already defined functions `solve(args..., kwargs...)` from the library [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/#DifferentialEquations.jl:-Scientific-Machine-Learning-(SciML)-Enabled-Simulation-and-Estimation)

# Returns
- `success::Bool` for CS-FMUs
- `ODESolution` for ME-FMUs
- if keyword `recordValues` is set, a tuple of type (success::Bool, DiffEqCallbacks.SavedValues) for CS-FMUs
- if keyword `recordValues` is set, a tuple of type (ODESolution, DiffEqCallbacks.SavedValues) for ME-FMUs

See also [`fmi2Simulate`](@ref), [`fmi2SimulateME`](@ref), [`fmi2SimulateCS`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).

"""
function fmiSimulate(str::fmi2Struct, args...; kwargs...)
    fmi2Simulate(str, args...; kwargs...)
end

"""

    fmiSimulateCS(str::fmi2Struct, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
                tolerance::Union{Real, Nothing} = nothing,
                dt::Union{Real, Nothing} = nothing,
                solver = nothing,
                customFx = nothing,
                recordValues::fmi2ValueReferenceFormat = nothing,
                saveat = [],
                setup::Bool = true,
                reset::Union{Bool, Nothing} = nothing, # nothing = auto
                inputValueReferences::fmi2ValueReferenceFormat = nothing,
                inputFunction = nothing,
                parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing,
                dtmax::Union{Real, Nothing} = nothing,
                kwargs...)

Starts a simulation of the Co-Simulation FMU instance.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
- `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.

# Keywords
- `tolerance::Union{Real, Nothing} = nothing`: Real number to set the tolerance for any OED-solver
- `dt::Union{Real, Nothing} = nothing`: Real number to set the step size of the OED-solver. Defaults to an automatic choice if the method is adaptive. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control)
- `solver = nothing`: Any Julia-supported OED-solver  (default is Tsit5). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/solvers/ode_solve/#ode_solve)
- `customFx = nothing`: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t)
- `recordValues::fmi2ValueReferenceFormat = nothing`: Array of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues`
- `saveat = []`: Time points to save values at (interpolated). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Output-Control)
- `setup::Bool = true`: Boolean, if FMU should be setup (default: setup=true)
- `reset::Union{Bool, Nothing} = nothing`: Boolean, if FMU should be reset before simulation (default: reset:=auto)
- `inputValueReferences::fmi2ValueReferenceFormat = nothing`: Array of input variables (strings or variableIdentifiers) to set at every simulation step
- `inputFunction = nothing`: Function to retrieve the values to set the inputs to
- `parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing`: Dictionary of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization
- `dtmax::Union{Real, Nothing} = nothing`: Real number for setting maximum dt for adaptive timestepping for the ODE solver. The default values are package dependent. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control)
- `kwargs...`: Further parameters of already defined functions `solve(args..., kwargs...)` from the library [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/#DifferentialEquations.jl:-Scientific-Machine-Learning-(SciML)-Enabled-Simulation-and-Estimation)

# Returns
- If keyword `recordValues` is not set, a boolean `success` is returned (simulation success).
- If keyword `recordValues` is set, a tuple of type (true, DiffEqCallbacks.SavedValues) or (false, nothing).

See also [`fmi2SimulateCS`](@ref), [`fmi2Simulate`](@ref), [`fmi2SimulateME`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).

"""
function fmiSimulateCS(str::fmi2Struct, args...; kwargs...)
    fmi2SimulateCS(str, args...; kwargs...)
end

"""

    fmiSimulateME(str::fmi2Struct, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
                tolerance::Union{Real, Nothing} = nothing,
                dt::Union{Real, Nothing} = nothing,
                solver = nothing,
                customFx = nothing,
                recordValues::fmi2ValueReferenceFormat = nothing,
                saveat = [],
                setup::Bool = true,
                reset::Union{Bool, Nothing} = nothing, # nothing = auto
                inputValueReferences::fmi2ValueReferenceFormat = nothing,
                inputFunction = nothing,
                parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing,
                dtmax::Union{Real, Nothing} = nothing,
                kwargs...)

Simulates a FMU instance for the given simulation time interval.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
- `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.

# Keywords
- `tolerance::Union{Real, Nothing} = nothing`: Real number to set the tolerance for any OED-solver
- `dt::Union{Real, Nothing} = nothing`: Real number to set the step size of the OED-solver. Defaults to an automatic choice if the method is adaptive. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control)
- `solver = nothing`: Any Julia-supported OED-solver  (default is Tsit5). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/solvers/ode_solve/#ode_solve)
- `customFx = nothing`: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t)
- `recordValues::fmi2ValueReferenceFormat = nothing`: Array of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues`
- `saveat = []`: Time points to save values at (interpolated). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Output-Control)
- `setup::Bool = true`: Boolean, if FMU should be setup (default: setup=true)
- `reset::Union{Bool, Nothing} = nothing`: Boolean, if FMU should be reset before simulation (default: reset:=auto)
- `inputValueReferences::fmi2ValueReferenceFormat = nothing`: Array of input variables (strings or variableIdentifiers) to set at every simulation step
- `inputFunction = nothing`: Function to retrieve the values to set the inputs to
- `parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing`: Dictionary of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization
- `dtmax::Union{Real, Nothing} = nothing`: Real number for setting maximum dt for adaptive timestepping for the ODE solver. The default values are package dependent. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control)
- `kwargs...`: Further parameters of already defined functions `solve(args..., kwargs...)` from the library [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/#DifferentialEquations.jl:-Scientific-Machine-Learning-(SciML)-Enabled-Simulation-and-Estimation)

# Returns
- If keyword `recordValues` is not set, a struct of type `ODESolution`.
- If keyword `recordValues` is set, a tuple of type (ODESolution, DiffEqCallbacks.SavedValues).

See also [`fmi2SimulateME`](@ref) [`fmi2SimulateCS`](@ref), [`fmi2Simulate`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).

"""
function fmiSimulateME(str::fmi2Struct, args...; kwargs...)
    fmi2SimulateME(str, args...; kwargs...)
end

"""

    fmiUnload(fmu::FMU2)

Unloads the FMU and all its instances and frees the allocated memory.

# Arguments
- `fmu::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).

See also [`fmi2Unload`](@ref).
"""
function fmiUnload(fmu::FMU2)
    fmi2Unload(fmu)
end

"""

    fmiGetNumberOfStates(str::fmi2Struct)

Returns the number of states of the FMU.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).

# Returns
- Returns the length of the `str.stateValueReferences` array, which consists of `fmi2ValueReference` constants.

See also [`fmi2GetNumberOfStates`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiGetNumberOfStates(str::fmi2Struct)
    fmi2GetNumberOfStates(str)
end

"""

    fmiGetTypesPlatform(str::fmi2Struct)

Returns the header file used to compile the FMU. By default returns `default`, version independent.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).

# Returns
-

See also [`fmi2GetVersion`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiGetTypesPlatform(str::fmi2Struct)
    fmi2GetTypesPlatform(str)
end

"""

    fmiGetVersion(str::fmi2Struct)

Returns the version of the FMU, version independent.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.

# Returns
- Returns a string from the address of a C-style (NUL-terminated) string. The string represents the version of the “fmi2Functions.h” header file which was used to compile the functions of the FMU. The function returns “fmiVersion” which is defined in this header file. The standard header file as documented in this specification has version “2.0”

# Source
 - FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
 - FMISpec2.0.2[p.22]: 2.1.4 Inquire Platform and Version Number of Header Files
 - FMISpec2.0.2[p.16]: 2.1.2 Platform Dependent Definitions

See also [`fmi2GetVersion`](@ref), [`unsafe_string`](https://docs.julialang.org/en/v1/base/strings/#Base.unsafe_string), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiGetVersion(str::fmi2Struct)
    fmi2GetVersion(str)
end

"""

    fmiInfo(str::fmi2Struct)

Prints FMU-specific information into the REPL.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.

# Source
 - FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)

See also [`fmi2Info`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiInfo(str::fmi2Struct)
    fmi2Info(str)
end

"""

    fmiInstantiate!(fmu::FMU2; pushComponents::Bool = true, visible::Bool = false, loggingOn::Bool = false, externalCallbacks::Bool = false,
                          logStatusOK::Bool=true, logStatusWarning::Bool=true, logStatusDiscard::Bool=true, logStatusError::Bool=true, logStatusFatal::Bool=true, logStatusPending::Bool=true)

Creates a new instance of the FMU, version independent.

Create a new instance of the given fmu, adds a logger if logginOn == true.

# Arguments
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.

# Keywords
- `pushComponents::Bool = true`:  `pushComponents` if the item `component` should be inserted in `fmu.components`(default = `true`).
- `visible::Bool = false`: `visible` if the FMU should be started with graphic interface, if supported (default=`false`)
- `loggingOn::Bool = false`: `loggingOn` if the FMU should log and display function calls (default=`false`)
- `externalCallbacks::Bool = false`: `externalCallbacks` if an external DLL should be used for the fmi2CallbackFunctions, this may improve readability of logging messages (default=`false`)
- `logStatusOK::Bool=true`: `logStatusOK` whether to log status of kind `fmi2OK` (default=`true`)
- `logStatusWarning::Bool=true`: `logStatusWarning` whether to log status of kind `fmi2Warning` (default=`true`)
- `logStatusDiscard::Bool=true`: `logStatusDiscard` whether to log status of kind `fmi2Discard` (default=`true`)
- `logStatusError::Bool=true`: `logStatusError` whether to log status of kind `fmi2Error` (default=`true`)
- `logStatusFatal::Bool=true`: `logStatusFatal` whether to log status of kind `fmi2Fatal` (default=`true`)
- `logStatusPending::Bool=true`: `logStatusPending` whether to log status of kind `fmi2Pending` (default=`true`)

# Returns
- `nothing`: if the instantiation failed. In addition, an error message appears.
- `component`: Returns the instance of a new FMU component.

# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.19]: 2.1.5 Creation, Destruction and Logging of FMU Instances

See also [`fmi2Instantiate!`](@ref), [`fmi2Instantiate`](@ref), [`FMU2`](@ref).
"""
function fmiInstantiate!(fmu::FMU2, args...; kwargs...)
    fmi2Instantiate!(fmu, args...; kwargs...)
end

"""

   fmiFreeInstance!(str::fmi2Struct)

Frees the allocated memory of the last instance of the FMU.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.

# Returns
- `status::fmi2Status`: returned by all functions to indicate the success of the function call

# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an FMU
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions

See also [fmi2FreeInstance](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiFreeInstance!(str::fmi2Struct)
    fmi2FreeInstance!(str)
end

"""

    fmiSetDebugLogging(str::fmi2Struct)

Control the use of the logging callback function, version independent.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.

# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.22]: 2.1.5 Creation, Destruction and Logging of FMU Instances
"""
function fmiSetDebugLogging(str::fmi2Struct)
    fmi2SetDebugLogging(str)
end

"""

    fmiSetupExperiment(str::fmi2Struct, c::FMU2Component, startTime::Union{Real, Nothing} = nothing, stopTime::Union{Real, Nothing} = nothing; tolerance::Union{Real, Nothing} = nothing)

Initialize the Simulation boundries

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.
- `startTime::Union{Real, Nothing} = nothing`: `startTime` is a real number which sets the value of starting time of the experiment. The default value is set automatically if doing nothing (default = `nothing`).
- `stopTime::Union{Real, Nothing} = nothing`: `stopTime` is a real number which sets the value of ending time of the experiment. The default value is set automatically if doing nothing (default = `nothing`).

# Keywords
- `tolerance::Union{Real, Nothing} = nothing`: `tolerance` is a real number which sets the value of tolerance range. The default value is set automatically if doing nothing (default = `nothing`).

# Returns
- Returns a warning if `str.state` is not called in `fmi2ComponentStateInstantiated`.
- `status::fmi2Status`: returned by all functions to indicate the success of the function call

# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an FMU
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions

See also [fmi2SetupExperiment](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiSetupExperiment(str::fmi2Struct, args...; kwargs...)
    fmi2SetupExperiment(str, args...; kwargs...)
end

"""

    fmiEnterInitializationMode(str::fmi2Struct)

Informs the FMU to enter initializaton mode, version independent.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.

# Returns
-  Returns a warning if `str.state` is not called in `fmi2ComponentStateInstantiated`.
-  `status::fmi2Status`: returned by all functions to indicate the success of the function call

# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an FMU
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions

 See also [fmi2EnterInitializationMode](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
"""
function fmiEnterInitializationMode(str::fmi2Struct)
    fmi2EnterInitializationMode(str)
end

"""

    fmiExitInitializationMode(str::fmi2Struct)

Informs the FMU to exit initialization mode, version independent.

# Arguments
- `str::fmi2Struct`:  Representative for an FMU in the FMI 2.0.2 Standard.
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
 - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
 - `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.

# Returns
-  Returns a warning if `str.state` is not called in `fmi2ComponentStateInitializationMode`.
-  `status::fmi2Status`: returned by all functions to indicate the success of the function call

# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an FMU
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions

 See also [fmi2ExitInitializationMode](@ref)
"""
function fmiExitInitializationMode(str::fmi2Struct)
    fmi2ExitInitializationMode(str)
end

"""
Informs the FMU that the simulation run is terminated, version independent.
"""
function fmiTerminate(str::fmi2Struct)
    fmi2Terminate(str)
end

"""
Resets the FMU after a simulation run, version independent.
"""
function fmiReset(str::fmi2Struct)
    fmi2Reset(str)
end

"""
ToDo
"""
function fmiGet(str::fmi2Struct, args...; kwargs...)
    fmi2Get(str, args...; kwargs...)
end

"""
ToDo
"""
function fmiGet!(str::fmi2Struct, args...; kwargs...)
    fmi2Get!(str, args...; kwargs...)
end

"""
ToDo
"""
function fmiSet(str::fmi2Struct, args...; kwargs...)
    fmi2Set(str, args...; kwargs...)
end

"""
Returns the real values of an array of variables
"""
function fmiGetReal(str::fmi2Struct, args...; kwargs...)
    fmi2GetReal(str, args...; kwargs...)
end

function fmiGetRealOutputDerivatives(str::fmi2Struct, args...; kwargs...)
    fmi2GetRealOutputDerivatives(str, args...; kwargs...)
end

"""
Writes the real values of an array of variables in the given field
"""
function fmiGetReal!(str::fmi2Struct, args...; kwargs...)
    fmi2GetReal!(str, args...; kwargs...)
end

"""
Set the values of an array of real variables
"""
function fmiSetReal(str::fmi2Struct, args...; kwargs...)
    fmi2SetReal(str, args...; kwargs...)
end

"""
ToDo
"""
function fmiSetRealInputDerivatives(str::fmi2Struct, args...; kwargs...)
    fmi2SetRealInputDerivatives(str, args...; kwargs...)
end

"""
Returns the integer values of an array of variables
"""
function fmiGetInteger(str::fmi2Struct,args...; kwargs...)
    fmi2GetInteger(str, args...; kwargs...)
end

"""
Writes the integer values of an array of variables in the given field
"""
function fmiGetInteger!(str::fmi2Struct, args...; kwargs...)
    fmi2GetInteger!(str, args...; kwargs...)
end

"""
Set the values of an array of integer variables
"""
function fmiSetInteger(str::fmi2Struct, args...; kwargs...)
    fmi2SetInteger(str, args...; kwargs...)
end

"""
Returns the boolean values of an array of variables
"""
function fmiGetBoolean(str::fmi2Struct, args...; kwargs...)
    fmi2GetBoolean(str, args...; kwargs...)
end

"""
Writes the boolean values of an array of variables in the given field
"""
function fmiGetBoolean!(str::fmi2Struct, args...; kwargs...)
    fmi2GetBoolean!(str, args...; kwargs...)
end

"""
Set the values of an array of boolean variables
"""
function fmiSetBoolean(str::fmi2Struct, args...; kwargs...)
    fmi2SetBoolean(str, args...; kwargs...)
end

"""
Returns the string values of an array of variables
"""
function fmiGetString(str::fmi2Struct, args...; kwargs...)
    fmi2GetString(str, args...; kwargs...)
end

"""
Writes the string values of an array of variables in the given field
"""
function fmiGetString!(str::fmi2Struct, args...; kwargs...)
    fmi2GetString!(str, args...; kwargs...)
end

"""
Set the values of an array of string variables
"""
function fmiSetString(str::fmi2Struct, args...; kwargs...)
    fmi2SetString(str, args...; kwargs...)
end

"""
Returns the FMU state of the fmu
"""
function fmiGetFMUstate(str::fmi2Struct)
    fmi2GetFMUstate(str)
end

"""
Sets the FMU to the given state
"""
function fmiSetFMUstate(str::fmi2Struct, args...; kwargs...)
    fmi2SetFMUstate(str, args...; kwargs...)
end

"""
Free the memory for the allocated FMU state
    """
function fmiFreeFMUstate!(str::fmi2Struct, args...; kwargs...)
    fmi2FreeFMUstate!(str, args...; kwargs...)
end

"""
Returns the size of the byte vector the FMU can be stored in
"""
function fmiSerializedFMUstateSize(str::fmi2Struct, args...; kwargs...)
    fmi2SerializedFMUstateSize(str, args...; kwargs...)
end

"""
Serialize the data in the FMU state pointer
"""
function fmiSerializeFMUstate(str::fmi2Struct, args...; kwargs...)
    fmi2SerializeFMUstate(str, args...; kwargs...)
end

"""
Deserialize the data in the FMU state pointer
"""
function fmiDeSerializeFMUstate(str::fmi2Struct, args...; kwargs...)
    fmi2DeSerializeFMUstate(str, args...; kwargs...)
end

"""
Returns the values of the directional derivatives.
"""
function fmiGetDirectionalDerivative(str::fmi2Struct, args...; kwargs...)
    fmi2GetDirectionalDerivative(str, args...; kwargs...)
end

"""
Returns the values of the directional derivatives (in-place).
"""
function fmiGetDirectionalDerivative!(str::fmi2Struct, args...; kwargs...)
    fmi2GetDirectionalDerivative!(str, args...; kwargs...)
end

"""
Does one step in the CoSimulation FMU
"""
function fmiDoStep(str::fmi2Struct, args...; kwargs...)
    fmi2DoStep(str, args...; kwargs...)
end

"""
Samples the values of the directional derivatives.
"""
function fmiSampleDirectionalDerivative(str::fmi2Struct, args...; kwargs...)
    fmi2SampleDirectionalDerivative(str, args...; kwargs...)
end

"""
Samples the values of the directional derivatives (in-place).
"""
function fmiSampleDirectionalDerivative!(str::fmi2Struct, args...; kwargs...)
    fmi2SampleDirectionalDerivative!(str, args...; kwargs...)
end

"""
Set a time instant
"""
function fmiSetTime(c::fmi2Struct, args...; kwargs...)
    fmi2SetTime(c, args...; kwargs...)
end

"""
Set a new (continuous) state vector
"""
function fmiSetContinuousStates(str::fmi2Struct, args...; kwargs...)
    fmi2SetContinuousStates(str, args...; kwargs...)
end

"""
The model enters Event Mode
"""
function fmi2EnterEventMode(str::fmi2Struct)
    fmi2EnterEventMode(str)
end

"""
Returns the next discrete states
"""
function fmiNewDiscreteStates(str::fmi2Struct)
    fmi2NewDiscreteStates(str)
end

"""
The model enters Continuous-Time Mode
"""
function fmiEnterContinuousTimeMode(str::fmi2Struct)
    fmi2EnterContinuousTimeMode(str)
end

"""
This function must be called by the environment after every completed step
"""
function fmiCompletedIntegratorStep(str::fmi2Struct, args...; kwargs...)
    fmi2CompletedIntegratorStep(str, args...; kwargs...)
end

"""
Compute state derivatives at the current time instant and for the current states
"""
function  fmiGetDerivatives(str::fmi2Struct)
    fmi2GetDerivatives(str)
end

"""
Returns the event indicators of the FMU
"""
function fmiGetEventIndicators(str::fmi2Struct)
    fmi2GetEventIndicators(str)
end

"""
Return the new (continuous) state vector x
"""
function fmiGetContinuousStates(s::fmi2Struct)
    fmi2GetContinuousStates(s)
end

"""
Return the new (continuous) state vector x
"""
function fmiGetNominalsOfContinuousStates(s::fmi2Struct)
    fmi2GetNominalsOfContinuousStates(s)
end

"""
Returns the start/default value for a given value reference.

TODO: Add this command in the documentation.
"""
function fmiGetStartValue(s::fmi2Struct, vr::fmi2ValueReferenceFormat)
    fmi2GetStartValue(s, vr)
end

##### function setters

function fmiSetFctGetTypesPlatform(fmu::FMU2, fun)
    fmi2SetFctGetTypesPlatform(fmu, fun)
end

function fmiSetFctGetVersion(fmu::FMU2, fun)
    fmi2SetFctGetVersion(fmu, fun)
end

function fmiSetFctInstantiate(fmu::FMU2, fun)
    fmi2SetFctInstantiate(fmu, fun)
end

function fmiSetFctFreeInstance(fmu::FMU2, fun)
    fmi2SetFctFreeInstance(fmu, fun)
end

function fmiSetFctSetDebugLogging(fmu::FMU2, fun)
    fmi2SetFctSetDebugLogging(fmu, fun)
end

function fmiSetFctSetupExperiment(fmu::FMU2, fun)
    fmi2SetFctSetupExperiment(fmu, fun)
end

function fmiSetEnterInitializationMode(fmu::FMU2, fun)
    fmi2SetEnterInitializationMode(fmu, fun)
end

function fmiSetFctExitInitializationMode(fmu::FMU2, fun)
    fmi2SetFctExitInitializationMode(fmu, fun)
end

function fmiSetFctTerminate(fmu::FMU2, fun)
    fmi2SetFctTerminate(fmu, fun)
end

function fmiSetFctReset(fmu::FMU2, fun)
    fmi2SetFctReset(fmu, fun)
end

function fmiSetFctGetReal(fmu::FMU2, fun)
    fmi2SetFctGetReal(fmu, fun)
end

function fmiSetFctGetInteger(fmu::FMU2, fun)
    fmi2SetFctGetInteger(fmu, fun)
end

function fmiSetFctGetBoolean(fmu::FMU2, fun)
    fmi2SetFctGetBoolean(fmu, fun)
end

function fmiSetFctGetString(fmu::FMU2, fun)
    fmi2SetFctGetString(fmu, fun)
end

function fmiSetFctSetReal(fmu::FMU2, fun)
    fmi2SetFctSetReal(fmu, fun)
end

function fmiSetFctSetInteger(fmu::FMU2, fun)
    fmi2SetFctSetInteger(fmu, fun)
end

function fmiSetFctSetBoolean(fmu::FMU2, fun)
    fmi2SetFctSetBoolean(fmu, fun)
end

function fmiSetFctSetString(fmu::FMU2, fun)
    fmi2SetFctSetString(fmu, fun)
end

function fmiSetFctSetTime(fmu::FMU2, fun)
    fmi2SetFctSetTime(fmu, fun)
end

function fmiSetFctSetContinuousStates(fmu::FMU2, fun)
    fmi2SetFctSetContinuousStates(fmu, fun)
end

function fmiSetFctEnterEventMode(fmu::FMU2, fun)
    fmi2SetFctEnterEventMode(fmu, fun)
end

function fmiSetFctNewDiscreteStates(fmu::FMU2, fun)
    fmi2SetFctNewDiscreteStates(fmu, fun)
end

function fmiSetFctEnterContinuousTimeMode(fmu::FMU2, fun)
    fmi2SetFctEnterContinuousTimeMode(fmu, fun)
end

function fmiSetFctCompletedIntegratorStep(fmu::FMU2, fun)
    fmi2SetFctCompletedIntegratorStep(fmu, fun)
end

function fmiSetFctGetDerivatives(fmu::FMU2, fun)
    fmi2SetFctGetDerivatives(fmu, fun)
end

function fmiSetFctGetEventIndicators(fmu::FMU2, fun)
    fmi2SetFctGetEventIndicators(fmu, fun)
end

function fmiSetFctGetContinuousStates(fmu::FMU2, fun)
    fmi2SetFctGetContinuousStates(fmu, fun)
end

function fmiSetFctGetNominalsOfContinuousStates(fmu::FMU2, fun)
    fmi2SetFctGetNominalsOfContinuousStates(fmu, fun)
end

function fmiGetSolutionTime(solution::FMU2Solution, args...; kwargs...)
    fmi2GetSolutionTime(solution, args...; kwargs...)
end

function fmiGetSolutionState(solution::FMU2Solution, args...; kwargs...)
    fmi2GetSolutionState(solution, args...; kwargs...)
end

function fmiGetSolutionValue(solution::FMU2Solution, args...; kwargs...)
    fmi2GetSolutionValue(solution, args...; kwargs...)
end

##### Multiple Dispatch fallback for FMUs with unsupported versions #####

unsupportedFMUs = Union{FMU1,FMU3}
function fmiDoStep(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiUnload(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiGetTypesPlatform(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiGetVersion(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiInfo(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiInstantiate!(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiFreeInstance!(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiSetDebugLogging(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiSetupExperiment(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiEnterInitializationMode(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiExitInitializationMode(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiTerminate(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiReset(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiSimulate(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSimulateCS(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSimulateME(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function (fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetReal(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetReal!(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSetReal(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetInteger(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetInteger!(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSetInteger(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetBoolean(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetBoolean!(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSetBoolean(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetString(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetString!(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSetString(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetFMUstate(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiSetFMUstate(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiFreeFMUstate!(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiSerializedFMUstateSize(c::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSerializeFMUstate(c::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiDeSerializeFMUstate(c::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiGetDirectionalDerivative(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSetTime(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmiSetContinuousStates(c::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function fmi2EnterEventMode(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiNewDiscreteStates(c::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiEnterContinuousTimeMode(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiCompletedIntegratorStep(fmu::unsupportedFMUs, args...; kwargs...)
    error(unsupportedFMU::errorType)
end

function  fmiGetDerivatives(c::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiGetEventIndicators(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiGetContinuousStates(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

function fmiGetNominalsOfContinuousStates(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

# model description specific

function fmiGetModelName(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiGetGUID(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiGetGenerationTool(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiGetGenerationDateAndTime(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiGetVariableNamingConvention(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiGetNumberOfEventIndicators(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiCanGetSetState(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiCanSerializeFMUstate(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiProvidesDirectionalDerivative(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiIsCoSimulation(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end
function fmiIsModelExchange(fmu::unsupportedFMUs)
    error(unsupportedFMU::errorType)
end

end # module FMI

ext: jl


text:
#LibPSFC: A Julia wrapper for libpsf tools
#-------------------------------------------------------------------------------
__precompile__(true)

module LibPSFC

const rootpath = realpath(joinpath(dirname(realpath(@__FILE__)),"../."))
const libroot = joinpath(rootpath, "core/lib")
const objfile = joinpath(libroot, "libpsfreader_c.so")

include("base.jl")
#include("show.jl")

export readsweep, readscalar

end #LibPSFC
#Last line


text:
module BasisMatrices

# TODO: still need to write fund, minterp

#=
Note that each subtype of `BT<:BasisFamily` (with associated `PT<:BasisParam`)
will define the following constructor methods:

```julia
# basis constructors
Basis(::BT, args...)
Basis(::PT)

# node constructor
nodes(::PT)
```

=#

import Base: ==, *, \
using Base.Cartesian

using QuantEcon: gridmake, gridmake!, ckron, fix, fix!

using Combinatorics: with_replacement_combinations
using Base.Iterators: product

# types
export BasisFamily, Cheb, Lin, Spline, Basis, Smolyak,
       BasisParams, ChebParams, LinParams, SplineParams, SmolyakParams,
       AbstractBasisMatrixRep, Tensor, Expanded, Direct,
       BasisMatrix, Interpoland, SplineSparse, RowKron

# functions
export nodes, get_coefs, funfitxy, funfitf, funeval, evalbase,
       derivative_op, row_kron, evaluate, fit!, update_coefs!,
       complete_polynomial, complete_polynomial!, n_complete

#re-exports
export gridmake, gridmake!, ckron

# stdlib
using SparseArrays, LinearAlgebra, Statistics

abstract type BasisFamily end
abstract type BasisParams end
const IntSorV = Union{Int, AbstractVector{Int}}
const TensorX = Union{Tuple{Vararg{AbstractVector}},AbstractVector{<:AbstractVector}}

include("util.jl")
include("spline_sparse.jl")

# include the families

# BasisParams interface
SparseArrays.issparse(::Type{T}) where {T<:BasisParams} = false
Base.ndims(::BasisParams) = 1
for f in [:family, :family_name, :(SparseArrays.issparse), :(Base.eltype)]
    @eval $(f)(::T) where {T<:BasisParams} = $(f)(T)
end
include("cheb.jl")
include("lin.jl")
include("spline.jl")
include("complete.jl")
include("smolyak.jl")

evalbase(p::BasisParams, x::Number, args...) = evalbase(p, [x], args...)

# now some more interface methods that only make sense once we have defined
# the subtypes
basis_eltype(::TP, x) where {TP<:BasisParams} = promote_type(eltype(TP), eltype(x))
basis_eltype(::Type{TP}, x) where {TP<:BasisParams} = promote_type(eltype(TP), eltype(x))
"""
    basis_eltype(p::Union{BasisParams,Type{<:BasisParams}, x)

Return the eltype of the Basis matrix that would be obtained by calling
`evalbase(p, x)`
"""
basis_eltype

# give the type of the `vals` field based on the family type parameter of the
# corresponding basis. `Spline` and `Lin` use sparse, `Cheb` uses dense
# a hybrid must fall back to a generic AbstractMatrix{Float64}
# the default is just a dense matrix
# because there is only one dense version, we will start with the sparse
# case and overload for Cheb
bmat_type(::Type{TP}, x) where {TP<:BasisParams} = SparseMatrixCSC{basis_eltype(TP, x),Int}
bmat_type(::Type{T2}, ::Type{TP}, x) where {TP<:BasisParams,T2} = bmat_type(TP, x)
function bmat_type(::Type{T2}, ::Type{TP}, x) where {TP<:BasisParams,T2<:SplineSparse}
    SplineSparse{basis_eltype(TP, x),Int}
end

bmat_type(::Type{T}, x) where {T<:ChebParams} = Matrix{basis_eltype(T, x)}
function bmat_type(::Type{T2}, ::Type{TP}, x) where {TP<:ChebParams,T2<:SplineSparse}
    bmat_type(TP, x)
end

# version where there isn't an x passed
bmat_type(::Type{TP}) where {TP<:BasisParams} = bmat_type(TP, one(eltype(TP)))
function bmat_type(::Type{T2}, ::Type{TP}) where {TP<:BasisParams,T2}
    bmat_type(T2, TP, one(eltype(TP)))
end

# add methods to instances
bmat_type(::T) where {T<:BasisParams} = bmat_type(T)
bmat_type(ss::Type{T2}, ::TF) where {TF<:BasisParams,T2} = bmat_type(T2, TF)
bmat_type(ss::Type{T2}, ::TF, x) where {TF<:BasisParams,T2} = bmat_type(T2, TF, x)

# default method for evalbase with extra type hint is to just ignore the extra
# type hint
evalbase(::Type{T}, bp::BasisParams, x, order) where {T} = evalbase(bp, x, order)

# include other
include("basis.jl")
include("basis_structure.jl")
include("interp.jl")

# deprecations
@deprecate BasisStructure BasisMatrix

end # module


text:
using Test
using Sunny
using Random
using LinearAlgebra

Random.seed!(1111)

# Idea taken from StaticArrays.jl
enabled_tests = lowercase.(ARGS)
function addtests(fname)
    key = lowercase(splitext(fname)[1])
    # If no arguments given on command line, run all tests.
    # Otherwise, only run requested tests
    if isempty(enabled_tests) || key in enabled_tests
        println(fname)
        include(fname)
    end
end

# Generates a "standard" set of exchange interactions for a
#  diamond lattice with randomized coupling constants for use
#  across many tests.
function diamond_test_exchanges()
    crystal = Sunny.diamond_crystal()
    latsize = (4, 4, 4)

    # Arbitrary Heisenberg
    heisen = heisenberg(rand(), Bond(1, 3, [0, 0, 0]))

    # On-site on all diamond atoms must be proportional to identity
    on_site   = single_ion_anisotropy(rand() * I(3), 1)

    # This bond has allowed J of form [A A B] along diagonal
    diag_coup_J    = [rand(), 0.0, rand()]
    diag_coup_J[2] = diag_coup_J[1]
    diag_int       = exchange(diagm(diag_coup_J), Bond(1, 2, [0, 0, 0]))

    # Construct random matrix of allowed form on Bond(1, 4, [0, 0, 0])
    A, B, C, D = rand(), rand(), rand(), rand()
    gen_coup_J = [A D C; D A C; C C B]
    gen_int = exchange(gen_coup_J, Bond(1, 4, [0, 0, 0]))

    return [heisen, on_site, diag_int, gen_int]
end

function produce_example_system()
    cryst = Sunny.diamond_crystal()
    latsize = [5, 5, 5]

    interactions = [
        diamond_test_exchanges()...,
        external_field([0, 0, 1])
    ]

    return SpinSystem(cryst, interactions, latsize)
end

@testset verbose=true "Sunny Tests" begin
    addtests("test_lattice.jl")
    addtests("test_pair_interactions.jl")
    addtests("test_ewald.jl")
    addtests("test_units.jl")
    addtests("test_symmetry.jl")
    addtests("test_metropolis.jl")
    addtests("test_fourier.jl")
    addtests("test_dynamics.jl")
end


text:
using LightGraphs
using BenchmarkTools
using Dates
import JLD: load, save

TUNE = false
LOAD_PARS = true
SAVE_RES = false

bench_dir = Base.source_dir()

### ADD BENCHMARKS  ###############
suite = BenchmarkGroup()
GLIST = [Graph]
DGLIST = [DiGraph]
GROUPS = ["core", "generators","flow"]
# GROUPS = ["core"]
for group in GROUPS
    include("$group/$group.jl")
end

####  TUNING / LOADING / SAVING PARAMS

function tune_and_savepars!(suite)
    tune!(suite)
    path = joinpath(bench_dir,"parameters","$(Date(now())).jld")
    save(path, "suite", params(suite))
end

function loadpars!(suite)
    path = joinpath(bench_dir, "parameters")
    files = readdir(path)
    dates = map(x -> Date(split(x, ['.'])[1]), files)
    f = joinpath(bench_dir, "parameters","$(maximum(dates)).jld")
    loadparams!(suite, load(f, "suite"), :evals, :samples)
end

function saveres(res)
    path = joinpath(bench_dir,"results","$(Date(now())).jld")
    save(path, "res", res)
end

function loadres()
    path = joinpath(bench_dir, "results")
    files = readdir(path)
    dates = map(x -> Date(split(x, ['.'])[1]), files)
    f = joinpath(bench_dir, "results","$(maximum(dates)).jld")
    return load(f, "res")
end

TUNE && tune_and_savepars!(suite)
LOAD_PARS && loadpars!(suite)

#####  RUNNING ###################
res = run(suite, verbose=true)

#### COMPARISONS ##########
resold = loadres()

has_regressions = false
has_improves = true
for group in GROUPS
    m = median(res[group])
    println(m)
    !haskey(resold, group) && continue

    mold = median(resold[group])
    judgement = judge(m, mold)

    regr = regressions(judgement)
    if length(regr) > 0
        has_regressions = false
        @warn("REGRESSIONS FOUND:")
        println(regr)
    end
    improvs = improvements(judgement)
    if length(improvs) > 0
        has_improves = true
        print_with_color(:green, "IMPROVEMENTS FOUND:\n")
        println(improvs)
    end
end

###  SAVING ############
# if SAVE_RES && !has_improves && !has_regressions
println()
if SAVE_RES
    saveres(res)
    println("Results saved!")
else
    println("Results not saved. Save the with `saveres(res)`")
end


text:
## macro for argument checking

macro check_args(D, cond)
    quote
        if !($(esc(cond)))
            throw(ArgumentError(string(
                $(string(D)), ": the condition ", $(string(cond)), " is not satisfied.")))
        end
    end
end

##### Utility functions

isunitvec(v::AbstractVector) = (norm(v) - 1.0) < 1.0e-12

isprobvec(p::AbstractVector{<:Real}) =
    all(x -> x ≥ zero(x), p) && isapprox(sum(p), one(eltype(p)))

pnormalize!(v::AbstractVector{<:Real}) = (v ./= sum(v); v)

add!(x::AbstractArray, y::AbstractVector) = broadcast!(+, x, x, y)
add!(x::AbstractArray, y::Zeros) = x

multiply!(x::AbstractArray, c::Number) = (x .*= c; x)

exp!(x::AbstractArray) = (x .= exp.(x); x)

# get a type wide enough to represent all a distributions's parameters
# (if the distribution is parametric)
# if the distribution is not parametric, we need this to be a float so that
# inplace pdf calculations, etc. allocate storage correctly
@inline partype(::Distribution) = Float64

# for checking the input range of quantile functions
# comparison with NaN is always false, so no explicit check is required
macro checkquantile(p,ex)
    p, ex = esc(p), esc(ex)
    :(zero($p) <= $p <= one($p) ? $ex : NaN)
end

macro checkinvlogcdf(lp,ex)
    lp, ex = esc(lp), esc(ex)
    :($lp <= zero($lp) ? $ex : NaN)
end

# because X == X' keeps failing due to floating point nonsense
function isApproxSymmmetric(a::AbstractMatrix{Float64})
    tmp = true
    for j in 2:size(a, 1)
        for i in 1:(j - 1)
            tmp &= abs(a[i, j] - a[j, i]) < 1e-8
        end
    end
    return tmp
end

# because isposdef keeps giving the wrong answer for samples
# from Wishart and InverseWisharts
hasCholesky(a::Matrix{Float64}) = isa(trycholesky(a), Cholesky)

function trycholesky(a::Matrix{Float64})
    try cholesky(a)
    catch e
        return e
    end
end


text:
using Test, Random, LinearAlgebra, MPStates

@testset "Tensor factorizations" begin
Random.seed!(0)

@testset "QR decomposition" begin
    (m1, m2, m3) = (4, 5, 6)
    M = randn(m1, m2, m3)
    Q, R = factorize_qr(M)

    # Check dims.
    @test size(Q) == (4, 5, 6)
    @test size(R) == (6, 6)

    # Check Q*R=M.
    Q = reshape(Q, 4*5, 6)
    @test Q*R ≈ reshape(M, 4*5, 6)

    # Check that Q is unitary.
    @test Q'*Q ≈ Matrix(I, 6, 6)

end

@testset "LQ decomposition" begin
    (m1, m2, m3) = (4, 5, 6)
    M = randn(m1, m2, m3)
    L, Q = factorize_lq(M)

    # Check dims.
    @test size(L) == (4, 4)
    @test size(Q) == (4, 5, 6)

    # Check L*Q=M.
    Q = reshape(Q, 4, 5*6)
    @test L*Q ≈ reshape(M, 4, 5*6)

    # Check that Q is unitary.
    @test Q*Q' ≈ Matrix(I, 4, 4)
end

@testset "SVD right decomposition" begin
    (m1, m2, m3, m4) = (3, 4, 5, 3)
    M = randn(m1, m2, m3, m4)

    # Full factorization.
    U, SVt = factorize_svd_right(M, cutoff=0., normalize_S=false)
    @test size(U) == (3, 4, 12)
    @test size(SVt) == (12, 5, 3)
    rU = reshape(U, 12, 12)
    @test rU'*rU ≈ Matrix(I, 12, 12)
    rSVt = reshape(SVt, 12, 15)
    @test rU*rSVt ≈ reshape(M, 12, 15)

    # SVD with cutoff.
    U, SVt = factorize_svd_right(M, cutoff=1., normalize_S=false)
    @test size(U, 1) == 3
    @test size(U, 2) == 4
    @test size(U, 3) < 12
    @test size(SVt, 2) == 5
    @test size(SVt, 3) == 3
    @test size(SVt, 1) < 12
    rU = reshape(U, 12, size(U, 3))
    @test rU'*rU ≈ Matrix(I, size(U, 3), size(U, 3))

    # SVD with dimensional cutoff.
    dimcutoff = 5
    U, SVt = factorize_svd_right(M, cutoff=1., dimcutoff=dimcutoff,
                                 normalize_S=false)
    @test size(U, 1) == 3
    @test size(U, 2) == 4
    @test size(U, 3) == dimcutoff
    @test size(SVt, 2) == 5
    @test size(SVt, 3) == 3
    @test size(SVt, 1) == dimcutoff
    rU = reshape(U, 12, dimcutoff)
    @test rU'*rU ≈ Matrix(I, dimcutoff, dimcutoff)
end

@testset "SVD left decomposition" begin
    (m1, m2, m3, m4) = (3, 4, 5, 3)
    M = randn(m1, m2, m3, m4)

    # Full factorization.
    US, Vt = factorize_svd_left(M, cutoff=0., normalize_S=false)
    @test size(US) == (3, 4, 12)
    @test size(Vt) == (12, 5, 3)
    rUS = reshape(US, 12, 12)
    rVt = reshape(Vt, 12, 15)
    @test rVt*rVt' ≈ Matrix(I, 12, 12)
    @test rUS*rVt ≈ reshape(M, 12, 15)

    # SVD with cutoff.
    US, Vt = factorize_svd_left(M, cutoff=1., normalize_S=false)
    @test size(US, 1) == 3
    @test size(US, 2) == 4
    @test size(US, 3) < 12
    @test size(Vt, 2) == 5
    @test size(Vt, 3) == 3
    @test size(Vt, 1) < 12
    rVt = reshape(Vt, size(Vt, 1), 15)
    @test rVt*rVt' ≈ Matrix(I, size(Vt, 1), size(Vt, 1))

    # SVD with dimensional cutoff.
    dimcutoff = 5
    US, Vt = factorize_svd_left(M, cutoff=1., dimcutoff=dimcutoff,
                                normalize_S=false)
    @test size(US, 1) == 3
    @test size(US, 2) == 4
    @test size(US, 3) == dimcutoff
    @test size(Vt, 2) == 5
    @test size(Vt, 3) == 3
    @test size(Vt, 1) == dimcutoff
    rVt = reshape(Vt, dimcutoff, 15)
    @test rVt*rVt' ≈ Matrix(I, dimcutoff, dimcutoff)
end
end # @testset "Tensor factorizations"


text:
#
# Types supporting parameterized Timestep and Clock objects
#

abstract type AbstractTimestep <: MimiStruct end

struct FixedTimestep{FIRST, STEP, LAST} <: AbstractTimestep
    t::Int
end

struct VariableTimestep{TIMES} <: AbstractTimestep
    t::Int
    current::Int

    function VariableTimestep{TIMES}(t::Int = 1) where {TIMES}
        # The special case below handles when functions like next_step step beyond
        # the end of the TIMES array.  The assumption is that the length of this
        # last timestep, starting at TIMES[end], is 1.
        current::Int = t > length(TIMES) ? TIMES[end] + 1 : TIMES[t]

        return new(t, current)
    end
end

struct TimestepValue{T}
    value::T
    offset::Int

    function TimestepValue(v::T; offset::Int = 0) where T
        return new{T}(v, offset)
    end
end

struct TimestepIndex
    index::Int
end

mutable struct Clock{T <: AbstractTimestep} <: MimiStruct
	ts::T

	function Clock{T}(FIRST::Int, STEP::Int, LAST::Int) where T
		return new(FixedTimestep{FIRST, STEP, LAST}(1))
    end

    function Clock{T}(TIMES::NTuple{N, Int} where N) where T
        return new(VariableTimestep{TIMES}())
    end
end

mutable struct TimestepArray{T_TS <: AbstractTimestep, T, N, ti} <: MimiStruct
	data::Array{T, N}

    function TimestepArray{T_TS, T, N, ti}(d::Array{T, N}) where {T_TS, T, N, ti}
		return new(d)
	end

    function TimestepArray{T_TS, T, N, ti}(lengths::Int...) where {T_TS, T, N, ti}
		return new(Array{T, N}(undef, lengths...))
	end
end

# Since these are the most common cases, we define methods (in time.jl)
# specific to these type aliases, avoiding some of the inefficiencies
# associated with an arbitrary number of dimensions.
const TimestepMatrix{T_TS, T, ti} = TimestepArray{T_TS, T, 2, ti}
const TimestepVector{T_TS, T} = TimestepArray{T_TS, T, 1, 1}


text:
type SoundBuffer
    ptr::Ptr{Void}

    function SoundBuffer(ptr::Ptr{Void})
        s = new(ptr)
        # finalizer(s, destroy)
        # s
    end
end

function SoundBuffer(filename::AbstractString)
    SoundBuffer(ccall((:sfSoundBuffer_createFromFile, libcsfml_audio), Ptr{Void}, (Ptr{Cchar},), filename))
end

function copy(buffer::SoundBuffer)
    return SoundBuffer(ccall((:sfSoundBuffer_copy, libcsfml_audio), Ptr{Void}, (Ptr{Void},), buffer.ptr))
end

function destroy(buffer::SoundBuffer)
    ccall((:sfSoundBuffer_destroy, libcsfml_audio), Void, (Ptr{Void},), buffer.ptr)
end

function get_samplecount(buffer::SoundBuffer)
    ccall((:sfSoundBuffer_getSampleCount), UInt64, (Ptr{Void},), buffer.ptr)
end

function get_samples(buffer::SoundBuffer)
    ptr = ccall((:sfSoundBuffer_getSamples, libcsfml_audio), Ptr{Int16}, (Ptr{Void},), buffer.ptr)
    @compat unsafe_wrap(Vector{Int16}, ptr, get_samplecount(buffer), true)
end

function get_samplerate(buffer::SoundBuffer)
    ccall((:sfSoundBuffer_getSampleRate, libcsfml_audio), UInt32, (Ptr{Void},), buffer.ptr)
end

function save_to_file(buffer::SoundBuffer, filename::AbstractString)
    ccall((:sfSoundBuffer_saveToFile, libcsfml_audio), Void, (Ptr{Void}, Ptr{Cchar},), buffer.ptr, filename)
end

function get_duration(buffer::SoundBuffer)
    return ccall((:sfSoundBUffer_getDuration, libcsfml_audio), Time, (Ptr{Void},), buffer.ptr)
end


text:
using JuMP, EAGO

                     m = Model()

                     EAGO.register_eago_operators!(m)

                     @variable(m, -1 <= x[i=1:2] <= 1)

                     @variable(m, -3.357520876571253 <= q <= 3.7351340364874854)

                     add_NL_constraint(m, :(swish(0.618782057809276 + -0.4828154761503023*swish(0.8842828756523886 + -0.6089179668773923*swish(0.019018219753813348 + 0.8244023518746615*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.3296833580573324*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2]))) + 0.44138625638024864*swish(-0.9126485111761089 + 0.9176394232569307*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.371990627187599*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2])))) + 0.3197591788003944*swish(0.7251254505916935 + -0.923704860259535*swish(0.019018219753813348 + 0.8244023518746615*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.3296833580573324*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2]))) + 0.28767280168552967*swish(-0.9126485111761089 + 0.9176394232569307*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.371990627187599*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2]))))) + swish(-0.0992210488643468 + 0.772244110024173*swish(0.8842828756523886 + -0.6089179668773923*swish(0.019018219753813348 + 0.8244023518746615*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.3296833580573324*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2]))) + 0.44138625638024864*swish(-0.9126485111761089 + 0.9176394232569307*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.371990627187599*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2])))) + 0.07492471407403789*swish(0.7251254505916935 + -0.923704860259535*swish(0.019018219753813348 + 0.8244023518746615*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.3296833580573324*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2]))) + 0.28767280168552967*swish(-0.9126485111761089 + 0.9176394232569307*swish(-0.7862452642307147 + -0.6499989200319587*$(x[1]) + -0.39995929749666725*$(x[2])) + 0.371990627187599*swish(0.2947581350949262 + -0.3447038526107806*$(x[1]) + -0.5996590697259889*$(x[2]))))) - $q <= 0.0))

                     @objective(m, Min, q)

                     return m

max_stars_repo_name: PSORLab/RSActivationFunctions

max_issues_repo_name: PSORLab/RSActivationFunctions

max_forks_repo_name: PSORLab/RSActivationFunctions


text:
mutable struct GoalVisitation <: LoggerKeyData
    goal_visitations::Array{Int64}
    visit_order::Array{Int64}

    function GoalVisitation(logger_init_info)
        new(zeros(4),[])
    end
end

function lg_step!(self::GoalVisitation, env, agent, s, a, s_next, r, is_terminal, cur_step_in_episode, cur_step_total)
    # C,_,_ = get(agent.demons, s, a, s_next)
    #NOTE This only works for TTMaze.
    C = s_next[2:5]
    if sum(C) != 0
        gvf_i = findfirst(!iszero,C)
        self.goal_visitations[gvf_i] += 1
        push!(self.visit_order, gvf_i)
    end
end

function lg_episode_end!(self::GoalVisitation, cur_step_in_episode, cur_step_total)
end

function save_log(self::GoalVisitation, save_dict::Dict)
    save_dict[:goal_visitation] = self.goal_visitations
    save_dict[:visit_order] = self.visit_order
end

mutable struct OneDGoalVisitation <: LoggerKeyData
    goal_visitations::Array{Int64}
    visit_order::Array{Int64}

    function OneDGoalVisitation(logger_init_info)
        new(zeros(4),[])
    end
end

function lg_step!(self::OneDGoalVisitation, env, agent, s, a, s_next, r, is_terminal, cur_step_in_episode, cur_step_total)
    # C,_,_ = get(agent.demons, s, a, s_next)
    #NOTE This only works for TTMaze.
    C = s_next[3:end]
    if sum(C) != 0
        gvf_i = findfirst(!iszero,C)
        self.goal_visitations[gvf_i] += 1
        push!(self.visit_order, gvf_i)
    end
end

function lg_episode_end!(self::OneDGoalVisitation, cur_step_in_episode, cur_step_total)
end

function save_log(self::OneDGoalVisitation, save_dict::Dict)
    save_dict[:goal_visitation] = self.goal_visitations
    save_dict[:visit_order] = self.visit_order
end

max_stars_repo_name: MatthewMcLeod/curiosity

max_issues_repo_name: MatthewMcLeod/curiosity

max_forks_repo_name: MatthewMcLeod/curiosity


text:
module Fields

export
    Face, Center,
    AbstractField, Field,
    CenterField, XFaceField, YFaceField, ZFaceField,
    ReducedField, AveragedField, ComputedField, KernelComputedField, BackgroundField,
    interior, data,
    xnode, ynode, znode, location,
    set!, compute!, @compute,
    VelocityFields, TracerFields, tracernames, PressureFields, TendencyFields,
    interpolate, FieldSlicer

using Oceananigans.Architectures
using Oceananigans.Grids
using Oceananigans.BoundaryConditions

include("abstract_field.jl")
include("reduced_getindex_setindex.jl")
include("field.jl")
include("zero_field.jl")
include("reduced_field.jl")
include("averaged_field.jl")
include("computed_field.jl")
include("kernel_computed_field.jl")
include("pressure_field.jl")
include("function_field.jl")
include("set!.jl")
include("tracer_names.jl")
include("validate_field_tuple_grid.jl")
include("field_tuples.jl")
include("background_fields.jl")
include("interpolate.jl")
include("field_slicer.jl")
include("show_fields.jl")
include("broadcasting_abstract_fields.jl")
include("mapreduce_abstract_fields.jl")

end


text:
module CustomFireBarrierFrostHelper

using ..Ahorn, Maple

@mapdef Entity "FrostHelper/CustomFireBarrier" CustomFireBarrier(x::Integer, y::Integer, width::Integer=Maple.defaultBlockWidth, height::Integer=Maple.defaultBlockHeight, isIce::Bool=false, surfaceColor::String="ff8933", edgeColor::String="f25e29", centerColor::String="d01c01", silent::Bool=false, ignoreCoreMode::Bool=false)

const placements = Ahorn.PlacementDict(
    "Custom Fire Barrier (Frost Helper)" => Ahorn.EntityPlacement(
        CustomFireBarrier,
        "rectangle"
    ),
)

Ahorn.minimumSize(entity::CustomFireBarrier) = 8, 8
Ahorn.resizable(entity::CustomFireBarrier) = true, true

Ahorn.selection(entity::CustomFireBarrier) = Ahorn.getEntityRectangle(entity)

edgeColor = (246, 98, 18, 255) ./ 255
centerColor = (209, 9, 1, 102) ./ 255

function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::CustomFireBarrier, room::Maple.Room)
    x = Int(get(entity.data, "x", 0))
    y = Int(get(entity.data, "y", 0))

    width = Int(get(entity.data, "width", 32))
    height = Int(get(entity.data, "height", 32))

	edgeColor = Ahorn.argb32ToRGBATuple(parse(Int, get(entity.data, "edgeColor", "f25e29"), base=16))[1:3] ./ 255
	realEdgeColor = (edgeColor..., 1.0)

	centerColor = Ahorn.argb32ToRGBATuple(parse(Int, get(entity.data, "centerColor", "d01c01"), base=16))[1:3] ./ 255
	realCenterColor = (centerColor..., 1.0)

    Ahorn.drawRectangle(ctx, 0, 0, width, height, realCenterColor, realEdgeColor)
end

end

max_stars_repo_name: JaThePlayer/FrostHelper

max_issues_repo_name: JaThePlayer/FrostHelper

max_forks_repo_name: JaThePlayer/FrostHelper


text:
struct DiscretizedEnvironment{N,E<:Environment,D,S} <: Environment
    env::E
    discretizers::D
    dims::S
end

function DiscretizedEnvironment(
        env::Environment,
        discretizers::NTuple{N,Discretizers.AbstractDiscretizer}) where N
    dims = map(nlabels, discretizers)
    DiscretizedEnvironment{N,typeof(env),typeof(discretizers),typeof(dims)}(env, discretizers, dims)
end

function DiscretizedEnvironment(
        env::Environment,
        bins::NTuple{N,AbstractVector}) where N
    fbins = map(A->convert(Vector{Float64},A), bins)
    discretizers = map(LinearDiscretizer, fbins)
    DiscretizedEnvironment(env, discretizers)
end

discount(env::DiscretizedEnvironment) = discount(env.env)
isterminal(env::DiscretizedEnvironment) = isterminal(env.env)

state_index(env::DiscretizedEnvironment, s) = sub2ind(env.dims, s...)
n_states(env::DiscretizedEnvironment) = prod(env.dims)

actions(env::DiscretizedEnvironment) = actions(env.env)
action_index(env::DiscretizedEnvironment, a) = action_index(env.env, a)
n_actions(env::DiscretizedEnvironment) = n_actions(env.env)

function Base.show(io::IO, env::DiscretizedEnvironment)
    print(io, n_states(env), "-state discretized ")
    Base.show(io, env.env)
end

function step!(env::DiscretizedEnvironment{N}, args...) where N
    s, r = step!(env.env, args...)
    ntuple(i->encode(env.discretizers[i], s[i]), Val{N}), r
end

function reset!(env::DiscretizedEnvironment{N}, args...) where N
    s = reset!(env.env, args...)
    ntuple(i->encode(env.discretizers[i], s[i]), Val{N})
end


text:
# Ocean put and get calls

"""
function preOcean(csolver)
    - saves and regrids the `BoundaryEnergyFlux` couplerfield (i.e. regridded `mAtmos.state.F_ρθ_accum[mAtmos.boundary]`) to `F_ρθ_prescribed[mOcean.boundary]` on the `mOcean` grid
    csolver::CplSolver
"""
function preOcean(csolver)
    mOcean = csolver.component_list.domainOcean.component_model
    mAtmos = csolver.component_list.domainAtmos.component_model

    mOcean.odesolver.rhs!.state_auxiliary.F_ρe_prescribed[mOcean.boundary] .=
        coupler_get(csolver.coupler, :BoundaryEnergyFlux, mOcean.grid, DateTime(0), u"J")
end

"""
function postOcean(csolver)
    - updates couplerfield `OceanSurfaceTemerature` with `mOcean.state.T_sfc[mOcean.boundary]` regridded to the coupler grid, and updates the coupler time
    csolver::CplSolver
"""
function postOcean(csolver)
    mOcean = csolver.component_list.domainOcean.component_model
    mAtmos = csolver.component_list.domainAtmos.component_model
    coupler_put!(
        csolver.coupler,
        :SeaSurfaceTemerature,
        mOcean.state.T_sfc[mOcean.boundary],
        mOcean.grid.numerical,
        DateTime(0),
        u"K",
    )
end

# Atmos put and get calls
"""
function preAtmos(csolver)
    - saves and regrids the `SeaSurfaceTemerature` couplerfield (i.e. regridded `mOcean.state.T_sfc[mOcean.boundary]``) on coupler grid to `mAtmos.aux.T_sfc[mAtmos.boundary]` on the `mAtmos` grid
    - resets the accumulated flux `F_ρe_accum` in `mAtmos` to 0
    csolver::CplSolver
"""
function preAtmos(csolver)
    mOcean = csolver.component_list.domainOcean.component_model
    mAtmos = csolver.component_list.domainAtmos.component_model
    # Set boundary T_sfc used in atmos at the start of the coupling cycle.
    mAtmos.odesolver.rhs!.state_auxiliary.T_sfc[mAtmos.boundary] .=
        coupler_get(csolver.coupler, :SeaSurfaceTemerature, mAtmos.grid.numerical, DateTime(0), u"K")
    # Set atmos boundary flux accumulator to 0.
    mAtmos.state.F_ρe_accum .= 0

    # get indicies of the required variables from the `mAtmos` MPIStateArray
    idx = varsindex(vars(mAtmos.state), :ρe)[1]
    idx_rho = varsindex(vars(mAtmos.state), :ρ)[1]

    # Calculate, print and log domain-averaged energy
    FT = eltype(mAtmos.state)

    model_type = cpl_solver.component_list.domainAtmos.component_model.model[1] # TODO: bl should be more accessible
    p = model_type.model.physics.parameters # maybe the parameter list could be saved in the coupler? (this would requre all the compute kernels below to import the coupler)
    nel = mAtmos.grid.resolution.elements.vertical
    po = mAtmos.grid.resolution.polynomial_order.vertical

    Earth_sfc_area = FT(4π * 6371000.0^2)
    ocean_layer_volume = sum((mOcean.state.weights[:, :, mOcean.state.realelems])[mOcean.boundary])
    E_Ocean = weightedsum(mOcean.state, 1) .* p.ρ_o .* p.c_o .* p.h_o ./ ocean_layer_volume # J / m^2
    E_Atmos = weightedsum(mAtmos.state, idx) ./ Earth_sfc_area  # J / m^2

    @info(
        "preatmos",
        time = string(csolver.t) * "/" * string(mAtmos.time.finish),
        total_energyA = E_Ocean,
        total_energyB = E_Atmos,
        total_energy = E_Atmos + E_Ocean,
        Ocean_T_sfc = maximum(mOcean.state.T_sfc[mOcean.boundary]),
        atmos_ρe_sfc = maximum(mAtmos.state.ρe[mAtmos.boundary]),
    )

    isnothing(csolver.fluxlog) ? nothing : csolver.fluxlog.A[csolver.steps] = E_Ocean
    isnothing(csolver.fluxlog) ? nothing : csolver.fluxlog.B[csolver.steps] = E_Atmos
end

"""
function postAtmos(csolver)
    - updates couplerfield `BoundaryEnergyFlux` with mAtmos.state.F_ρe_accum[mAtmos.boundary] regridded to the coupler grid, and updates the coupler time
    csolver::CplSolver
"""
function postAtmos(csolver)
    mOcean = csolver.component_list.domainOcean.component_model
    mAtmos = csolver.component_list.domainAtmos.component_model

    # Pass atmos exports to "coupler" namespace
    # 1. Save mean θ flux at the Atmos boundary during the coupling period

    @info(
        "postatmos",
        time = string(csolver.t) * "/" * string(mAtmos.time.finish),
        Ocean_T_sfc = maximum(mOcean.state.T_sfc[mOcean.boundary]),
        atmos_ρe_sfc = maximum(mAtmos.state.ρe[mAtmos.boundary]),
    )

    coupler_put!(
        csolver.coupler,
        :BoundaryEnergyFlux,
        mAtmos.state.F_ρe_accum[mAtmos.boundary] ./ csolver.dt,
        mAtmos.grid.numerical,
        DateTime(0),
        u"J",
    )
end

max_stars_repo_name: CliMA/CouplerMachine

max_issues_repo_name: CliMA/CouplerMachine


text:
@doc raw"""
    dm(x, y; reduction="mean")
Distortion Measure. This is used as an evalution function of the Kmeans model. This is the expression:
```math
DM(x, y, μ) = \sum^{N-1}_{n=0} \sum^{K-1}_{k=0} y_{nk}|| x_{n} - \mu_{k} ||^2
```

#Example
```jldoctest
julia> model = Kmeans(3);

julia> dm(x, model(x), model.μ);
```
"""
function dm(x::AbstractVector, y::AbstractVector, μ::AbstractMatrix; reduction = "mean")
    J = Array{Float64}(undef, size(x, 1))
    for n in 1 : size(x, 1)
        J[n] = sum(y[n, :] .* sum(x[n, :]' .- μ, dims=2))
    end
    if reduction=="none"
        return J
    elseif reduction=="sum"
        return sum(J)
    elseif reduction=="mean"
        return mean(J)
    else
        throw(ArgumentError("`reduction` must be either `none`, `sum` or `mean`"))
    end
end
function dm(x, y, μ)
    J = Array{Float64}(undef, size(x, 1))
    for n in 1 : size(x, 1)
        J[n] = sum(y[n, :] .* sum(x[n, :]' .- μ, dims=2))
    end
    return J
end
function dm(x::Number, y::Number, μ::AbstractMatrix)
    throw(DomainError("distortion measure doesn't support numbers but Arrays."))
end

function dm(y::AbstractMatrix{TY}, t::AbstractVector{TT}, μ::AbstractMatrix; reduction::String="mean") where {TY, TT}
    if length(filter(!isone, size(y))) != 1
        throw(DimensionMismatch("LossFunctions don't support for Matrix!"))
    end
    dm(vec(y), t, μ, reduction=reduction)
end
function dm(y::AbstractVector{T}, t::Number, μ::AbstractMatrix; reduction::String="mean") where {T}
    dm(y, fill(t, length(y)), μ, reduction=reduction)
end
function dm(y::AbstractMatrix{T}, t::Number, μ::AbstractMatrix; reduction::String="mean") where {T}
    if length(y) == 1
        return dm(y..., t, μ)
    elseif size(y, 1)==1 || size(y, 2)==1
        return dm(vec(y), t, μ, reduction=reduction)
    else
        throw(DimensionMismatch("LossFunctions don't support for Matrix!"))
    end
end

function gauss(x, μ, σ)
    N, D = size(x)
    c1 = 1 / (2 * π)^(D/2)
    c2 = 1 / det(σ)^0.5
    c3 = x .- μ'
    c4 = c3 * σ'
    c5 = zeros(N)
    for d in 1 : D
        c5 += c4[:, d] .* c3[:, d]
    end
    p = @. c1 * c2 * exp(-c5 / 2)
    return p
end

@doc raw"""
    nlh(x, π, μ, σ)
nagative log-likehood. This is used as an evalution function of the GMM model. This is the expression:
```math
E(x, \pi, \mu, \sigma) = - \sum^{N-1}_{n=0} \{ log \sum^{K}_{k=0} π_{k} N(x_{n}|\mu_{k}, \sigma_{k}) \}
```
# Example
```jldoctest
julia> model = GMM(3);

julia> π, μ, σ = model.π, model.μ, model.σ;

julia> nlh(x, π, μ, σ);
```
"""
function nlh(x, π, μ, σ)
    N, D = size(x)
    K = length(π)
    y = Array{Float32}(undef, N, K)
    for k in 1 : K
        y[:, k] = gauss(x, μ[k, :], σ[k, :, :])
    end
    return sum(log.(sum(π' .* y, dims=2)))
end