text:
"""
Basic structure keeping the parameters of all the agents in the current simulation of a model.

# Elements

 - **t**::AbstractFloat Time of the community
 - **N**::Int Number of particles in the community
 - **declaredSymb**::Dict{String,Array{Symbol}} Dictionary storing the names of all the parameters declared in model according to the respective fields where they have been declared.
 - **var**::Array{<:AbstractFloat,2} 2D Array with all the agents in rows and all the corresponding values of the variables in columns.
 - **inter**::Array{<:AbstractFloat,2} 2D Array with all the agents in rows and all the corresponding values of the interaction parameters in columns.
 - **loc**::Array{<:AbstractFloat,2} 2D Array with all the agents in rows and all the corresponding values of the local parameters in columns.
 - **locInter**::Array{<:AbstractFloat,2} 2D Array with all the agents in rows and all the corresponding values of the local interaction parameters in columns.
 - **glob**::Array{<:AbstractFloat,1} 1D Array with all the corresponding values of the global parameters in rows.
 - **globArray**::Array{Array{AbstractFloat},1} 1D Array with all the corresponding global arrays in rows.
 - **ids**::Array{Int,2} 2D Array with all the agents in rows and all the corresponding values of the identities in columns

# Constructors

    function Community(abm::Model; N::Int=1, t::AbstractFloat=0.)

### Arguments
 - **abm** (Model) Agent Model structure

### Additional keyword arguments
 - **N::Int** Number of Agent with wich start the model. N=1 by default.
 - **t::AbstractFloat** Time of the community at creation. t=1. by default
 - **mediumN::Array{Int,1}** Size of the medium grid. (Default [])

### Example
```@julia
 julia> model = Model();
 julia> addLocal!(model,:x);
 julia> community = Community(model, N=2, t=0.5)
 Community(0.5, 2, Dict{String,Array{Symbol,N} where N}("glob" => [],"ids" => [],"locInter" => [],"loc" => [:x],"inter" => [],"var" => []), Array{AbstractFloat}(undef,2,0), Array{AbstractFloat}(undef,2,0), AbstractFloat[0.0; 0.0], AbstractFloat[0.0; 0.0], AbstractFloat[], Array{Int64}(undef,2,0))
 julia> community.N
 2
 julia> community.t
 0.5
 julia> community.declaredSymb
 Dict{String,Array{Symbol,N} where N} with 6 entries:
   "glob"     => Symbol[]
   "ids"      => Symbol[]
   "locInter" => Symbol[]
   "loc"      => [:x]
   "inter"    => Symbol[]
   "var"      => Symbol[]
```

# Base extended methods

In addition for directly accessing the elements of the structure, the following methods from the Base Package has been extended to access the elements of the Community.

    function Base.getindex(a::Community,var::Symbol)

Returns an array with all the values of the declared symbol for all the agents.

Example
```julia
 julia> model.N
 2
 julia> community.declaredSymbols["loc"]
 1-element Array{Symbol,1}:
 :x
 julia> community[:x]
 2-element Array{<:AbstractFloat,1}:
  0.0
  0.0
```

    function Base.setindex!(a::Community,v::Array{<:AbstractFloat},var::Symbol)

Sets the values of a declared symbol to the values of the array v. The array has to be the same length as N.

Example
```@julia
 julia> community[:x] = [1.,2.];
 julia> community[:x]
 2-element Array{Float64,1}:
  1.0
  2.0
```

    function Base.setindex!(a::Community,v::Number,var::Symbol)

Sets the values of a declared symbol to the given value v.

Example
```@julia
julia> community[:x] = 2.;
julia> community[:x]
2-element Array{Float64,1}:
 2.0
 2.0
```
"""
mutable struct Community
    dims::Int
    t::AbstractFloat
    N::Int
    mediumN::Array{Int,1}
    simulationBox::Array{<:AbstractFloat,2}
    radiusInteraction::Union{Real,Array{<:AbstractFloat,1}}
    declaredSymbols_::Dict{String,Array{Symbol}}
    local_::Array{<:AbstractFloat,2}
    identity_::Array{Int,2}
    global_::Array{<:AbstractFloat,1}
    globalArray_::Array{Array{Float64},1}
    medium_::Union{Array{<:AbstractFloat,1},Array{<:AbstractFloat,2},Array{<:AbstractFloat,3},Array{<:AbstractFloat,4}}
end

function Community(abm::Model; N::Int=1, t::AbstractFloat=0., mediumN::Array{Int,1}=Array{Int,1}([]))

    if !isempty(abm.agent.declaredSymbols["Medium"])
        if length(mediumN) != abm.agent.dims
            error("mediumN has to be an array with the same length as Model dimensions specifing the number of points in the grid.")
        end
    end

    dims = abm.agent.dims
    mediumN = mediumN
    simulationBox = zeros(FLOAT,dims,2)
    radiusInteraction = 0.
    loc = zeros(FLOAT,N,length(abm.agent.declaredSymbols["Local"]))
    ids = ones(INT,N,length(abm.agent.declaredSymbols["Identity"]))
    ids[:,1] .= 1:N
    glob = zeros(FLOAT,length(abm.agent.declaredSymbols["Global"]))
    globArray = []
    medium = zeros(FLOAT,mediumN...,length(abm.agent.declaredSymbols["Medium"]))
    for i in abm.agent.declaredSymbols["GlobalArray"]
        push!(globArray,[])
    end

    declaredSymbols = abm.agent.declaredSymbols

    return Community(dims,t,N,mediumN,simulationBox,radiusInteraction,declaredSymbols,loc,ids,glob,globArray,medium)
end

function Base.show(io::IO,com::Community)
    print("PARAMETERS\n")
    for i in keys(com.declaredSymbols_)
        if ! isempty(com.declaredSymbols_[i])
            print(i,"\n\t")
            for j in com.declaredSymbols_[i]
                println(" ",j)
                println(" ",getproperty(com,j))
            end
            print("\n")
        end
    end
end

"""
Structure that basically stores an array of Coomunities at different time points.

# Elements

 - **com** (Array{Community}) Array where the communities are stored

# Constructors

    function CommunityInTime()

Instantiates an empty CommunityInTime folder.

# Base extended methods

    function Base.push!(a::CommunityInTime,c::Community)

Adds one Community element to the CommunityInTime object.

    function Base.length(a::CommunityInTime)

Returns the number of time points of the Community in time.

    function Base.getindex(a::CommunityInTime,var::Int)
    function Base.firstindex(a::CommunityInTime,var::Int)
    function Base.lastindex(a::CommunityInTime,var::Int)

Returns the Community of the corresponding entry.

    function Base.getindex(a::CommunityInTime,var::Symbol)

Returns a 2D array with rows being the agents and the rows the timepoints. If the agent did not existed for certain time point, the extry is filled with a NaN value.
"""
mutable struct CommunityInTime
    com::Array{Community,1}

    function CommunityInTime()

        return new(Community[])
    end
end


text:
"""
MatrixInterp(in::Array{T,N},MTRX,siz) where {T,N}

Interpolate `in` using `MTRX` to grid of size `siz`.
"""
function MatrixInterp(in::Array{T,N},MTRX::SparseMatrixCSC,siz) where {T,N}
#input
l=size(in,1)*size(in,2);
m=size(in,3);
tmp1=reshape(in,l,m)
tmp0=Float64.(.!(isnan.(tmp1)))
tmp1[isnan.(tmp1)].=0.
siz=siz[1],siz[2],m
#matrix product
tmp0=MTRX*tmp0
tmp1=MTRX*tmp1
tmp1=tmp1./tmp0
#this may be redundant:
tmp1[tmp0 .== 0.] .= NaN
#output
out=reshape(tmp1,siz)
m==1 ? out=dropdims(out,dims=3) : nothing
return out
end

## convert2array method:

"""
    convert2array(fld::MeshArray)

Convert MeshArray to Array (or vice versa otherwise)
"""
function convert2array(fld::MeshArray)

if fld.grid.class=="LatLonCap";
    tmp1=cat(fld.f[1],fld.f[2],rotr90(fld.f[4]),rotr90(fld.f[5]);dims=1);
    tmp2=cat(rotl90(fld.f[3]),NaN*fld.f[3],NaN*fld.f[3],NaN*fld.f[3];dims=1);
    arr=cat(tmp1,tmp2;dims=2);
elseif fld.grid.class=="CubeSphere";
    tmp1=cat(fld.f[1],fld.f[2],rotr90(fld.f[4]),rotr90(fld.f[5]);dims=1);
    tmp2=cat(rotl90(fld.f[3]),NaN*fld.f[3],NaN*fld.f[3],NaN*fld.f[3];dims=1);
    tmp0=cat(NaN*fld.f[3],NaN*fld.f[3],NaN*fld.f[3],rotr90(fld.f[6]);dims=1);
    arr=cat(tmp0,tmp1,tmp2;dims=2);
elseif fld.grid.class=="ll";
  arr=fld.f[1];
else;
  error("unknown grTopo case");
end;

return arr;

end

## convert2array ##

function convert2array(fld::Array{T,N},grid::gcmgrid) where {T,N}

grTopo=grid.class
nFaces=grid.nFaces
facesSize=grid.fSize

v1=Array{Array{T,N},1}(undef,nFaces);
N>2 ? error("N>2 case not implemented yet") : nothing

if grTopo=="LatLonCap";
    (n1,n2)=facesSize[1];
    v1[1]=fld[1:n1,1:n2];
    v1[2]=fld[n1+1:n1*2,1:n2];
    v1[3]=rotr90(fld[1:n1,n2+1:n2+n1]);
    v1[4]=rotl90(fld[n1*2+1:n1*3,1:n2]);
    v1[5]=rotl90(fld[n1*3+1:n1*4,1:n2]);
elseif grTopo=="CubeSphere";
    (n1,n2)=facesSize[1];
    v1[1]=fld[1:n1,n1+1:n1+n2];
    v1[2]=fld[n1+1:2*n1,n1+1:n1+n2];
    v1[3]=rotr90(fld[1:n1,n1+n2+1:n2+n1*2]);
    v1[4]=rotl90(fld[n1*2+1:n1*3,n1+1:n1+n2]);
    v1[5]=rotl90(fld[n1*3+1:n1*4,n1+1:n1+n2]);
    v1[6]=rotl90(fld[n1*3+1:n1*4,1:n1]);
elseif grTopo=="ll";
    v1[1]=fld;
else;
  error("unknown grTopo case");
end;

MeshArray(grid,v1);

end

## convert2gcmfaces method:

"""
    convert2gcmfaces(fld::MeshArray)

Convert mitgcm output to MeshArray (or vice versa otherwise)
"""
function convert2gcmfaces(fld::MeshArray)

    grTopo=fld.grid.class
    nFaces=fld.grid.nFaces
    (n1,n2)=fld.grid.ioSize
    facesSize=fld.grid.fSize

    aa=0;bb=0;
    for iFace=1:nFaces;
        aa=aa+prod(facesSize[iFace]);
        bb=bb+prod(size(fld.f[iFace]));
    end;
    n3=Int64(bb/aa);
    v11=NaN*zeros(aa,n3);

    i0=0; i1=0;
    for iFace=1:nFaces;
        i0=i1+1;
        nn=facesSize[iFace][1];
        mm=facesSize[iFace][2];
        i1=i1+nn*mm;
        v11[i0:i1,:]=reshape(fld.f[iFace],(nn*mm,n3));
    end;
    v1=reshape(v11,(n1,n2,n3));
    n3==1 ? v1=dropdims(v1,dims=3) : nothing

    return v1;

end

## convert2gcmfaces ##

function convert2gcmfaces(fld::Array,grid::gcmgrid)

grTopo=grid.class
nFaces=grid.nFaces
(n1,n2)=grid.ioSize
facesSize=grid.fSize

n3=Int64(prod(size(fld))/n1/n2);

v0=reshape(fld,(n1*n2,n3));
v1=Array{Array{eltype(fld),ndims(fld)}}(undef,nFaces);
i0=0; i1=0;
for iFace=1:nFaces
  i0=i1+1;
  nn=facesSize[iFace][1]; mm=facesSize[iFace][2];
  i1=i1+nn*mm;
  if n3>1;
    v1[iFace]=reshape(v0[i0:i1,:],(nn,mm,n3));
  else;
    v1[iFace]=reshape(v0[i0:i1,:],(nn,mm));
  end;
end

MeshArray(grid,v1);

end

ext: jl


text: # Deprecated functionality


text:
module DiracNotation

include("prettyprint.jl")

end # module


text:
using Dates, DelimitedFiles, Plots, Random, ScoreDrivenModels

# Define dates and load historical Affluent Natural Energy data
dates = collect(Date(1961):Month(1):Date(2000, 12))
y = vec(readdlm("../test/data/nie_northeastern.csv"))

y_train = y[1:400]
y_test = y[401:460]

# Set RNG seed to guarantee consistent results
Random.seed!(123)

# Specify GAS model: a lognormal model with time-varying μ, constant σ, and lags 4 and 12
gas = Model([1,2,3,4], [1,2,3,10,11,12], LogNormal, 1.0; time_varying_params=[1])

# Obtain initial parameters to start the GAS recursion
initial_params = dynamic_initial_params(y_train, gas)

# Fit specified model to historical data using initial parameters
f = ScoreDrivenModels.fit!(gas, y_train; initial_params=initial_params)

# Print estimation statistics
results(f)
plot(f)

# Simulate 1000 future scenarios and obtain the 5% and 95% quantiles in each time period
forec = forecast(y_train, gas, 60; S=1000, initial_params=initial_params)

# Plot results
plot(dates[401:460], forec.observation_scenarios, color="grey", w=0.05, label="", ylims=(0, 70))
plot!(dates[360:460], y[360:460], label="NIE", color="black", xlabel="Months", ylabel="GWmed", legend=:topright)
plot!(dates[401:460], forec.observation_quantiles, label=["Quantiles" "" ""], color="red", line=:dash)


text:
"""
  sandbox(test::Bool = true)
---
This function has two nested functions:
- release()
  - This holds the proved workflow steps
- debug()
  - The steps under test

By default, sandbox will call `debug()`. To run `release()`, call `sandbox(true)`.
"""
function sandbox_ReDiverse(test::Bool = true)
    cd(work_dir)
    isdir("tmp") || mkdir("tmp")
    # make()

    dutch = ["d1", "d2", "d3", "v2", "v3", "v7"]
    german = ["v2", "v3"]
    norge = ["v1", "v2", "v7"]
    list = ["dutch-" .* dutch; "german-" .* german; "norge-" .* norge]
    countries = ["dutch", "german", "norge"]

    if test
        message("Testing ...")
        @time calc_grm()
    else
        ##################################################
        ## Data clean, QC of genotypes
        ##################################################
        title("Organize genotypes")
        message("Release version")
        warning("Warning: This will overwrite all previous results!!!")
        warning("Warning: This will take several hours to finish")

        # The workflow
        # -------------------------------------------------
        message("Raw data section, ~11 minutes")
        @time prepare_maps()    # v1-3 d1-3 and d7
        @time update_maps() # only update Dutch and German data to 50k-v3
        @time orgDutchGT()  # merge Dutch final reports to plink
        @time orgGermanGT() # merge German final reports to plink
        @time orgNorgeGT()  # done on 2020-May-19
        @time autosome_subset(list)

        # -------------------------------------------------
        message("Quality control section, <1 min")
        @time plot_lmiss_n_hwe(list)
        @time filter_lowQ_snp(list)
        @time plot_imiss(list, 0.05)
        @time filter_id(list, 0.05)
        # @time find_duplicates() # only need to run once
        @time remove_duplicates(list)
        # QC finishes here, results are in step-5.plk

        # -------------------------------------------------
        message("Merge for imputation section, <1 min")
        @time merge_by_country("dutch", dutch)
        @time merge_by_country("german", german)
        @time merge_by_country("norge", norge)
        @time update_german_id() # to interbull names
        @time merge_dg()         # merge dutch and german data as they are very connected
        message("Imputation, ca 1hr")
        @time imputation_with_beagle() # ca 4455s
        @time rm_country_specific_snp()

        ##################################################
        ## Calculation of GRM, further QC
        ##################################################
        title("GRM related")
        @time calc_grm()
    end
end


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: appconfig
using AWS.Compat
using AWS.UUIDs

"""
    create_application(name)
    create_application(name, params::Dict{String,<:Any})

An application in AppConfig is a logical unit of code that provides capabilities for your
customers. For example, an application can be a microservice that runs on Amazon EC2
instances, a mobile application installed by your users, a serverless application using
Amazon API Gateway and AWS Lambda, or any system you run on behalf of others.

# Arguments
- `name`: A name for the application.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the application.
- `"Tags"`: Metadata to assign to the application. Tags help organize and categorize your
  AppConfig resources. Each tag consists of a key and an optional value, both of which you
  define.
"""
function create_application(Name; aws_config::AbstractAWSConfig=global_aws_config())
    return appconfig(
        "POST",
        "/applications",
        Dict{String,Any}("Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_application(
    Name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "POST",
        "/applications",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Name" => Name), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_configuration_profile(application_id, location_uri, name)
    create_configuration_profile(application_id, location_uri, name, params::Dict{String,<:Any})

Information that enables AppConfig to access the configuration source. Valid configuration
sources include Systems Manager (SSM) documents, SSM Parameter Store parameters, and Amazon
S3 objects. A configuration profile includes the following information.   The Uri location
of the configuration data.   The AWS Identity and Access Management (IAM) role that
provides access to the configuration data.   A validator for the configuration data.
Available validators include either a JSON Schema or an AWS Lambda function.   For more
information, see Create a Configuration and a Configuration Profile in the AWS AppConfig
User Guide.

# Arguments
- `application_id`: The application ID.
- `location_uri`: A URI to locate the configuration. You can specify a Systems Manager
  (SSM) document, an SSM Parameter Store parameter, or an Amazon S3 object. For an SSM
  document, specify either the document name in the format
  ssm-document://<Document_name> or the Amazon Resource Name (ARN). For a parameter,
  specify either the parameter name in the format ssm-parameter://<Parameter_name> or
  the ARN. For an Amazon S3 object, specify the URI in the following format:
  s3://<bucket>/<objectKey> . Here is an example:
  s3://my-bucket/my-app/us-east-1/my-config.json
- `name`: A name for the configuration profile.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the configuration profile.
- `"RetrievalRoleArn"`: The ARN of an IAM role with permission to access the configuration
  at the specified LocationUri.
- `"Tags"`: Metadata to assign to the configuration profile. Tags help organize and
  categorize your AppConfig resources. Each tag consists of a key and an optional value, both
  of which you define.
- `"Validators"`: A list of methods for validating the configuration.
"""
function create_configuration_profile(
    ApplicationId, LocationUri, Name; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/configurationprofiles",
        Dict{String,Any}("LocationUri" => LocationUri, "Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_configuration_profile(
    ApplicationId,
    LocationUri,
    Name,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/configurationprofiles",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("LocationUri" => LocationUri, "Name" => Name),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_deployment_strategy(deployment_duration_in_minutes, growth_factor, name, replicate_to)
    create_deployment_strategy(deployment_duration_in_minutes, growth_factor, name, replicate_to, params::Dict{String,<:Any})

A deployment strategy defines important criteria for rolling out your configuration to the
designated targets. A deployment strategy includes: the overall duration required, a
percentage of targets to receive the deployment during each interval, an algorithm that
defines how percentage grows, and bake time.

# Arguments
- `deployment_duration_in_minutes`: Total amount of time for a deployment to last.
- `growth_factor`: The percentage of targets to receive a deployed configuration during
  each interval.
- `name`: A name for the deployment strategy.
- `replicate_to`: Save the deployment strategy to a Systems Manager (SSM) document.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the deployment strategy.
- `"FinalBakeTimeInMinutes"`: The amount of time AppConfig monitors for alarms before
  considering the deployment to be complete and no longer eligible for automatic roll back.
- `"GrowthType"`: The algorithm used to define how percentage grows over time. AWS
  AppConfig supports the following growth types:  Linear: For this type, AppConfig processes
  the deployment by dividing the total number of targets by the value specified for Step
  percentage. For example, a linear deployment that uses a Step percentage of 10 deploys the
  configuration to 10 percent of the hosts. After those deployments are complete, the system
  deploys the configuration to the next 10 percent. This continues until 100% of the targets
  have successfully received the configuration.  Exponential: For this type, AppConfig
  processes the deployment exponentially using the following formula: G*(2^N). In this
  formula, G is the growth factor specified by the user and N is the number of steps until
  the configuration is deployed to all targets. For example, if you specify a growth factor
  of 2, then the system rolls out the configuration as follows:  2*(2^0)   2*(2^1)   2*(2^2)
  Expressed numerically, the deployment rolls out as follows: 2% of the targets, 4% of the
  targets, 8% of the targets, and continues until the configuration has been deployed to all
  targets.
- `"Tags"`: Metadata to assign to the deployment strategy. Tags help organize and
  categorize your AppConfig resources. Each tag consists of a key and an optional value, both
  of which you define.
"""
function create_deployment_strategy(
    DeploymentDurationInMinutes,
    GrowthFactor,
    Name,
    ReplicateTo;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/deploymentstrategies",
        Dict{String,Any}(
            "DeploymentDurationInMinutes" => DeploymentDurationInMinutes,
            "GrowthFactor" => GrowthFactor,
            "Name" => Name,
            "ReplicateTo" => ReplicateTo,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_deployment_strategy(
    DeploymentDurationInMinutes,
    GrowthFactor,
    Name,
    ReplicateTo,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/deploymentstrategies",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DeploymentDurationInMinutes" => DeploymentDurationInMinutes,
                    "GrowthFactor" => GrowthFactor,
                    "Name" => Name,
                    "ReplicateTo" => ReplicateTo,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_environment(application_id, name)
    create_environment(application_id, name, params::Dict{String,<:Any})

For each application, you define one or more environments. An environment is a logical
deployment group of AppConfig targets, such as applications in a Beta or Production
environment. You can also define environments for application subcomponents such as the
Web, Mobile and Back-end components for your application. You can configure Amazon
CloudWatch alarms for each environment. The system monitors alarms during a configuration
deployment. If an alarm is triggered, the system rolls back the configuration.

# Arguments
- `application_id`: The application ID.
- `name`: A name for the environment.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the environment.
- `"Monitors"`: Amazon CloudWatch alarms to monitor during the deployment process.
- `"Tags"`: Metadata to assign to the environment. Tags help organize and categorize your
  AppConfig resources. Each tag consists of a key and an optional value, both of which you
  define.
"""
function create_environment(
    ApplicationId, Name; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/environments",
        Dict{String,Any}("Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_environment(
    ApplicationId,
    Name,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/environments",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Name" => Name), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_hosted_configuration_version(application_id, configuration_profile_id, content, content-_type)
    create_hosted_configuration_version(application_id, configuration_profile_id, content, content-_type, params::Dict{String,<:Any})

Create a new configuration in the AppConfig configuration store.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The configuration profile ID.
- `content`: The content of the configuration or the configuration data.
- `content-_type`: A standard MIME type describing the format of the configuration content.
  For more information, see Content-Type.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the configuration.
- `"Latest-Version-Number"`: An optional locking token used to prevent race conditions from
  overwriting configuration updates when creating a new version. To ensure your data is not
  overwritten when creating multiple hosted configuration versions in rapid succession,
  specify the version of the latest hosted configuration version.
"""
function create_hosted_configuration_version(
    ApplicationId,
    ConfigurationProfileId,
    Content,
    Content_Type;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions",
        Dict{String,Any}(
            "Content" => Content,
            "headers" => Dict{String,Any}("Content-Type" => Content_Type),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_hosted_configuration_version(
    ApplicationId,
    ConfigurationProfileId,
    Content,
    Content_Type,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Content" => Content,
                    "headers" => Dict{String,Any}("Content-Type" => Content_Type),
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_application(application_id)
    delete_application(application_id, params::Dict{String,<:Any})

Delete an application. Deleting an application does not delete a configuration from a host.

# Arguments
- `application_id`: The ID of the application to delete.

"""
function delete_application(
    ApplicationId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_application(
    ApplicationId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_configuration_profile(application_id, configuration_profile_id)
    delete_configuration_profile(application_id, configuration_profile_id, params::Dict{String,<:Any})

Delete a configuration profile. Deleting a configuration profile does not delete a
configuration from a host.

# Arguments
- `application_id`: The application ID that includes the configuration profile you want to
  delete.
- `configuration_profile_id`: The ID of the configuration profile you want to delete.

"""
function delete_configuration_profile(
    ApplicationId, ConfigurationProfileId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_configuration_profile(
    ApplicationId,
    ConfigurationProfileId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_deployment_strategy(deployment_strategy_id)
    delete_deployment_strategy(deployment_strategy_id, params::Dict{String,<:Any})

Delete a deployment strategy. Deleting a deployment strategy does not delete a
configuration from a host.

# Arguments
- `deployment_strategy_id`: The ID of the deployment strategy you want to delete.

"""
function delete_deployment_strategy(
    DeploymentStrategyId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "DELETE",
        "/deployementstrategies/$(DeploymentStrategyId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_deployment_strategy(
    DeploymentStrategyId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/deployementstrategies/$(DeploymentStrategyId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_environment(application_id, environment_id)
    delete_environment(application_id, environment_id, params::Dict{String,<:Any})

Delete an environment. Deleting an environment does not delete a configuration from a host.

# Arguments
- `application_id`: The application ID that includes the environment you want to delete.
- `environment_id`: The ID of the environment you want to delete.

"""
function delete_environment(
    ApplicationId, EnvironmentId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_environment(
    ApplicationId,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_hosted_configuration_version(application_id, configuration_profile_id, version_number)
    delete_hosted_configuration_version(application_id, configuration_profile_id, version_number, params::Dict{String,<:Any})

Delete a version of a configuration from the AppConfig configuration store.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The configuration profile ID.
- `version_number`: The versions number to delete.

"""
function delete_hosted_configuration_version(
    ApplicationId,
    ConfigurationProfileId,
    VersionNumber;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions/$(VersionNumber)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_hosted_configuration_version(
    ApplicationId,
    ConfigurationProfileId,
    VersionNumber,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions/$(VersionNumber)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_application(application_id)
    get_application(application_id, params::Dict{String,<:Any})

Retrieve information about an application.

# Arguments
- `application_id`: The ID of the application you want to get.

"""
function get_application(ApplicationId; aws_config::AbstractAWSConfig=global_aws_config())
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_application(
    ApplicationId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_configuration(application, configuration, environment, client_id)
    get_configuration(application, configuration, environment, client_id, params::Dict{String,<:Any})

Receive information about a configuration.  AWS AppConfig uses the value of the
ClientConfigurationVersion parameter to identify the configuration version on your clients.
If you don’t send ClientConfigurationVersion with each call to GetConfiguration, your
clients receive the current configuration. You are charged each time your clients receive a
configuration. To avoid excess charges, we recommend that you include the
ClientConfigurationVersion value with every call to GetConfiguration. This value must be
saved on your client. Subsequent calls to GetConfiguration must pass this value by using
the ClientConfigurationVersion parameter.

# Arguments
- `application`: The application to get. Specify either the application name or the
  application ID.
- `configuration`: The configuration to get. Specify either the configuration name or the
  configuration ID.
- `environment`: The environment to get. Specify either the environment name or the
  environment ID.
- `client_id`: A unique ID to identify the client for the configuration. This ID enables
  AppConfig to deploy the configuration in intervals, as defined in the deployment strategy.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"client_configuration_version"`: The configuration version returned in the most recent
  GetConfiguration response.  AWS AppConfig uses the value of the ClientConfigurationVersion
  parameter to identify the configuration version on your clients. If you don’t send
  ClientConfigurationVersion with each call to GetConfiguration, your clients receive the
  current configuration. You are charged each time your clients receive a configuration. To
  avoid excess charges, we recommend that you include the ClientConfigurationVersion value
  with every call to GetConfiguration. This value must be saved on your client. Subsequent
  calls to GetConfiguration must pass this value by using the ClientConfigurationVersion
  parameter.   For more information about working with configurations, see Retrieving the
  Configuration in the AWS AppConfig User Guide.
"""
function get_configuration(
    Application,
    Configuration,
    Environment,
    client_id;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(Application)/environments/$(Environment)/configurations/$(Configuration)",
        Dict{String,Any}("client_id" => client_id);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_configuration(
    Application,
    Configuration,
    Environment,
    client_id,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(Application)/environments/$(Environment)/configurations/$(Configuration)",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("client_id" => client_id), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_configuration_profile(application_id, configuration_profile_id)
    get_configuration_profile(application_id, configuration_profile_id, params::Dict{String,<:Any})

Retrieve information about a configuration profile.

# Arguments
- `application_id`: The ID of the application that includes the configuration profile you
  want to get.
- `configuration_profile_id`: The ID of the configuration profile you want to get.

"""
function get_configuration_profile(
    ApplicationId, ConfigurationProfileId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_configuration_profile(
    ApplicationId,
    ConfigurationProfileId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_deployment(application_id, deployment_number, environment_id)
    get_deployment(application_id, deployment_number, environment_id, params::Dict{String,<:Any})

Retrieve information about a configuration deployment.

# Arguments
- `application_id`: The ID of the application that includes the deployment you want to get.
- `deployment_number`: The sequence number of the deployment.
- `environment_id`: The ID of the environment that includes the deployment you want to get.

"""
function get_deployment(
    ApplicationId,
    DeploymentNumber,
    EnvironmentId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments/$(DeploymentNumber)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_deployment(
    ApplicationId,
    DeploymentNumber,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments/$(DeploymentNumber)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_deployment_strategy(deployment_strategy_id)
    get_deployment_strategy(deployment_strategy_id, params::Dict{String,<:Any})

Retrieve information about a deployment strategy. A deployment strategy defines important
criteria for rolling out your configuration to the designated targets. A deployment
strategy includes: the overall duration required, a percentage of targets to receive the
deployment during each interval, an algorithm that defines how percentage grows, and bake
time.

# Arguments
- `deployment_strategy_id`: The ID of the deployment strategy to get.

"""
function get_deployment_strategy(
    DeploymentStrategyId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/deploymentstrategies/$(DeploymentStrategyId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_deployment_strategy(
    DeploymentStrategyId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/deploymentstrategies/$(DeploymentStrategyId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_environment(application_id, environment_id)
    get_environment(application_id, environment_id, params::Dict{String,<:Any})

Retrieve information about an environment. An environment is a logical deployment group of
AppConfig applications, such as applications in a Production environment or in an EU_Region
environment. Each configuration deployment targets an environment. You can enable one or
more Amazon CloudWatch alarms for an environment. If an alarm is triggered during a
deployment, AppConfig roles back the configuration.

# Arguments
- `application_id`: The ID of the application that includes the environment you want to get.
- `environment_id`: The ID of the environment you wnat to get.

"""
function get_environment(
    ApplicationId, EnvironmentId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_environment(
    ApplicationId,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_hosted_configuration_version(application_id, configuration_profile_id, version_number)
    get_hosted_configuration_version(application_id, configuration_profile_id, version_number, params::Dict{String,<:Any})

Get information about a specific configuration version.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The configuration profile ID.
- `version_number`: The version.

"""
function get_hosted_configuration_version(
    ApplicationId,
    ConfigurationProfileId,
    VersionNumber;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions/$(VersionNumber)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_hosted_configuration_version(
    ApplicationId,
    ConfigurationProfileId,
    VersionNumber,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions/$(VersionNumber)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_applications()
    list_applications(params::Dict{String,<:Any})

List all applications in your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of items to return for this call. The call also
  returns a token that you can specify in a subsequent call to get the next set of results.
- `"next_token"`: A token to start the list. Use this token to get the next set of results.
"""
function list_applications(; aws_config::AbstractAWSConfig=global_aws_config())
    return appconfig(
        "GET", "/applications"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_applications(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/applications",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_configuration_profiles(application_id)
    list_configuration_profiles(application_id, params::Dict{String,<:Any})

Lists the configuration profiles for an application.

# Arguments
- `application_id`: The application ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of items to return for this call. The call also
  returns a token that you can specify in a subsequent call to get the next set of results.
- `"next_token"`: A token to start the list. Use this token to get the next set of results.
"""
function list_configuration_profiles(
    ApplicationId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_configuration_profiles(
    ApplicationId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_deployment_strategies()
    list_deployment_strategies(params::Dict{String,<:Any})

List deployment strategies.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of items to return for this call. The call also
  returns a token that you can specify in a subsequent call to get the next set of results.
- `"next_token"`: A token to start the list. Use this token to get the next set of results.
"""
function list_deployment_strategies(; aws_config::AbstractAWSConfig=global_aws_config())
    return appconfig(
        "GET",
        "/deploymentstrategies";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_deployment_strategies(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/deploymentstrategies",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_deployments(application_id, environment_id)
    list_deployments(application_id, environment_id, params::Dict{String,<:Any})

Lists the deployments for an environment.

# Arguments
- `application_id`: The application ID.
- `environment_id`: The environment ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of items to return for this call. The call also
  returns a token that you can specify in a subsequent call to get the next set of results.
- `"next_token"`: A token to start the list. Use this token to get the next set of results.
"""
function list_deployments(
    ApplicationId, EnvironmentId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_deployments(
    ApplicationId,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_environments(application_id)
    list_environments(application_id, params::Dict{String,<:Any})

List the environments for an application.

# Arguments
- `application_id`: The application ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of items to return for this call. The call also
  returns a token that you can specify in a subsequent call to get the next set of results.
- `"next_token"`: A token to start the list. Use this token to get the next set of results.
"""
function list_environments(ApplicationId; aws_config::AbstractAWSConfig=global_aws_config())
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_environments(
    ApplicationId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/environments",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_hosted_configuration_versions(application_id, configuration_profile_id)
    list_hosted_configuration_versions(application_id, configuration_profile_id, params::Dict{String,<:Any})

View a list of configurations stored in the AppConfig configuration store by version.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The configuration profile ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max_results"`: The maximum number of items to return for this call. The call also
  returns a token that you can specify in a subsequent call to get the next set of results.
- `"next_token"`: A token to start the list. Use this token to get the next set of results.
"""
function list_hosted_configuration_versions(
    ApplicationId, ConfigurationProfileId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_hosted_configuration_versions(
    ApplicationId,
    ConfigurationProfileId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/hostedconfigurationversions",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_tags_for_resource(resource_arn)
    list_tags_for_resource(resource_arn, params::Dict{String,<:Any})

Retrieves the list of key-value tags assigned to the resource.

# Arguments
- `resource_arn`: The resource ARN.

"""
function list_tags_for_resource(
    ResourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "GET",
        "/tags/$(ResourceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    ResourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "GET",
        "/tags/$(ResourceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    start_deployment(application_id, configuration_profile_id, configuration_version, deployment_strategy_id, environment_id)
    start_deployment(application_id, configuration_profile_id, configuration_version, deployment_strategy_id, environment_id, params::Dict{String,<:Any})

Starts a deployment.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The configuration profile ID.
- `configuration_version`: The configuration version to deploy.
- `deployment_strategy_id`: The deployment strategy ID.
- `environment_id`: The environment ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the deployment.
- `"Tags"`: Metadata to assign to the deployment. Tags help organize and categorize your
  AppConfig resources. Each tag consists of a key and an optional value, both of which you
  define.
"""
function start_deployment(
    ApplicationId,
    ConfigurationProfileId,
    ConfigurationVersion,
    DeploymentStrategyId,
    EnvironmentId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments",
        Dict{String,Any}(
            "ConfigurationProfileId" => ConfigurationProfileId,
            "ConfigurationVersion" => ConfigurationVersion,
            "DeploymentStrategyId" => DeploymentStrategyId,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_deployment(
    ApplicationId,
    ConfigurationProfileId,
    ConfigurationVersion,
    DeploymentStrategyId,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ConfigurationProfileId" => ConfigurationProfileId,
                    "ConfigurationVersion" => ConfigurationVersion,
                    "DeploymentStrategyId" => DeploymentStrategyId,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    stop_deployment(application_id, deployment_number, environment_id)
    stop_deployment(application_id, deployment_number, environment_id, params::Dict{String,<:Any})

Stops a deployment. This API action works only on deployments that have a status of
DEPLOYING. This action moves the deployment to a status of ROLLED_BACK.

# Arguments
- `application_id`: The application ID.
- `deployment_number`: The sequence number of the deployment.
- `environment_id`: The environment ID.

"""
function stop_deployment(
    ApplicationId,
    DeploymentNumber,
    EnvironmentId;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments/$(DeploymentNumber)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function stop_deployment(
    ApplicationId,
    DeploymentNumber,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)/deployments/$(DeploymentNumber)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    tag_resource(resource_arn, tags)
    tag_resource(resource_arn, tags, params::Dict{String,<:Any})

Metadata to assign to an AppConfig resource. Tags help organize and categorize your
AppConfig resources. Each tag consists of a key and an optional value, both of which you
define. You can specify a maximum of 50 tags for a resource.

# Arguments
- `resource_arn`: The ARN of the resource for which to retrieve tags.
- `tags`: The key-value string map. The valid character set is [a-zA-Z+-=._:/]. The tag key
  can be up to 128 characters and must not start with aws:. The tag value can be up to 256
  characters.

"""
function tag_resource(ResourceArn, Tags; aws_config::AbstractAWSConfig=global_aws_config())
    return appconfig(
        "POST",
        "/tags/$(ResourceArn)",
        Dict{String,Any}("Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    ResourceArn,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/tags/$(ResourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Tags" => Tags), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    untag_resource(resource_arn, tag_keys)
    untag_resource(resource_arn, tag_keys, params::Dict{String,<:Any})

Deletes a tag key and value from an AppConfig resource.

# Arguments
- `resource_arn`: The ARN of the resource for which to remove tags.
- `tag_keys`: The tag keys to delete.

"""
function untag_resource(
    ResourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "DELETE",
        "/tags/$(ResourceArn)",
        Dict{String,Any}("tagKeys" => tagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    ResourceArn,
    tagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "DELETE",
        "/tags/$(ResourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("tagKeys" => tagKeys), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_application(application_id)
    update_application(application_id, params::Dict{String,<:Any})

Updates an application.

# Arguments
- `application_id`: The application ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the application.
- `"Name"`: The name of the application.
"""
function update_application(
    ApplicationId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "PATCH",
        "/applications/$(ApplicationId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_application(
    ApplicationId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "PATCH",
        "/applications/$(ApplicationId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_configuration_profile(application_id, configuration_profile_id)
    update_configuration_profile(application_id, configuration_profile_id, params::Dict{String,<:Any})

Updates a configuration profile.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The ID of the configuration profile.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the configuration profile.
- `"Name"`: The name of the configuration profile.
- `"RetrievalRoleArn"`: The ARN of an IAM role with permission to access the configuration
  at the specified LocationUri.
- `"Validators"`: A list of methods for validating the configuration.
"""
function update_configuration_profile(
    ApplicationId, ConfigurationProfileId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "PATCH",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_configuration_profile(
    ApplicationId,
    ConfigurationProfileId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "PATCH",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_deployment_strategy(deployment_strategy_id)
    update_deployment_strategy(deployment_strategy_id, params::Dict{String,<:Any})

Updates a deployment strategy.

# Arguments
- `deployment_strategy_id`: The deployment strategy ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DeploymentDurationInMinutes"`: Total amount of time for a deployment to last.
- `"Description"`: A description of the deployment strategy.
- `"FinalBakeTimeInMinutes"`: The amount of time AppConfig monitors for alarms before
  considering the deployment to be complete and no longer eligible for automatic roll back.
- `"GrowthFactor"`: The percentage of targets to receive a deployed configuration during
  each interval.
- `"GrowthType"`: The algorithm used to define how percentage grows over time. AWS
  AppConfig supports the following growth types:  Linear: For this type, AppConfig processes
  the deployment by increments of the growth factor evenly distributed over the deployment
  time. For example, a linear deployment that uses a growth factor of 20 initially makes the
  configuration available to 20 percent of the targets. After 1/5th of the deployment time
  has passed, the system updates the percentage to 40 percent. This continues until 100% of
  the targets are set to receive the deployed configuration.  Exponential: For this type,
  AppConfig processes the deployment exponentially using the following formula: G*(2^N). In
  this formula, G is the growth factor specified by the user and N is the number of steps
  until the configuration is deployed to all targets. For example, if you specify a growth
  factor of 2, then the system rolls out the configuration as follows:  2*(2^0)   2*(2^1)
  2*(2^2)  Expressed numerically, the deployment rolls out as follows: 2% of the targets, 4%
  of the targets, 8% of the targets, and continues until the configuration has been deployed
  to all targets.
"""
function update_deployment_strategy(
    DeploymentStrategyId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "PATCH",
        "/deploymentstrategies/$(DeploymentStrategyId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_deployment_strategy(
    DeploymentStrategyId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "PATCH",
        "/deploymentstrategies/$(DeploymentStrategyId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_environment(application_id, environment_id)
    update_environment(application_id, environment_id, params::Dict{String,<:Any})

Updates an environment.

# Arguments
- `application_id`: The application ID.
- `environment_id`: The environment ID.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the environment.
- `"Monitors"`: Amazon CloudWatch alarms to monitor during the deployment process.
- `"Name"`: The name of the environment.
"""
function update_environment(
    ApplicationId, EnvironmentId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return appconfig(
        "PATCH",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_environment(
    ApplicationId,
    EnvironmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "PATCH",
        "/applications/$(ApplicationId)/environments/$(EnvironmentId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    validate_configuration(application_id, configuration_profile_id, configuration_version)
    validate_configuration(application_id, configuration_profile_id, configuration_version, params::Dict{String,<:Any})

Uses the validators in a configuration profile to validate a configuration.

# Arguments
- `application_id`: The application ID.
- `configuration_profile_id`: The configuration profile ID.
- `configuration_version`: The version of the configuration to validate.

"""
function validate_configuration(
    ApplicationId,
    ConfigurationProfileId,
    configuration_version;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/validators",
        Dict{String,Any}("configuration_version" => configuration_version);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function validate_configuration(
    ApplicationId,
    ConfigurationProfileId,
    configuration_version,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return appconfig(
        "POST",
        "/applications/$(ApplicationId)/configurationprofiles/$(ConfigurationProfileId)/validators",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("configuration_version" => configuration_version),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


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

"""
The mutable struct representing an abstract (version unknown) FMU.
"""
abstract type FMU end

include("FMI1.jl")
include("FMI2.jl")
include("FMI3.jl")
include("assertions.jl")

# ToDo: Submodules
include("FMI2_sim.jl")
include("FMI_plot.jl")

### EXPORTING LISTS START ###

export FMU

# FMI.jl
export prepareValueReference
export fmiLoad, 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, fmi2EnterEventMode, 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 fmiString2ValueReference
export fmiGetDependencies
export fmiGetStartValue

# FMI2.jl
export fmi2Dependency, fmi2DependencyDependent, fmi2DependencyIndependent, fmi2DependencyUnknown, fmi2DependencyFixed, fmi2GetDependencies
export FMU2, fmi2True, fmi2False
export fmi2SimulationResult, fmi2SimulationResultGetValuesAtIndex, fmi2SimulationResultGetTime, fmi2SimulationResultGetValues
export fmi2ValueReference, fmi2String2ValueReference, fmi2ValueReference2String
export fmi2Unzip, fmi2Load, fmi2Unload
export fmi2GetTypesPlatform, fmi2GetVersion
export fmi2Instantiate!, fmi2FreeInstance!, fmi2SetDebugLogging
export fmi2SetupExperiment, fmi2EnterInitializationMode, fmi2ExitInitializationMode, fmi2Terminate, fmi2Reset
export fmi2GetReal, fmi2SetReal, fmi2GetInteger, fmi2SetInteger, fmi2GetBoolean, fmi2SetBoolean, fmi2GetString, fmi2SetString
export fmi2GetFMUstate, fmi2SetFMUstate, fmi2FreeFMUstate, fmi2SerializedFMUstateSize, fmi2SerializeFMUstate, fmi2DeSerializeFMUstate
export fmi2GetDirectionalDerivative, fmi2SampleDirectionalDerivative, fmi2GetDirectionalDerivative!, fmi2SampleDirectionalDerivative!
export fmi2SetRealInputDerivatives, fmi2GetRealOutputDerivatives
export fmi2DoStep, fmi2CancelStep
export fmi2GetStatus, fmi2GetRealStatus, fmi2GetIntegerStatus, fmi2GetBooleanStatus, fmi2GetStringStatus
export fmi2SetTime, fmi2SetContinuousStates
export fmi2EnterEventMode, fmi2NewDiscreteStates, fmi2EnterContinuousTimeMode, fmi2CompletedIntegratorStep, fmi2GetDerivatives, fmi2GetEventIndicators, fmi2GetContinuousStates, fmi2GetNominalsOfContinuousStates
export fmi2Info, fmi2PrintDependencies, fmi2DependenciesSupported, fmi2VariableDependsOnVariable
export fmi2GetStartValue

# FMI2_comp.jl
# already exported above

# FMI2_c.jl
export fmi2Component
export fmi2Char, fmi2String, fmi2Boolean, fmi2Real, fmi2Integer, fmi2Byte, fmi2FMUstate, fmi2ComponentEnvironment, fmi2Enum

# FMI2_sim.jl
export fmi2Simulate, fmi2SimulateCS, fmi2SimulateME
# export stepCompleted, affect!, condition, handleEvents

# FMI_plot.jl
export fmiPlot

# FMI2_md.jl
export fmi2GetModelName, fmi2GetGUID
export fmi2GetGenerationTool, fmi2GetGenerationDateAndTime
export fmi2GetVariableNamingConvention, fmi2GetNumberOfEventIndicators
export fmi2CanGetSetState, fmi2CanSerializeFMUstate
export fmi2ProvidesDirectionalDerivative
export fmi2IsCoSimulation, fmi2IsModelExchange
export fmi2ModelVariablesForValueReference

### EXPORTING LISTS END ###

fmi2Struct = Union{FMU2, fmi2Component}

# Receives one or an array of value references in an arbitrary format (see fmi2ValueReferenceFormat) and converts it into an Array{fmi2ValueReference} (if not already).
function prepareValueReference(md::fmi2ModelDescription, vr::fmi2ValueReferenceFormat)
    tvr = typeof(vr)
    if tvr == Array{fmi2ValueReference,1}
        return vr
    elseif tvr == fmi2ValueReference
        return [vr]
    elseif tvr == String
        return [fmi2String2ValueReference(md, vr)]
    elseif tvr == Array{String,1}
        return fmi2String2ValueReference(md, vr)
    elseif tvr == Int64
        return [fmi2ValueReference(vr)]
    elseif tvr == Array{Int64,1}
        return fmi2ValueReference.(vr)
    elseif tvr == Nothing
        return Array{fmi2ValueReference,1}()
    end

    @assert false "prepareValueReference(...): Unknown value reference structure `$tvr`."
end
function prepareValueReference(fmu::FMU2, vr::fmi2ValueReferenceFormat)
    prepareValueReference(fmu.modelDescription, vr)
end
function prepareValueReference(comp::fmi2Component, vr::fmi2ValueReferenceFormat)
    prepareValueReference(comp.fmu.modelDescription, vr)
end

# Receives one or an array of values and converts it into an Array{typeof(value)} (if not already).
function prepareValue(value)
    if isa(value, Array) && length(size(value)) == 1
        return value
    else
        return [value]
    end

    @assert false "prepareValue(...): Unknown dimension of structure `$dim`."
end

"""
Returns the FMU's depndency-matrix for fast look-ups on variable dependencies.

Enter ```fmi2GetDependencies``` for more information.
"""
function fmiGetDependencies(fmu::FMU2)
    fmi2GetDependencies(fmu)
end

"""
Returns the ValueReference coresponding to the variable name.

Enter ```fmi2String2ValueReference``` for more information.
"""
function fmiString2ValueReference(dataStruct::Union{FMU2, fmi2ModelDescription}, identifier::Union{String, Array{String}})
    fmi2String2ValueReference(dataStruct, identifier)
end

# Wrapping modelDescription Functions
"""
Returns the tag 'modelName' from the model description.
"""
function fmiGetModelName(fmu::FMU2)
    fmi2GetModelName(fmu)
end

"""
Returns the tag 'guid' from the model description.
"""
function fmiGetGUID(fmu::FMU2)
    fmi2GetGUID(fmu)
end

"""
Returns the tag 'generationtool' from the model description.
"""
function fmiGetGenerationTool(fmu::FMU2)
    fmi2GetGenerationTool(fmu)
end

"""
Returns the tag 'generationdateandtime' from the model description.
"""
function fmiGetGenerationDateAndTime(fmu::FMU2)
    fmi2GetGenerationDateAndTime(fmu)
end

"""
Returns the tag 'varaiblenamingconvention' from the model description.
"""
function fmiGetVariableNamingConvention(fmu::FMU2)
    fmi2GetVariableNamingConvention(fmu)
end

"""
Returns the tag 'numberOfEventIndicators' from the model description.
"""
function fmiGetNumberOfEventIndicators(fmu::FMU2)
    fmi2GetNumberOfEventIndicators(fmu)
end

"""
Returns the tag 'modelIdentifier' from CS or ME section.
"""
function fmiGetModelIdentifier(fmu::FMU2)
    fmi2GetModelIdentifier(fmu.modelDescription; type=fmu.type)
end

"""
Returns true, if the FMU supports the getting/setting of states
"""
function fmiCanGetSetState(fmu::FMU2)
    fmi2CanGetSetState(fmu)
end

"""
Returns true, if the FMU state can be serialized
"""
function fmiCanSerializeFMUstate(fmu::FMU2)
    fmi2CanSerializeFMUstate(fmu)
end

"""
Returns true, if the FMU provides directional derivatives
"""
function fmiProvidesDirectionalDerivative(fmu::FMU2)
    fmi2ProvidesDirectionalDerivative(fmu)
end

"""
Returns true, if the FMU supports co simulation
"""
function fmiIsCoSimulation(fmu::FMU2)
    fmi2IsCoSimulation(fmu)
end

"""
Returns true, if the FMU supports model exchange
"""
function fmiIsModelExchange(fmu::FMU2)
    fmi2IsModelExchange(fmu)
end

# Multiple Dispatch variants for FMUs with version 2.0.X

"""
Load FMUs independent of the FMI version, currently supporting version 2.0.X.
"""
function fmiLoad(pathToFMU::String; unpackPath=nothing, type=nothing)
    fmi2Load(pathToFMU; unpackPath=unpackPath, type=type)
end

"""
Simulate an fmu according to its standard from 0.0 to t_stop.
"""
function fmiSimulate(str::fmi2Struct, t_start::Real = 0.0, t_stop::Real = 1.0; kwargs...)
    fmi2Simulate(str, t_start, t_stop; kwargs...)
end

"""
Simulate an CoSimulation fmu according to its standard from 0.0 to t_stop.
"""
function fmiSimulateCS(str::fmi2Struct, t_start::Real = 0.0, t_stop::Real = 1.0; kwargs...)
    fmi2SimulateCS(str, t_start, t_stop; kwargs...)
end

"""
Simulate an ModelExchange fmu according to its standard from 0.0 to t_stop.
"""
function fmiSimulateME(str::fmi2Struct, t_start::Real = 0.0, t_stop::Real = 1.0; kwargs...)
    fmi2SimulateME(str, t_start, t_stop; kwargs...)
end

"""
Unloads the FMU and all its instances and frees the allocated memory.
"""
function fmiUnload(fmu::FMU2)
    fmi2Unload(fmu)
end

"""
Returns the number of states of the FMU.
"""
function fmiGetNumberOfStates(fmu::FMU2)
    fmi2GetNumberOfStates(fmu)
end

"""
Returns the header file used to compile the FMU. By default returns `default`, version independent.
"""
function fmiGetTypesPlatform(fmu::FMU2)
    fmi2GetTypesPlatform(fmu)
end

"""
Returns the version of the FMU, version independent.
"""
function fmiGetVersion(fmu::FMU2)
    fmi2GetVersion(fmu)
end

"""
Prints FMU-specific information into the REPL.
"""
function fmiInfo(fmu::FMU2)
    fmi2Info(fmu)
end

"""
Creates a new instance of the FMU, version independent.
"""
function fmiInstantiate!(fmu::FMU2; visible::Bool = false, loggingOn::Bool = false)

    version = fmiGetVersion(fmu)

    if version == "2.0"
        return fmi2Instantiate!(fmu; visible, loggingOn)
    else
        @assert false ["fmiInstantiate!(...): Unknown FMI version $version !"]
    end

    nothing
end

"""
Frees the allocated memory of the last instance of the FMU.
"""
function fmiFreeInstance!(s::fmi2Struct)
    fmi2FreeInstance!(s)
end

"""
Control the use of the logging callback function, version independent.
"""
function fmiSetDebugLogging(s::fmi2Struct)
    fmi2SetDebugLogging(s)
end

"""
Initialize the Simulation boundries
"""
function fmiSetupExperiment(fmu::fmi2Struct, startTime::Real = 0.0, stopTime::Real = startTime; tolerance::Real = 0.0)
    fmi2SetupExperiment(fmu, startTime, stopTime; tolerance=tolerance)
end

"""
Informs the FMU to enter initializaton mode, version independent.
"""
function fmiEnterInitializationMode(s::fmi2Struct)
    fmi2EnterInitializationMode(s)
end

"""
Informs the FMU to exit initialization mode, version independent.
"""
function fmiExitInitializationMode(s::fmi2Struct)
    fmi2ExitInitializationMode(s)
end

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

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

"""
Returns the real values of an array of variables
"""
function fmiGetReal(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat)
    fmi2GetReal(fmu, vr)
end

function fmiGetRealOutputDerivatives(str::fmi2Struct, vr::fmi2ValueReferenceFormat, order::Union{Array{<:Integer}, <:Integer})
    fmi2GetRealOutputDerivatives(str, vr, order)
end

"""
Writes the real values of an array of variables in the given field
"""
function fmiGetReal!(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{<:Real}, <:Real})
    fmi2GetReal!(fmu, vr, values)
end

"""
Set the values of an array of real variables
"""
function fmiSetReal(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{<:Real}, <:Real})
    fmi2SetReal(fmu, vr, values)
end

"""
ToDo
"""
function fmiSetRealInputDerivatives(str::fmi2Struct, vr::fmi2ValueReferenceFormat, order, values)
    fmi2SetRealInputDerivatives(str, vr, order, values)
end

"""
Returns the integer values of an array of variables
"""
function fmiGetInteger(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat)
    fmi2GetInteger(fmu, vr)
end

"""
Writes the integer values of an array of variables in the given field
"""
function fmiGetInteger!(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{<:Integer}, <:Integer})
    fmi2GetInteger!(fmu, vr, values)
end

"""
Set the values of an array of integer variables
"""
function fmiSetInteger(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{<:Integer}, <:Integer})
    fmi2SetInteger(fmu, vr, values)
end

"""
Returns the boolean values of an array of variables
"""
function fmiGetBoolean(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat)
    fmi2GetBoolean(fmu, vr)
end

"""
Writes the boolean values of an array of variables in the given field
"""
function fmiGetBoolean!(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{Bool}, Bool, Array{fmi2Boolean}})
    fmi2GetBoolean!(fmu, vr, values)
end

"""
Set the values of an array of boolean variables
"""
function fmiSetBoolean(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{Bool}, Bool})
    fmi2SetBoolean(fmu, vr, values)
end

"""
Returns the string values of an array of variables
"""
function fmiGetString(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat)
    fmi2GetString(fmu, vr)
end

"""
Writes the string values of an array of variables in the given field
"""
function fmiGetString!(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{String}, String})
    fmi2GetString!(fmu, vr, values)
end

"""
Set the values of an array of string variables
"""
function fmiSetString(fmu::fmi2Struct, vr::fmi2ValueReferenceFormat, values::Union{Array{String}, String})
    fmi2SetString(fmu, vr, values)
end

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

"""
Sets the FMU to the given state
"""
function fmiSetFMUstate(fmu2::fmi2Struct, state::fmi2FMUstate)
    fmi2SetFMUstate(fmu2, state)
end

"""
Free the memory for the allocated FMU state
    """
function fmiFreeFMUstate(fmu2::fmi2Struct, state::fmi2FMUstate)
    fmi2FreeFMUstate(fmu2, state)
end

"""
Returns the size of the byte vector the FMU can be stored in
"""
function fmiSerializedFMUstateSize(c::fmi2Struct, state::fmi2FMUstate)
    fmi2SerializedFMUstateSize(c, state)
end

"""
Serialize the data in the FMU state pointer
"""
function fmiSerializeFMUstate(c::fmi2Struct, state::fmi2FMUstate)
    fmi2SerializeFMUstate(c, state)
end

"""
Deserialize the data in the FMU state pointer
"""
function fmiDeSerializeFMUstate(c::fmi2Struct, serializedState::Array{fmi2Byte})
    fmi2DeSerializeFMUstate(c, serializedState)
end

"""
Returns the values of the directional derivatives.
"""
function fmiGetDirectionalDerivative(str::fmi2Struct,
                                     vUnknown_ref::Array{<:Integer},
                                     vKnown_ref::Array{<:Integer},
                                     dvKnown::Array{<:Real} = Array{Real}([]))
    fmi2GetDirectionalDerivative(str,
                                 Array{fmi2ValueReference}(vUnknown_ref),
                                 Array{fmi2ValueReference}(vKnown_ref),
                                 Array{fmi2Real}(dvKnown))
end

"""
Returns the values of a single directional derivative.
"""
function fmiGetDirectionalDerivative(str::fmi2Struct,
                                     vUnknown_ref::Integer,
                                     vKnown_ref::Integer,
                                     dvKnown::Real = 1.0)
    fmi2GetDirectionalDerivative(str,
                                 fmi2ValueReference(vUnknown_ref),
                                 fmi2ValueReference(vKnown_ref),
                                 fmi2Real(dvKnown))
end

"""
Returns the values of the directional derivatives (in-place).
"""
function fmiGetDirectionalDerivative!(str::fmi2Struct,
                                     vUnknown_ref::Array{<:Integer},
                                     vKnown_ref::Array{<:Integer},
                                     dvUnknown::AbstractArray,
                                     dvKnown::Array{<:Real} = Array{Real}([]))
    fmi2GetDirectionalDerivative!(str,
                                 Array{fmi2ValueReference}(vUnknown_ref),
                                 Array{fmi2ValueReference}(vKnown_ref),
                                 dvUnknown,
                                 Array{fmi2Real}(dvKnown))
end

"""
Does one step in the CoSimulation FMU
"""
function fmiDoStep(fmu::fmi2Struct, currentCommunicationPoint::Real, communicationStepSize::Real, noSetFMUStatePriorToCurrentPoint::Bool = true)
    fmi2DoStep(fmu, currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint)
end

"""
Samples the values of the directional derivatives.
"""
function fmiSampleDirectionalDerivative(str::fmi2Struct,
                                     vUnknown_ref::Array{<:Integer},
                                     vKnown_ref::Array{<:Integer},
                                     steps::Array{<:Real} = ones(Real, length(vKnown_ref)).*1e-5)
    fmi2SampleDirectionalDerivative(str,
                                    Array{fmi2ValueReference}(vUnknown_ref),
                                    Array{fmi2ValueReference}(vKnown_ref),
                                    steps)
end

"""
Samples the values of the directional derivatives (in-place).
"""
function fmiSampleDirectionalDerivative!(str::fmi2Struct,
                                     vUnknown_ref::Array{<:Integer},
                                     vKnown_ref::Array{<:Integer},
                                     dvUnknown::Array{fmi2Real, 2},
                                     steps::Array{<:Real} = ones(Real, length(vKnown_ref)).*1e-5)
    fmi2SampleDirectionalDerivative!(str,
                                     Array{fmi2ValueReference}(vUnknown_ref),
                                     Array{fmi2ValueReference}(vKnown_ref),
                                     dvUnknown,
                                     steps)
end

"""
Does one step in the CoSimulation FMU
"""
function fmiDoStep(c::fmi2Struct, communicationStepSize::Real)
    fmi2DoStep(c, communicationStepSize)
end

"""
Set a time instant
"""
function fmiSetTime(c::fmi2Struct, time::Real)
    fmi2SetTime(c, time)
end

"""
Set a new (continuous) state vector
"""
function fmiSetContinuousStates(c::fmi2Struct, x::Union{Array{Float32}, Array{Float64}})
    fmi2SetContinuousStates(c, x)
end

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

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

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

"""
This function must be called by the environment after every completed step
"""
function fmiCompletedIntegratorStep(fmu2::fmi2Struct,
                                     noSetFMUStatePriorToCurrentPoint::fmi2Boolean)
    fmi2CompletedIntegratorStep(fmu2,noSetFMUStatePriorToCurrentPoint)
end

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

"""
Returns the event indicators of the FMU
"""
function fmiGetEventIndicators(s::fmi2Struct)
    fmi2GetEventIndicators(s)
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

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

unsupportedFMUs = Union{FMU1,FMU3}
function fmiDoStep(fmu::unsupportedFMUs, currentCommunicationPoint::Real, communicationStepSize::Real, noSetFMUStatePriorToCurrentPoint::Bool = true)
    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; visible::Bool = false, loggingOn::Bool = false)
    error(unsupportedFMU::errorType)
end

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

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

function fmiSetupExperiment(fmu::unsupportedFMUs,
    toleranceDefined::Bool,
                tolerance::Real,
                startTime::Real,
                stopTimeDefined::Bool,
                stopTime::Real)
    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, dt::Real, t_start::Real = 0.0, t_stop::Real = 1.0)
    error(unsupportedFMU::errorType)
end

function fmiSimulateCS(fmu::unsupportedFMUs, t_start::Real = 0.0, t_stop::Real = 1.0)
    error(unsupportedFMU::errorType)
end

function fmiSimulateME(fmu::unsupportedFMUs, t_start::Real = 0.0, t_stop::Real = 1.0)
    error(unsupportedFMU::errorType)
end

function (fmu::unsupportedFMUs, t_start::Real = 0.0, t_stop::Real = 1.0)
    error(unsupportedFMU::errorType)
end

function fmiGetReal(fmu::unsupportedFMUs, vr)
    error(unsupportedFMU::errorType)
end

function fmiGetReal!(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiSetReal(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiGetInteger(fmu::unsupportedFMUs, vr)
    error(unsupportedFMU::errorType)
end

function fmiGetInteger!(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiSetInteger(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiGetBoolean(fmu::unsupportedFMUs, vr)
    error(unsupportedFMU::errorType)
end

function fmiGetBoolean!(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiSetBoolean(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiGetString(fmu::unsupportedFMUs, vr)
    error(unsupportedFMU::errorType)
end

function fmiGetString!(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

function fmiSetString(fmu::unsupportedFMUs, vr, values)
    error(unsupportedFMU::errorType)
end

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

function fmiSetFMUstate(fmu::unsupportedFMUs, state::fmi2FMUstate)
    error(unsupportedFMU::errorType)
end

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

function fmiSerializedFMUstateSize(c::unsupportedFMUs, size::Int64)
    error(unsupportedFMU::errorType)
end

function fmiSerializeFMUstate(c::unsupportedFMUs, serializedState::fmi2Byte, size::Int64)
    error(unsupportedFMU::errorType)
end

function fmiDeSerializeFMUstate(c::unsupportedFMUs, serializedState::fmi2Byte, size::Int64)
    error(unsupportedFMU::errorType)
end

function fmiGetDirectionalDerivative(fmu::unsupportedFMUs,
                                     vUnknown_ref::Array{Cint},
                                     vKnown_ref::Array{Cint},
                                     dvKnown::Array{Real} = nothing,
                                     dvUnknown::Array{Real} = nothing)
    error(unsupportedFMU::errorType)
end

function fmiGetDirectionalDerivative(fmu::unsupportedFMUs, vUnknown_ref::Cint, vKnown_ref::Cint, dvKnown::Real = 1.0, dvUnknown::Real = 1.0)
    error(unsupportedFMU::errorType)
end

function fmiDoStep(c::unsupportedFMUs, communicationStepSize::Real)
    error(unsupportedFMU::errorType)
end

function fmiSetTime(fmu::unsupportedFMUs, time::Real)
    error(unsupportedFMU::errorType)
end

function fmiSetContinuousStates(c::unsupportedFMUs, x::Union{Array{Float32}, Array{Float64}})
    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,
                                     noSetFMUStatePriorToCurrentPoint::fmi2Boolean)
    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


text:
const branches = 4
const nmax = 500

const rooted = zeros(BigInt, nmax + 1)
const unrooted = zeros(BigInt, nmax + 1)
rooted[1] = rooted[2] = unrooted[1] = unrooted[2] = 1
const c = zeros(BigInt, branches)

function tree(br, n, l, sum, cnt)
    for b in br+1:branches
        sum += n
        if (sum > nmax) || (l * 2 >= sum && b >= branches)
            return
        elseif b == br + 1
            c[br + 1] = rooted[n + 1] * cnt
        else
            c[br + 1] *= rooted[n + 1] + b - br - 1
            c[br + 1] = div(c[br + 1], b - br)
        end
        if l*2 < sum
            unrooted[sum + 1] += c[br + 1]
        end
        if b < branches
            rooted[sum + 1] += c[br + 1]
        end
        for m in n-1:-1:1
            tree(b, m, l, sum, c[br + 1])
        end
    end
end

bicenter(n) = if iseven(n) unrooted[n + 1] += div(rooted[div(n, 2) + 1] * (rooted[div(n, 2) + 1] + 1), 2) end

function paraffins()
    for n in 1:nmax
        tree(0, n, n, 1, one(BigInt))
        bicenter(n)
        println("$n: $(unrooted[n + 1])")
    end
end

paraffins()

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
# This file was generated, do not modify it. # hide
y = [4, 5, 6, 1]
z = x .+ y # elementwise operation


text:
function converged(obj::ConvergenceChecker, arg0::jint, arg1::Object, arg2::Object)
    return jcall(obj, "converged", jboolean, (jint, Object, Object), arg0, arg1, arg2)
end


text:
arrayshape(::Type{Array{T,N}}, sz...) where {T,N} =
  Partial{Array{T,N}}(convert(Array{Any}, fill(T, sz)))

arrayshape(T::Type, sz...) = arrayshape(Array{T,length(sz)}, sz...)

@abstract Basic getindex(xs::Const{<:Array}, i::Const...) =
  Const(xs.value[map(i -> i.value, i)...])

@abstract Basic length(xs::Const{<:Array}) = Const(length(xs.value))
@abstract Basic length(xs::Partial{<:Array}) = Const(length(xs.value))
@abstract Basic length(xs::Shape{<:Array}) = Const(prod(size(xs)))
@abstract Basic length(xs::Array) = Int

@abstract Basic size(xs::Const) = Const(size(xs.value))
@abstract Basic size(xs::Const{Array{T,N}}) where {T,N} = Const(size(xs.value))
@abstract Basic size(xs::Partial{<:Array}) = Const(size(xs.value))
@abstract Basic size(xs::Shape{Array{T,N}}) where {T,N} = Const(size(xs))
@abstract Basic size(xs::AType{Array{T,N}}) where {T,N} = NTuple{N,Int}
@abstract Basic size(xs::AType{Array{T,N}}, i::Const) where {T,N} = i.value > N ? Const(1) : Const(size(xs)[i.value])

@abstract Basic eltype(xs::AbstractArray{T}) where T = Const(T)

@partial Basic getindex(xs::Partial{<:Array}, i::Const...) =
  xs.value[map(i -> i.value, i)...]

# TODO needs edges if we implement `setindex!`
@abstract Basic getindex(xs::Partial{<:AbstractArray{T}}, i::Const{<:Integer}...) where T =
  xs.value[map(i -> i.value, i)...]

@abstract Basic getindex(xs::AbstractArray{T}, is::Integer...) where T = T

@abstract Basic setindex!(xs::AbstractArray, is...) = xs

@abstract Basic Vector{T}() where T = Partial{Vector{T}}([])

@partial Basic push!(xs::Partial{Vector{T}}, x::T) where T = (push!(xs.value, x); xs)

@partial Basic pop!(xs::Partial{Vector{T}}) where T = pop!(xs.value)

@abstract Basic collect(xs::Const{<:Tuple}) = Const(collect(xs.value))

@pure Basic Colon(), similar, Broadcast.BroadcastStyle, Broadcast.result_style

@abstract Basic map(xs::Const...) = Const(map([x.value for x in xs]...))

@abstract Basic function Broadcast.broadcasted(::Broadcast.AbstractArrayStyle, f, args...)
  A = Core.Compiler.return_type(broadcast, Tuple{widen(f),widen.(args)...})
  if f isa Const && args isa Tuple{Vararg{Const}}
    return Const(broadcast(f.value, map(x -> x.value, args)...))
  elseif args isa Tuple{Vararg{Union{Const,Shape,AType{<:Number}}}} && !(args isa Tuple{Vararg{AType{<:Number}}})
    return Shape{A}(Broadcast.broadcast_shape(size.(args)...))
  else
    return A
  end
end

@abstract Basic function mapreduce(f, op, A; dims = :)
  T = Core.Compiler.return_type(mapreduce, Tuple{widen(f),widen(op),widen(A)})
  (dims == Const(:) || dims == (:)) && return T
  A isa Type && return A
  return Shape{Array{eltype(T),ndims(A)}}(ntuple(i -> i in dims ? 1 : size(A)[i], ndims(A)))
end