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

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

Creates a discoverer.

# Arguments
- `source_arn`: The ARN of the event bus.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description for the discoverer.
- `"tags"`: Tags associated with the resource.
"""
create_discoverer(SourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discoverers", Dict{String, Any}("SourceArn"=>SourceArn); aws_config=aws_config)
create_discoverer(SourceArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discoverers", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("SourceArn"=>SourceArn), params)); aws_config=aws_config)

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

Creates a registry.

# Arguments
- `registry_name`: The name of the registry.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the registry to be created.
- `"tags"`: Tags to associate with the registry.
"""
create_registry(registryName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/registries/name/$(registryName)"; aws_config=aws_config)
create_registry(registryName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/registries/name/$(registryName)", params; aws_config=aws_config)

"""
    create_schema(content, type, registry_name, schema_name)
    create_schema(content, type, registry_name, schema_name, params::Dict{String,<:Any})

Creates a schema definition. Inactive schemas will be deleted after two years.

# Arguments
- `content`: The source of the schema definition.
- `type`: The type of schema.
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the schema.
- `"tags"`: Tags associated with the schema.
"""
create_schema(Content, Type, registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)", Dict{String, Any}("Content"=>Content, "Type"=>Type); aws_config=aws_config)
create_schema(Content, Type, registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Content"=>Content, "Type"=>Type), params)); aws_config=aws_config)

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

Deletes a discoverer.

# Arguments
- `discoverer_id`: The ID of the discoverer.

"""
delete_discoverer(discovererId; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/discoverers/id/$(discovererId)"; aws_config=aws_config)
delete_discoverer(discovererId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/discoverers/id/$(discovererId)", params; aws_config=aws_config)

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

Deletes a Registry.

# Arguments
- `registry_name`: The name of the registry.

"""
delete_registry(registryName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/registries/name/$(registryName)"; aws_config=aws_config)
delete_registry(registryName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/registries/name/$(registryName)", params; aws_config=aws_config)

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

Delete the resource-based policy attached to the specified registry.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"registryName"`: The name of the registry.
"""
delete_resource_policy(; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/policy"; aws_config=aws_config)
delete_resource_policy(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/policy", params; aws_config=aws_config)

"""
    delete_schema(registry_name, schema_name)
    delete_schema(registry_name, schema_name, params::Dict{String,<:Any})

Delete a schema definition.

# Arguments
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

"""
delete_schema(registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)"; aws_config=aws_config)
delete_schema(registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)", params; aws_config=aws_config)

"""
    delete_schema_version(registry_name, schema_name, schema_version)
    delete_schema_version(registry_name, schema_name, schema_version, params::Dict{String,<:Any})

Delete the schema version definition

# Arguments
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.
- `schema_version`: The version number of the schema

"""
delete_schema_version(registryName, schemaName, schemaVersion; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/version/$(schemaVersion)"; aws_config=aws_config)
delete_schema_version(registryName, schemaName, schemaVersion, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/version/$(schemaVersion)", params; aws_config=aws_config)

"""
    describe_code_binding(language, registry_name, schema_name)
    describe_code_binding(language, registry_name, schema_name, params::Dict{String,<:Any})

Describe the code binding URI.

# Arguments
- `language`: The language of the code binding.
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"schemaVersion"`: Specifying this limits the results to only this schema version.
"""
describe_code_binding(language, registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/language/$(language)"; aws_config=aws_config)
describe_code_binding(language, registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/language/$(language)", params; aws_config=aws_config)

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

Describes the discoverer.

# Arguments
- `discoverer_id`: The ID of the discoverer.

"""
describe_discoverer(discovererId; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/discoverers/id/$(discovererId)"; aws_config=aws_config)
describe_discoverer(discovererId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/discoverers/id/$(discovererId)", params; aws_config=aws_config)

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

Describes the registry.

# Arguments
- `registry_name`: The name of the registry.

"""
describe_registry(registryName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)"; aws_config=aws_config)
describe_registry(registryName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)", params; aws_config=aws_config)

"""
    describe_schema(registry_name, schema_name)
    describe_schema(registry_name, schema_name, params::Dict{String,<:Any})

Retrieve the schema definition.

# Arguments
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"schemaVersion"`: Specifying this limits the results to only this schema version.
"""
describe_schema(registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)"; aws_config=aws_config)
describe_schema(registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)", params; aws_config=aws_config)

"""
    export_schema(registry_name, schema_name, type)
    export_schema(registry_name, schema_name, type, params::Dict{String,<:Any})

# Arguments
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.
- `type`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"schemaVersion"`: Specifying this limits the results to only this schema version.
"""
export_schema(registryName, schemaName, type; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/export", Dict{String, Any}("type"=>type); aws_config=aws_config)
export_schema(registryName, schemaName, type, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/export", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("type"=>type), params)); aws_config=aws_config)

"""
    get_code_binding_source(language, registry_name, schema_name)
    get_code_binding_source(language, registry_name, schema_name, params::Dict{String,<:Any})

Get the code binding source URI.

# Arguments
- `language`: The language of the code binding.
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"schemaVersion"`: Specifying this limits the results to only this schema version.
"""
get_code_binding_source(language, registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/language/$(language)/source"; aws_config=aws_config)
get_code_binding_source(language, registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/language/$(language)/source", params; aws_config=aws_config)

"""
    get_discovered_schema(events, type)
    get_discovered_schema(events, type, params::Dict{String,<:Any})

Get the discovered schema that was generated based on sampled events.

# Arguments
- `events`: An array of strings where each string is a JSON event. These are the events
  that were used to generate the schema. The array includes a single type of event and has a
  maximum size of 10 events.
- `type`: The type of event.

"""
get_discovered_schema(Events, Type; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discover", Dict{String, Any}("Events"=>Events, "Type"=>Type); aws_config=aws_config)
get_discovered_schema(Events, Type, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discover", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Events"=>Events, "Type"=>Type), params)); aws_config=aws_config)

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

Retrieves the resource-based policy attached to a given registry.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"registryName"`: The name of the registry.
"""
get_resource_policy(; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/policy"; aws_config=aws_config)
get_resource_policy(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/policy", params; aws_config=aws_config)

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

List the discoverers.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"discovererIdPrefix"`: Specifying this limits the results to only those discoverer IDs
  that start with the specified prefix.
- `"limit"`:
- `"nextToken"`: The token that specifies the next page of results to return. To request
  the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be
  shared with other accounts.
- `"sourceArnPrefix"`: Specifying this limits the results to only those ARNs that start
  with the specified prefix.
"""
list_discoverers(; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/discoverers"; aws_config=aws_config)
list_discoverers(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/discoverers", params; aws_config=aws_config)

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

List the registries.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"limit"`:
- `"nextToken"`: The token that specifies the next page of results to return. To request
  the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be
  shared with other accounts.
- `"registryNamePrefix"`: Specifying this limits the results to only those registry names
  that start with the specified prefix.
- `"scope"`: Can be set to Local or AWS to limit responses to your custom registries, or
  the ones provided by AWS.
"""
list_registries(; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries"; aws_config=aws_config)
list_registries(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries", params; aws_config=aws_config)

"""
    list_schema_versions(registry_name, schema_name)
    list_schema_versions(registry_name, schema_name, params::Dict{String,<:Any})

Provides a list of the schema versions and related information.

# Arguments
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"limit"`:
- `"nextToken"`: The token that specifies the next page of results to return. To request
  the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be
  shared with other accounts.
"""
list_schema_versions(registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/versions"; aws_config=aws_config)
list_schema_versions(registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/versions", params; aws_config=aws_config)

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

List the schemas.

# Arguments
- `registry_name`: The name of the registry.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"limit"`:
- `"nextToken"`: The token that specifies the next page of results to return. To request
  the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be
  shared with other accounts.
- `"schemaNamePrefix"`: Specifying this limits the results to only those schema names that
  start with the specified prefix.
"""
list_schemas(registryName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas"; aws_config=aws_config)
list_schemas(registryName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas", params; aws_config=aws_config)

"""
    list_tags_for_resource(resource-arn)
    list_tags_for_resource(resource-arn, params::Dict{String,<:Any})

Get tags for resource.

# Arguments
- `resource-arn`: The ARN of the resource.

"""
list_tags_for_resource(resource_arn; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/tags/$(resource-arn)"; aws_config=aws_config)
list_tags_for_resource(resource_arn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/tags/$(resource-arn)", params; aws_config=aws_config)

"""
    put_code_binding(language, registry_name, schema_name)
    put_code_binding(language, registry_name, schema_name, params::Dict{String,<:Any})

Put code binding URI

# Arguments
- `language`: The language of the code binding.
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"schemaVersion"`: Specifying this limits the results to only this schema version.
"""
put_code_binding(language, registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/language/$(language)"; aws_config=aws_config)
put_code_binding(language, registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)/language/$(language)", params; aws_config=aws_config)

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

The name of the policy.

# Arguments
- `policy`: The resource-based policy.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"RevisionId"`: The revision ID of the policy.
- `"registryName"`: The name of the registry.
"""
put_resource_policy(Policy; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/policy", Dict{String, Any}("Policy"=>Policy); aws_config=aws_config)
put_resource_policy(Policy, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/policy", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Policy"=>Policy), params)); aws_config=aws_config)

"""
    search_schemas(keywords, registry_name)
    search_schemas(keywords, registry_name, params::Dict{String,<:Any})

Search the schemas

# Arguments
- `keywords`: Specifying this limits the results to only schemas that include the provided
  keywords.
- `registry_name`: The name of the registry.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"limit"`:
- `"nextToken"`: The token that specifies the next page of results to return. To request
  the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be
  shared with other accounts.
"""
search_schemas(keywords, registryName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/search", Dict{String, Any}("keywords"=>keywords); aws_config=aws_config)
search_schemas(keywords, registryName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("GET", "/v1/registries/name/$(registryName)/schemas/search", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("keywords"=>keywords), params)); aws_config=aws_config)

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

Starts the discoverer

# Arguments
- `discoverer_id`: The ID of the discoverer.

"""
start_discoverer(discovererId; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discoverers/id/$(discovererId)/start"; aws_config=aws_config)
start_discoverer(discovererId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discoverers/id/$(discovererId)/start", params; aws_config=aws_config)

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

Stops the discoverer

# Arguments
- `discoverer_id`: The ID of the discoverer.

"""
stop_discoverer(discovererId; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discoverers/id/$(discovererId)/stop"; aws_config=aws_config)
stop_discoverer(discovererId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/v1/discoverers/id/$(discovererId)/stop", params; aws_config=aws_config)

"""
    tag_resource(resource-arn, tags)
    tag_resource(resource-arn, tags, params::Dict{String,<:Any})

Add tags to a resource.

# Arguments
- `resource-arn`: The ARN of the resource.
- `tags`: Tags associated with the resource.

"""
tag_resource(resource_arn, tags; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/tags/$(resource-arn)", Dict{String, Any}("tags"=>tags); aws_config=aws_config)
tag_resource(resource_arn, tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("POST", "/tags/$(resource-arn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tags"=>tags), params)); aws_config=aws_config)

"""
    untag_resource(resource-arn, tag_keys)
    untag_resource(resource-arn, tag_keys, params::Dict{String,<:Any})

Removes tags from a resource.

# Arguments
- `resource-arn`: The ARN of the resource.
- `tag_keys`: Keys of key-value pairs.

"""
untag_resource(resource_arn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/tags/$(resource-arn)", Dict{String, Any}("tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(resource_arn, tagKeys, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("DELETE", "/tags/$(resource-arn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tagKeys"=>tagKeys), params)); aws_config=aws_config)

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

Updates the discoverer

# Arguments
- `discoverer_id`: The ID of the discoverer.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: The description of the discoverer to update.
"""
update_discoverer(discovererId; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/discoverers/id/$(discovererId)"; aws_config=aws_config)
update_discoverer(discovererId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/discoverers/id/$(discovererId)", params; aws_config=aws_config)

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

Updates a registry.

# Arguments
- `registry_name`: The name of the registry.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: The description of the registry to update.
"""
update_registry(registryName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/registries/name/$(registryName)"; aws_config=aws_config)
update_registry(registryName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/registries/name/$(registryName)", params; aws_config=aws_config)

"""
    update_schema(registry_name, schema_name)
    update_schema(registry_name, schema_name, params::Dict{String,<:Any})

Updates the schema definition Inactive schemas will be deleted after two years.

# Arguments
- `registry_name`: The name of the registry.
- `schema_name`: The name of the schema.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientTokenId"`: The ID of the client token.
- `"Content"`: The source of the schema definition.
- `"Description"`: The description of the schema.
- `"Type"`: The schema type for the events schema.
"""
update_schema(registryName, schemaName; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)", Dict{String, Any}("ClientTokenId"=>string(uuid4())); aws_config=aws_config)
update_schema(registryName, schemaName, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = schemas("PUT", "/v1/registries/name/$(registryName)/schemas/name/$(schemaName)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ClientTokenId"=>string(uuid4())), params)); aws_config=aws_config)


text:
mutable struct Linear2
    W::Matrix{Float64}
    b::Vector{Float64}
end

Linear2() = Linear2(rand(1,1), rand(1))

predict(m::Linear2, x) = m.W * x .+ m.b

@testset "xgrad" begin

    loss2(m::Linear2, x, y) = sum((predict(m, x) .- y).^2)

    m = Linear2(randn(10, 20), randn(10)); x = randn(20, 200); y = rand(10, 200)
    inputs = [:m => m, :x => x, :y => y]
    ctx = Dict()
    mem = Dict()
    l, dm, dx, dy = xgrad(loss2; mem=mem, m=m, x=x, y=y)
    mem_len = length(mem)
    @test mem_len > 0
    l, dm, dx, dy = xgrad(loss2; mem=mem, m=m, x=x, y=y)
    @test length(mem) == mem_len
    m = Linear2(randn(10, 20), randn(10)); x = randn(20, 200); y = rand(10, 200)
    l, dm, dx, dy = xgrad(loss2; mem=mem, m=m, x=x, y=y)
    @test length(mem) == mem_len

end

ext: jl


text:
using BayesianSumProductNetworks
using Test, Random
using AxisArrays

priors = [NormalInverseGamma(0.0, 1.0, 1.0, 1.0), Beta()]
likelihoods = [Normal(), Bernoulli()]
sstats = [NormalInvGammaSufficientStats(), BetaSufficientStats()]
prior_sums_alpha = 1.0
prior_prods_alpha = 10.0
prior_leaf_alpha = 1.0
K = 10
K_sum = 2
K_prod = 2

N = 100
D = 2

Random.seed!(1)
x = hcat(randn(N,1), rand(0:1, N,1))

# Leaves with factorization over scope
leaves = templateLeaves(prior_leaf_alpha, priors, likelihoods, sstats, N, D, K)
@test length(leaves) == K

setscope!(leaves, [1,2])
@test scope(leaves) == [1,2]

@test size(logpdf(leaves,x)) == (N,K)
@test vec(sum(logpdf(leaves,x), dims=1)) ≈ ones(K) * -238.453 atol=0.01

# Partitions
partition = templatePartition(prior_leaf_alpha, prior_sums_alpha,
    prior_prods_alpha, priors, likelihoods,
    sstats, N, D, K_sum, K_prod, 1, K, 1, 1)

@test length(partition) == K^K_prod

for (d, child) in enumerate(children(partition))
    setscope!(child, [d])
end

updatescope!(partition)
@test scope(partition) == [1,2]

@test size(logpdf(partition, x)) == (N, K^K_prod)

# Test if logpdf is correct
lp_ = logpdf.(children(partition), Ref(x))
r_ = logpdf(partition, x)[1,:]
for j in 1:length(r_)
    j1, j2 = Base._ind2sub((K,K), j)
    @test r_[j] ≈ lp_[1][1,j1] + lp_[2][1,j2]
end

# Test in-place logpdf
nodes = topoligicalorder(partition)
out = AxisArray(Vector{Matrix}(undef, length(nodes)), map(id, nodes))

for n in nodes
    logpdf!(n, x, out)
end

# Regions
region = templateRegion(prior_leaf_alpha, prior_sums_alpha,
    prior_prods_alpha, priors, likelihoods,
    sstats, N, D, K_sum, K_prod, 2, K, 1, 1)
@test length(region) == K_sum

for child in children(region)
    setscope!(child, [1,2])
end

updatescope!(region)
@test scope(region) == [1,2]

@test size(logpdf(region, x)) == (N, K_sum)

# Test if logpdf is correct
lp_ = logpdf.(children(region), Ref(x))
lp_ = mapreduce(k -> lp_[k][1,:], vcat, 1:K_sum)
lp_ = lp_ .+ region.logweights

@test vec(logpdf(region, x)[1,:]) ≈ vec(lse(lp_, dims=1))

# Test in-place logpdf
nodes = topoligicalorder(region)
out = AxisArray(Vector{Matrix}(undef, length(nodes)), map(id, nodes))

for n in nodes
    logpdf!(n, x, out)
    @test out[id(n)] ≈ logpdf(n, x)
end

# Test util function
@test length(topoligicalorder(region)) == 3

# Test region graph for heterogenuous data
priors = [[Beta(1, 1), Gamma()], [NormalInverseGamma(1.0, 1.0, 1.0, 1.0), Gamma()]]
likelihoods = [Distribution[Bernoulli(), Poisson()], Distribution[Normal(), Exponential()]]
sstats = [[BetaSufficientStats(), GammaSufficienStats()], [NormalInvGammaSufficientStats(), GammaSufficienStats()]]

leaves = templateLeaves(prior_leaf_alpha, priors, likelihoods, sstats, N, D, K);
@test length(leaves) == K
@test size(leaves.obsVecs) == (N, K)
@test all((i) -> i == 0, logpdf(leaves, x))

setscope!(leaves, [1,2])
@test scope(leaves) == [1,2]
@test size(logpdf(leaves,x)) == (N, K)
@test all(isfinite, logpdf(leaves,x))
@test all((i) -> i != 0, logpdf(leaves, x))

partition = templatePartition(prior_leaf_alpha, prior_sums_alpha,
    prior_prods_alpha, priors, likelihoods,
    sstats, N, D, K_sum, K_prod, 1, K, 1, 1)

@test length(partition) == K^K_prod

# Test case where the mixture has only a single component.
priors = [Distribution[Beta(1, 1)], Distribution[NormalInverseGamma(1.0, 1.0, 1.0, 1.0)]]
likelihoods = [Distribution[Bernoulli()], Distribution[Normal()]]
sstats = [AbstractSufficientStats[BetaSufficientStats()], AbstractSufficientStats[NormalInvGammaSufficientStats()]]

leaves = templateLeaves(prior_leaf_alpha, priors, likelihoods, sstats, N, D, K);
setscope!(leaves, [1,2])

@test all((i) -> isfinite(i) && i != 0, logpdf(leaves, x))

max_stars_repo_name: crinders/BayesianSumProductNetworks

max_issues_repo_name: crinders/BayesianSumProductNetworks

max_forks_repo_name: crinders/BayesianSumProductNetworks


text:
struct CatVec{T,A<:AbstractVector{T},B<:AbstractVector{T},L1,L2} <: AbstractVector{T}
    i::A
    f::B
end

CatVec(i::AbstractVector,f::AbstractVector) = CatVec{eltype(i),typeof(i),typeof(f),length(i),length(f)}(i,f)

Base.@pure Base.length(a::CatVec{T,A,B,L1,L2}) where {T,A,B,L1,L2} = L1+L2
Base.@pure Base.size(a::CatVec) = (length(a),)

Base.@propagate_inbounds @inline function Base.getindex(x::CatVec{T,A,B,L1,L2},i::Int) where {T,A,B,L1,L2}
    @boundscheck if i < 1 || i > length(x)
        throw(BoundsError(x,i))
    end
    return i<=L1 ? x.i[i] : x.f[i-L1]
end

Base.iterate(l::CatVec,state=firstindex(l)) = state <= length(l) ? (@inbounds l[state], state + 1) : nothing


text:
using Test
using DataFrames
using CSVFiles
using DataStructures
using Sobol
using Distributions

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

# define the (uncertain) parameters of the problem and their distributions
# (this data was used for the pre-computed py_sobolseq)
data = SobolData(
    params = OrderedDict(:x1 => Uniform(-3.14159265359, 3.14159265359),
        :x2 => Uniform(-3.14159265359, 3.14159265359),
        :x3 => Uniform(-3.14159265359, 3.14159265359)),
    N = 1000
)

N = data.N
D = length(data.params)

s = Sobol.SobolSeq(D)
Sobol_sobolseq = vcat(zeros(1,D), hcat([Sobol.next!(s) for i = 1:N-1]...)') # Sobol package sobol seq function

py_sobolseq = load("data/py_uniform/py_sobolseq.csv", header_exists=false, colnames = ["x1", "x2", "x3"]) |> DataFrame

@testset "Sobol Sequence" begin
    @test convert(Matrix, Sobol_sobolseq)  ≈ convert(Matrix, py_sobolseq)  atol = 1e-9
end


text:
module DER

import MathOptInterface
const MOI = MathOptInterface

"""
    Resource

Abstract Class for Energy Resources.
"""
abstract type Resource end

function add_resource_to_model! end

include("house.jl")

include("fixedLoad.jl")
include("curtailableLoad.jl")
include("deferrableLoad.jl")
include("battery.jl")
include("thermalLoad.jl")
include("shiftableLoad.jl")

end  # module


text:
"""
A type that represents a URI. The main feature provided currently is
that when the file:// scheme is used and the platform is Windows,
equality and hashing is case-insensitive.
"""
struct URI2
    _uri::String
end

import Base.==
function ==(a::URI2, b::URI2)
    @static if Sys.iswindows()
        if startswith(a._uri, "file://")
            return lowercase(escape_uri(a._uri)) == lowercase(escape_uri(b._uri))
        else
            return a._uri == b._uri
        end
    else
        return escape_uri(a._uri) == escape_uri(b._uri)
    end
end

function Base.hash(a::URI2, h::UInt)
    @static if Sys.iswindows()
        if startswith(a._uri, "file://")
            return hash(lowercase(escape_uri(a._uri)), h)
        else
            return hash(a._uri)
        end
    else
        return hash(escape_uri(a._uri))
    end
end


text:
using DelimitedFiles, ProgressMeter, PyPlot, Gurobi

include("./util.jl")
include("./parsers.jl")
include("./Algorithms/TEAVAR.jl")
include("./Algorithms/MaxMin.jl")
include("./Algorithms/SMORE.jl")
include("./Algorithms/FFC.jl")
include("./simulation.jl")

function availabilityPlot(algorithmns,
                          topologies,
                          demand_downscales,
                          num_demands,
                          iterations,
                          cutoff,
                          start,
                          step,
                          finish;
                          k=12,
                          target=0,
                          xliml=.95,
                          xlimr=1.0001,
                          paths="KSP",
                          weibull_scale=.0001,
                          plot=true,
                          dirname="./data/raw/availability/")
    env = Gurobi.Env()
    availability_vals = [[] for i in 1:length(algorithmns)]

    dir = nextRun(dirname)
    for algorithmn in algorithmns
        mkdir("$(dir)/$(algorithmn)")
    end

    scenarios_all = []
    scenario_probs_all = []
    for t in 1:length(topologies)
        topology = topologies[t]
        links, capacity, link_probs, nodes = readTopology(topology)
        scenarios_all_top = []
        scenario_probs_top = []
        for i in 1:iterations
            link_probs = weibullProbs(length(links), shape=.8, scale=weibull_scale)
            scenarios, probs = subScenarios(link_probs, cutoff, first=true, last=false)
            push!(scenarios_all_top, scenarios)
            push!(scenario_probs_top, probs)
            println(probs)
        end
        push!(scenarios_all, scenarios_all_top)
        push!(scenario_probs_all, scenario_probs_top)
    end

    scales = collect(start:step:finish)
    progress = ProgressMeter.Progress(length(scales)*length(topologies)*num_demands*iterations*length(algorithmns), .1, "Computing Availability...", 50)
    confidence = zeros(length(scales), 3 * length(algorithmns) + 1)
    confidence[:,1] = scales
    for s in 1:length(scales)
        availabilities = [[] for i in 1:length(algorithmns)]
        for t in 1:length(topologies)
            links, capacity, link_probs, nodes = readTopology(topologies[t])
            for d in 1:num_demands
                demand, flows = readDemand("$(topologies[t])/demand", length(nodes), d, scale=scales[s], downscale=demand_downscales[t])
                if paths != "KSP"
                    T, Tf, k = parsePaths("$(topologies[t])/paths/$(paths)", links, flows)
                else
                    T, Tf, g = getTunnels(nodes, links, capacity, flows, k)
                end
                for i in 1:iterations
                    for alg in 1:length(algorithmns)
                        if algorithmns[alg] == "TEAVAR"
                            var, cvar, a = TEAVAR(env, links, capacity, flows, demand, scenario_probs_all[t][i][1] - .01, k, T, Tf, scenarios_all[t][i], scenario_probs_all[t][i], average=true)
                        elseif algorithmns[alg] == "ECMP"
                            a = ones(size(Tf,1),k)
                        elseif algorithmns[alg] == "MaxMin"
                            a = MaxMin(env, links, capacity, flows, demand, k, T, Tf)
                        elseif algorithmns[alg] == "FFC-1"
                            a, _ = FFC(env, links, capacity, flows, demand, 1, T, Tf)
                        elseif algorithmns[alg] == "FFC-2"
                            a, _ = FFC(env, links, capacity, flows, demand, 2, T, Tf)
                        elseif algorithmns[alg] == "SMORE"
                            a = SMORE(env, links, capacity, flows, demand, T, Tf)
                        else
                            T, Tf, k = parsePaths("$(topologies[t])/paths/$(algorithmns[alg])", links, flows)
                            a = parseYatesSplittingRatios("$(topologies[t])/paths/$(algorithmns[alg])", k, flows)
                        end
                        losses = calculateLossReallocation(links, capacity, demand, flows, T, Tf, k, a, scenarios_all[t][i], scenario_probs_all[t][i])
                        println(losses)
                        open("$(dir)/$(algorithmns[alg])/$(algorithmns[alg])_losses.txt", "a") do io
                            writedlm(io, transpose(hcat(scenario_probs_all[t][i], losses)))
                        end
                        availability = PDF(losses, scenario_probs_all[t][i], target)
                        println(availability)
                        push!(availabilities[alg], availability)
                        ProgressMeter.next!(progress, showvalues = [(:topology,topologies[t]), (:scale,scales[s]), (:demand,"$(d)/$(num_demands)"), (:iteration, "$(i)/$(iterations)"), (:algorithmn,algorithmns[alg]), (:availability, availability)])
                    end
                end
            end
        end

        for alg in 1:length(algorithmns)
            confidence[s, (alg-1)*3 + 2] = sum(availabilities[alg]) / (num_demands * iterations * length(topologies))
            confidence[s, (alg-1)*3 + 3] = minimum(availabilities[alg])
            confidence[s, (alg-1)*3 + 4] = maximum(availabilities[alg])
            open("$(dir)/$(algorithmns[alg])/$(algorithmns[alg])_availabilities.txt", "a") do io writedlm(io, transpose(availabilities[alg])) end
            push!(availability_vals[alg], sum(availabilities[alg]) / (num_demands * iterations * length(topologies)))
        end
    end

    # write to output directory
    writedlm("$(dir)/availabilities", availability_vals)
    writedlm("$(dir)/scales", scales)
    if paths == nothing
        paths = ""
    end
    writedlm("$(dir)/params", [["algorithmns", "topologies", "demand_downscales", "num_demands", "iterations", "cutoff", "scales", "k", "target", "paths", "weibull_scale"], [algorithmns, topologies, demand_downscales, num_demands, iterations, cutoff, scales, k, target, paths, weibull_scale]])
    writedlm("$(dir)/confidence", confidence)

    if plot
        PyPlot.clf()
        for i in 1:size(availability_vals, 1)
            PyPlot.plot(availability_vals[i], scales)
        end
        PyPlot.xlabel("Availability", fontweight="bold")
        PyPlot.ylabel("Demand Scale", fontweight="bold")
        PyPlot.xlim(left=xliml, right=xlimr)
        PyPlot.legend(algorithmns, loc="upper right")
        PyPlot.savefig("$(dir)/plot.png")
        PyPlot.show()
    end
end


text:
using LinearAlgebra

export vadd,
    vaddg,
    valid!,
    vcrss,
    vdist,
    vdistg,
    vdot,
    vdotg,
    vequ,
    vequg,
    vhat,
    vhatg,
    vlcom3,
    vlcom,
    vlcomg,
    vminug,
    vminus,
    vnorm,
    vnormg,
    vpack,
    vperp,
    vprjp,
    vprjpi,
    vproj,
    vrel,
    vrelg,
    vrotv,
    vscl,
    vsclg,
    vsep,
    vsepg,
    vsub,
    vsubg,
    vtmv,
    vtmvg,
    vupack

function _vadd(v1, v2)
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vadd_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          v1, v2, vout)
    vout
end

"""
    vadd(v1, v2)

!!! warning "Deprecated"
    Use `v1 .+ v2` instead.
"""
vadd

@deprecate vadd(v1, v2) v1 .+ v2

function _vaddg(v1, v2)
    ndim = length(v1)
    vout = Array{SpiceDouble}(undef, ndim)
    ccall((:vaddg_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}),
          v1, v2, ndim, vout)
    vout
end

"""
    vaddg(v1, v2)

!!! warning "Deprecated"
    Use `v1 .+ v2` instead.
"""
vaddg

@deprecate vaddg(v1, v2) v1 .+ v2

"""
    valid!(set::SpiceCell{T}) where T

Create a valid SPICE set from a SPICE Cell of any data type.

### Arguments ###

- `set`: Set to be validated

### Output ###

Returns the validated set with ordered elements and duplicates removed.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/valid_c.html)
"""
function valid!(set::SpiceCell{T}) where T
    ccall((:valid_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, Ref{Cell{T}}),
          set.cell.size, set.cell.card, set.cell)
    handleerror()
    set
end

function _vcrss(v1, v2)
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vcrss_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}), v1, v2, vout)
    vout
end

"""
    vcrss(v1, v2)

!!! warning "Deprecated"
    Use `LinearAlgebra.cross(v1, v2)` instead.
"""
vcrss

@deprecate vcrss(v1, v2) LinearAlgebra.cross(v1, v2)

function _vdist(v1, v2)
    ccall((:vdist_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), v1, v2)
end

"""
    vdist(v1, v2)

!!! warning "Deprecated"
    Use `LinearAlgebra.norm(v1 .- v2)` instead.
"""
vdist

@deprecate vdist(v1, v2) LinearAlgebra.norm(v1 .- v2)

function _vdistg(v1, v2)
    ndim = length(v1)
    ccall((:vdistg_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt), v1, v2, ndim)
end

"""
    vdistg(v1, v2)

!!! warning "Deprecated"
    Use `LinearAlgebra.norm(v1 .- v2)` instead.
"""
vdistg

@deprecate vdistg(v1, v2) LinearAlgebra.norm(v1 .- v2)

function _vdot(v1, v2)
    ccall((:vdot_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), v1, v2)
end

"""
    vdot(v1, v2)

!!! warning "Deprecated"
    Use `LinearAlgebra.dot(v1, v2)` instead.
"""
vdot

@deprecate vdot(v1, v2) LinearAlgebra.dot(v1, v2)

function _vdotg(v1, v2)
    ndim = length(v1)
    ccall((:vdotg_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt), v1, v2, ndim)
end

"""
    vdotg(v1, v2)

!!! warning "Deprecated"
    Use `LinearAlgebra.dot(v1, v2)` instead.
"""
vdotg

@deprecate vdotg(v1, v2) LinearAlgebra.dot(v1, v2)

function _vequ(v1, v2)
    ccall((:vequ_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), v1, v2)
end

"""
    vequ(v1, v2)

!!! warning "Deprecated"
    Use `v1 .= v2` instead.
"""
vequ

@deprecate vequ(v1, v2) v1 .= v2

function _vequg(v1, v2)
    ndim = length(v1)
    ccall((:vequg_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}), v1, ndim, v2)
end

"""
    vequg(v1, v2)

!!! warning "Deprecated"
    Use `v1 .= v2` instead.
"""
vequg

@deprecate vequg(v1, v2) v1 .= v2

function _vhat(v1)
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vhat_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), v1, vout)
    vout
end

"""
    vhat(v1)

!!! warning "Deprecated"
    Use `LinearAlgebra.normalize(v1)` instead.
"""
vhat

@deprecate vhat(v1) LinearAlgebra.normalize(v1)

function _vhatg(v1)
    ndim = length(v1)
    vout = Array{SpiceDouble}(undef, ndim)
    ccall((:vhatg_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}), v1, ndim, vout)
    vout
end

"""
    vhatg(v1)

!!! warning "Deprecated"
    Use `LinearAlgebra.normalize(v1)` instead.
"""
vhatg

@deprecate vhatg(v1) LinearAlgebra.normalize(v1)

function _vlcom3(a, v1, b, v2, c, v3)
    sum = Array{SpiceDouble}(undef, 3)
    ccall((:vlcom3_c, libcspice), Cvoid,
          (SpiceDouble, Ref{SpiceDouble}, SpiceDouble, Ref{SpiceDouble},
           SpiceDouble, Ref{SpiceDouble}, Ref{SpiceDouble}),
          a, v1, b, v2, c, v3, sum)
    sum
end

"""
    vlcom3(a, v1, b, v2, c, v3)

!!! warning "Deprecated"
    Use `a .* v1 .+ b .* v2 .+ c .* v3` instead.
"""
vlcom3

@deprecate vlcom3(a, v1, b, v2, c, v3) a .* v1 .+ b .* v2 .+ c .* v3

function _vlcom(a, v1, b, v2)
    sum = Array{SpiceDouble}(undef, 3)
    ccall((:vlcom_c, libcspice), Cvoid,
          (SpiceDouble, Ref{SpiceDouble}, SpiceDouble, Ref{SpiceDouble}, Ref{SpiceDouble}),
          a, v1, b, v2, sum)
    sum
end

"""
    vlcom(a, v1, b, v2)

!!! warning "Deprecated"
    Use `a .* v1 .+ b .* v2` instead.
"""
vlcom

@deprecate vlcom(a, v1, b, v2) a .* v1 .+ b .* v2

function _vlcomg(a, v1, b, v2)
    n = length(v1)
    sum = Array{SpiceDouble}(undef, n)
    ccall((:vlcomg_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Ref{SpiceDouble}, SpiceDouble, Ref{SpiceDouble}, Ref{SpiceDouble}),
          n, a, v1, b, v2, sum)
    sum
end

"""
    vlcomg(a, v1, b, v2)

!!! warning "Deprecated"
    Use `a .* v1 .+ b .* v2` instead.
"""
vlcomg

@deprecate vlcomg(a, v1, b, v2) a .* v1 .+ b .* v2

function _vminug(vin)
    n = length(vin)
    vout = Array{SpiceDouble}(undef, n)
    ccall((:vminug_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}),
          vin, n, vout)
    vout
end

"""
    vminug(vin)

!!! warning "Deprecated"
    Use `-vin` instead.
"""
vminug

@deprecate vminug(vin) -vin

function _vminus(vin)
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vminus_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), vin, vout)
    vout
end

"""
    vminus(vin)

!!! warning "Deprecated"
    Use `-vin` instead.
"""
vminus

@deprecate vminus(vin) -vin

function _vnorm(v1)
    ccall((:vnorm_c, libcspice), SpiceDouble, (Ref{SpiceDouble},), v1)
end

"""
    vnorm(v1)

!!! warning "Deprecated"
    Use `LinearAlgebra.norm(v1)` instead.
"""
vnorm

@deprecate vnorm(v1) LinearAlgebra.norm(v1)

function _vnormg(v1)
    ndim = length(v1)
    ccall((:vnormg_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, SpiceInt), v1, ndim)
end

"""
    vnormg(v1, v2)

!!! warning "Deprecated"
    Use `LinearAlgebra.norm(v1)` instead.
"""
vnormg

@deprecate vnormg(v1) LinearAlgebra.norm(v1)

function _vpack(x, y, z)
    v = Array{SpiceDouble}(undef, 3)
    ccall((:vpack_c, libcspice), Cvoid,
          (SpiceDouble, SpiceDouble, SpiceDouble, Ref{SpiceDouble}),
          x, y, z, v)
    v
end

"""
    vpack(x, y, z)

!!! warning "Deprecated"
    Use `[x, y, z]` instead.
"""
vpack

@deprecate vpack(x, y, z) [x, y, z]

"""
    vperp(a, b)

Find the component of a vector that is perpendicular to a second vector.

### Arguments ###

- `a`: The vector whose orthogonal component is sought
- `b`: The vector used as the orthogonal reference

### Output ###

Returns the component `a` orthogonal to `b`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vperp_c.html)
"""
function vperp(a, b)
    @checkdims 3 a b
    p = Array{SpiceDouble}(undef, 3)
    ccall((:vperp_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          a, b, p)
    p
end

"""
    vprjp(vin, plane)

Project a vector onto a specified plane, orthogonally.

### Arguments ###

- `vin`: Vector to be projected
- `plane`: Plane onto which vin is projected

### Output ###

Returns the vector resulting from the projection.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vprjp_c.html)
"""
function vprjp(vin, plane)
    @checkdims 3 vin
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vprjp_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{Plane}, Ref{SpiceDouble}),
          vin, plane, vout)
    handleerror()
    vout
end

"""
    vprjpi(vin, projpl, invpl)

Find the vector in a specified plane that maps to a specified vector in another plane
under orthogonal projection.

### Arguments ###

- `vin`: The projected vector
- `projpl`: Plane containing `vin`
- `invpl`: Plane containing inverse image of `vin`

### Output ###

Returns the inverse projection of `vin` or `nothing` if `vin` could not be calculated.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vprjpi_c.html)
"""
function vprjpi(vin, projpl, invpl)
    @checkdims 3 vin
    vout = Array{SpiceDouble}(undef, 3)
    found = Ref{SpiceBoolean}()
    ccall((:vprjpi_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{Plane}, Ref{Plane}, Ref{SpiceDouble}, Ref{SpiceBoolean}),
          vin, projpl, invpl, vout, found)
    handleerror()
    Bool(found[]) ? vout : nothing
end

"""
    vproj(a, b)

Finds the projection of one vector onto another vector. All vectors are 3-dimensional.

### Arguments ###

- `a`: The vector to be projected
- `b`: The vector onto which `a` is to be projected

### Output ###

Returns the projection of `a` onto `b`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vproj_c.html)
"""
function vproj(a, b)
    @checkdims 3 a b
    p = Array{SpiceDouble}(undef, 3)
    ccall((:vproj_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}), a, b, p)
    p
end

"""
    vrel(v1, v2)

Return the relative difference between two 3-dimensional vectors.

### Arguments ###

- `v1`, `v2`: Two three-dimensional input vectors

### Output ###

Returns the relative differences between `v1` and `v2`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vrel_c.html)
"""
function vrel(v1, v2)
    @checkdims 3 v1 v2
    ccall((:vrel_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), v1, v2)
end

"""
    vrelg(v1, v2)

Return the relative difference between two vectors.

### Arguments ###

- `v1`, `v2`: Input vectors

### Output ###

Returns the relative differences between `v1` and `v2`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vrelg_c.html)
"""
function vrelg(v1, v2)
    ndim = length(v1)
    @checkdims ndim v2
    ccall((:vrelg_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt), v1, v2, ndim)
end

"""
    vrotv(v, axis, theta)

Rotate a vector about a specified axis vector by a specified angle and return
the rotated vector.

### Arguments ###

- `v`: Vector to be rotated
- `axis`: Axis of the rotation
- `theta`: Angle of rotation (radians)

### Output ###

Result of rotating `v` about `axis` by `theta`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vrotv_c.html)
"""
function vrotv(v, axis, theta)
    @checkdims 3 v
    r = Array{SpiceDouble}(undef, 3)
    ccall((:vrotv_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceDouble, Ref{SpiceDouble}),
          v, axis, theta, r)
    r
end

function _vscl(s, v1)
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vscl_c, libcspice), Cvoid,
          (SpiceDouble, Ref{SpiceDouble}, Ref{SpiceDouble}), s, v1, vout)
    vout
end

"""
    vscl(s, v1)

!!! warning "Deprecated"
    Use `s .* v1` instead.
"""
vscl

@deprecate vscl(s, v1) s .* v1

function _vsclg(s, v1)
    ndim = length(v1)
    vout = Array{SpiceDouble}(undef, ndim)
    ccall((:vsclg_c, libcspice), Cvoid,
          (SpiceDouble, Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}),
          s, v1, ndim, vout)
    vout
end

"""
    vsclg(s, v1)

!!! warning "Deprecated"
    Use `s .* v1` instead.
"""
vsclg

@deprecate vsclg(s, v1) s .* v1

"""
    vsep(v1, v2)

Return the sepative difference between two 3-dimensional vectors.

### Arguments ###

- `v1`, `v2`: Two three-dimensional input vectors

### Output ###

Returns the angle between `v1` and `v2` in radians.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vsep_c.html)
"""
function vsep(v1, v2)
    @checkdims 3 v1 v2
    ccall((:vsep_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}), v1, v2)
end

"""
    vsepg(v1, v2)

Return the sepative difference between two vectors.

### Arguments ###

- `v1`, `v2`: Input vectors

### Output ###

Returns the angle between `v1` and `v2` in radians.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/vsepg_c.html)
"""
function vsepg(v1, v2)
    ndim = length(v1)
    @checkdims ndim v2
    ccall((:vsepg_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt), v1, v2, ndim)
end

function _vsub(v1, v2)
    vout = Array{SpiceDouble}(undef, 3)
    ccall((:vsub_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          v1, v2, vout)
    vout
end

"""
    vsub(v1, v2)

!!! warning "Deprecated"
    Use `v1 .- v2` instead.
"""
vsub

@deprecate vsub(v1, v2) v1 .- v2

function _vsubg(v1, v2)
    ndim = length(v1)
    vout = Array{SpiceDouble}(undef, ndim)
    ccall((:vsubg_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}),
          v1, v2, ndim, vout)
    vout
end

"""
    vsubg(v1, v2)

!!! warning "Deprecated"
    Use `v1 .- v2` instead.
"""
vsubg

@deprecate vsubg(v1, v2) v1 .- v2

function _vtmv(v1, matrix, v2)
    ccall((:vtmv_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          v1, permutedims(matrix), v2)
end

"""
    vtmv(v1, matrix, v2)

!!! warning "Deprecated"
    Use `v1' * matrix * v2` instead.
"""
vtmv

@deprecate vtmv(v1, matrix, v2) v1' * matrix * v2

function _vtmvg(v1, matrix, v2)
    m, n = size(matrix)
    ccall((:vtmvg_c, libcspice), SpiceDouble,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceInt, SpiceInt),
          v1, permutedims(matrix), v2, n, m)
end

"""
    vtmvg(v1, matrix, v2)

!!! warning "Deprecated"
    Use `v1' * matrix * v2` instead.
"""
vtmvg

@deprecate vtmvg(v1, matrix, v2) v1' * matrix * v2

function _vupack(v)
    x = Ref{SpiceDouble}()
    y = Ref{SpiceDouble}()
    z = Ref{SpiceDouble}()
    ccall((:vupack_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          v, x, y, z)
    x[], y[], z[]
end

"""
    vupack(v)

!!! warning "Deprecated"
    Use `x, y, z = v` instead.
"""
vupack

@deprecate vupack(v) (x, y, z) = v

function _vzero(v1)
    Bool(ccall((:vzero_c, libcspice), SpiceBoolean, (Ref{SpiceDouble},), v1))
end

"""
    vzero(v1)

!!! warning "Deprecated"
    Use `iszero(v1)` instead.
"""
vzero

@deprecate vzero(v1) iszero(v1)

function _vzerog(v1)
    ndim = length(v1)
    Bool(ccall((:vzerog_c, libcspice), SpiceBoolean,
               (Ref{SpiceDouble}, SpiceInt), v1, ndim))
end

"""
    vzerog(v1, v2)

!!! warning "Deprecated"
    Use `iszero(v1)` instead.
"""
vzerog

@deprecate vzerog(v1) iszero(v1)


text:
"""
    approximate(R::Rectification; apply_convex_hull::Bool=false)

Approximate a rectification of a polytopic set with a convex polytope.

### Input

- `R`                 -- rectification
- `apply_convex_hull` -- (optional; default: `false`) option to remove redundant
                         vertices

### Output

A polytope in vertex representation.
There is no guarantee that the result over- or underapproximates `R`.

### Algorithm

Let ``X`` be the set that is rectified.
We compute the vertices of ``X``, rectify them, and return the convex hull of
the result.

### Notes

Let ``X`` be the set that is rectified and let ``p`` and ``q`` be two vertices
on a facet of ``X``.
Intuitively, an approximation may occur if the line segment connecting these
vertices crosses a coordinate hyperplane and if the line segment connecting the
rectified vertices has a different angle.

As a corollary, the approximation is exact for the special cases that the
original set is contained in either the positive or negative orthant or
is axis-aligned.
"""
function approximate(R::Rectification; apply_convex_hull::Bool=false)
    vlist = [rectify(v) for v in vertices_list(set(R))]
    if apply_convex_hull
        vlist = convex_hull(vlist)
    end
    T = dim(R) == 2 ? VPolygon : VPolytope
    return T(vlist)
end


text:
module Frameworks

using Printf: @printf, @sprintf
using TimerOutputs: TimerOutputs, TimerOutput, @timeit
using ..QuantumOperators: Transformation, idtype
using ..Spatials: Bonds, AbstractLattice, acrossbonds, isintracell
using ..DegreesOfFreedom: Hilbert, Operators, Table, Boundary, plain, Term, ismodulatable, otype
using ...Prerequisites: atol, rtol, decimaltostr
using ...Prerequisites.Traits: efficientoperations
using ...Prerequisites.CompositeStructures: NamedContainer

import ...Interfaces: id, add!, expand, expand!
import ...Essentials: update!, reset!
import ...Prerequisites.Traits: contentnames, getcontent

export Parameters, Entry, Engine, AbstractGenerator, Generator, SimplifiedGenerator, Action, Assignment, Algorithm
export prepare!, register!, run!, rundependences!, runassignment!, rundependence!, saveassignment

"""
    Parameters{Names}(values::Number...) where Names

A NamedTuple that contain the key-value pairs.
"""
const Parameters{Names} = NamedContainer{Number, Names}
@inline Parameters{Names}(values::Number...) where {Names} = NamedContainer{Names}(values)

"""
    match(params₁::Parameters, params₂::Parameters, atol=atol, rtol=rtol) -> Bool

Judge whether the second set of parameters matches the first.
"""
@generated function Base.match(params₁::Parameters, params₂::Parameters, atol=atol, rtol=rtol)
    names = intersect(fieldnames(params₁), fieldnames(params₂))
    length(names)==0 && return true
    name = QuoteNode(names[1])
    expr = :(isapprox(getfield(params₁, $name), getfield(params₂, $name), atol=atol, rtol=rtol))
    for i = 2:length(names)
        name = QuoteNode(names[i])
        expr = Expr(:&&, expr, :(isapprox(getfield(params₁, $name), getfield(params₂, $name), atol=atol, rtol=rtol)))
    end
    return expr
end

"""
    Entry{C, A<:NamedTuple, B<:NamedTuple}

An entry of quantum operators related to (part of) a quantum lattice system.
"""
struct Entry{C, A<:NamedTuple, B<:NamedTuple}
    constops::C
    alterops::A
    boundops::B
end
@inline Base.:(==)(entry₁::Entry, entry₂::Entry) = ==(efficientoperations, entry₁, entry₂)
@inline Base.isequal(entry₁::Entry, entry₂::Entry) = isequal(efficientoperations, entry₁, entry₂)

"""
    eltype(entry::Entry)
    eltype(::Type{<:Entry})

Get the eltype of an entry of quantum operators.
"""
@inline Base.eltype(entry::Entry) = eltype(typeof(entry))
@inline @generated function Base.eltype(::Type{<:Entry{C, A, B}}) where {C, A<:NamedTuple, B<:NamedTuple}
    optp = C
    (fieldcount(A) > 0) && (optp = reduce(promote_type, fieldtypes(A), init=optp))
    (fieldcount(B) > 0) && (optp = reduce(promote_type, fieldtypes(B), init=optp))
    return optp
end

"""
    empty!(entry::Entry) -> Entry

Empty an entry of quantum operators.
"""
function Base.empty!(entry::Entry)
    empty!(entry.constops)
    map(empty!, values(entry.alterops))
    map(empty!, values(entry.boundops))
    return entry
end

"""
    empty(entry::Entry) -> Entry

Get an empty entry of quantum operators.
"""
function Base.empty(entry::Entry)
    constops = empty(entry.constops)
    alterops = NamedContainer{keys(entry.alterops)}(map(empty, values(entry.alterops)))
    boundops = NamedContainer{keys(entry.boundops)}(map(empty, values(entry.boundops)))
    return Entry(constops, alterops, boundops)
end

"""
    merge!(entry::Entry, another::Entry) -> Entry

Merge the entry of quantum operators by another one.
"""
@generated function Base.merge!(entry::Entry, another::Entry)
    exprs = [:(merge!(entry.constops, another.constops))]
    for name in QuoteNode.(fieldnames(fieldtype(entry, :alterops)))
        push!(exprs, :(merge!(getfield(entry.alterops, $name), getfield(another.alterops, $name))))
    end
    for name in QuoteNode.(fieldnames(fieldtype(entry, :boundops)))
        push!(exprs, :(merge!(getfield(entry.boundops, $name), getfield(another.boundops, $name))))
    end
    push!(exprs, :(return entry))
    return Expr(:block, exprs...)
end

"""
    merge(entry::Entry, another::Entry) -> Entry

Get a new entry of quantum operators by merging two entries.
"""
@inline Base.merge(entry::Entry, another::Entry) = merge!(empty(entry), another)

"""
    (transformation::Transformation)(entry::Entry) -> Entry

Get the transformed entry of quantum operators.
"""
function (transformation::Transformation)(entry::Entry)
    constops = transformation(entry.constops)
    alterops = NamedContainer{keys(entry.alterops)}(map(transformation, values(entry.alterops)))
    boundops = NamedContainer{keys(entry.boundops)}(map(transformation, values(entry.boundops)))
    return Entry(constops, alterops, boundops)
end

"""
    Entry(terms::Tuple{Vararg{Term}}, bonds::Bonds, hilbert::Hilbert;
        half::Bool=false,
        table::Union{Nothing, Table}=nothing
        )

Construct an entry of operators based on the input terms, bonds and Hilbert space.
"""
@generated function Entry(terms::Tuple{Vararg{Term}}, bonds::Bonds, hilbert::Hilbert;
        half::Bool=false,
        table::Union{Nothing, Table}=nothing
        )
    constterms, alterterms = [], []
    for term in fieldtypes(terms)
        ismodulatable(term) ? push!(alterterms, term) : push!(constterms, term)
    end
    names = NTuple{fieldcount(terms), Symbol}(id(term) for term in fieldtypes(terms))
    alternames = NTuple{length(alterterms), Symbol}(id(term) for term in alterterms)
    exprs, alterops, boundops = [], [], []
    push!(exprs, quote
        choosedterms = length($constterms)>0 ? $constterms : $alterterms
        constoptp, constidtp = Union{}, Union{}
        for i = 1:length(choosedterms)
            tempoptp = otype(choosedterms[i], hilbert|>typeof, bonds|>eltype)
            constoptp = promote_type(constoptp, tempoptp)
            constidtp = promote_type(constidtp, tempoptp|>idtype)
        end
        constops = Operators{constidtp, constoptp}()
        innerbonds = filter(acrossbonds, bonds, Val(:exclude))
        boundbonds = filter(acrossbonds, bonds, Val(:include))
    end)
    for i = 1:fieldcount(terms)
        push!(boundops, :(expand(one(terms[$i]), boundbonds, hilbert, half=half, table=table)))
        if ismodulatable(fieldtype(terms, i))
            push!(alterops, :(expand(one(terms[$i]), innerbonds, hilbert, half=half, table=table)))
        else
            push!(exprs, :(expand!(constops, terms[$i], innerbonds, hilbert, half=half, table=table)))
        end
    end
    alterops = Expr(:tuple, alterops...)
    boundops = Expr(:tuple, boundops...)
    push!(exprs, quote
        alterops = NamedContainer{$alternames}($alterops)
        boundops = NamedContainer{$names}($boundops)
        return Entry(constops, alterops, boundops)
    end)
    return Expr(:block, exprs...)
end

"""
    expand!(operators::Operators,
        entry::Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}},
        boundary::Boundary;
        kwargs...
        ) -> Operators

Expand an entry of operators with the given boundary twist and term coefficients.
"""
@generated function expand!(operators::Operators,
        entry::Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}},
        boundary::Boundary;
        kwargs...
        )
    exprs = [:(add!(operators, entry.constops))]
    for name in QuoteNode.(fieldnames(fieldtype(entry, :alterops)))
        push!(exprs, :(value = get(kwargs, $name, nothing)))
        push!(exprs, :(for opt in values(getfield(entry.alterops, $name)) add!(operators, opt*value) end))
    end
    for name in QuoteNode.(fieldnames(fieldtype(entry, :boundops)))
        push!(exprs, :(value = get(kwargs, $name, nothing)))
        push!(exprs, :(for opt in values(getfield(entry.boundops, $name)) add!(operators, boundary(opt)*value) end))
    end
    push!(exprs, :(return operators))
    return Expr(:block, exprs...)
end

"""
    reset!(entry::Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}},
        terms::Tuple{Vararg{Term}}, bonds::Bonds, hilbert::Hilbert;
        half::Bool=false,
        table::Union{Nothing, Table}=nothing
        ) -> Entry

Reset an entry of operators by the new terms, bonds and Hilbert space.
"""
@generated function reset!(entry::Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}},
        terms::Tuple{Vararg{Term}}, bonds::Bonds, hilbert::Hilbert;
        half::Bool=false,
        table::Union{Nothing, Table}=nothing
        )
    exprs = []
    push!(exprs, quote
        empty!(entry)
        innerbonds = filter(acrossbonds, bonds, Val(:exclude))
        boundbonds = filter(acrossbonds, bonds, Val(:include))
    end)
    for (i, term) in enumerate(fieldtypes(terms))
        name = QuoteNode(term|>id)
        push!(exprs, :(expand!(getfield(entry.boundops, $name), one(terms[$i]), boundbonds, hilbert, half=half, table=table)))
        if ismodulatable(term)
            push!(exprs, :(expand!(getfield(entry.alterops, $name), one(terms[$i]), innerbonds, hilbert, half=half, table=table)))
        else
            push!(exprs, :(expand!(entry.constops, terms[$i], innerbonds, hilbert, half=half, table=table)))
        end
    end
    push!(exprs, :(return entry))
    return Expr(:block, exprs...)
end

"""
    Engine

Abstract type for all engines.
"""
abstract type Engine end
@inline Base.:(==)(engine₁::Engine, engine₂::Engine) = ==(efficientoperations, engine₁, engine₂)
@inline Base.isequal(engine₁::Engine, engine₂::Engine) = isequal(efficientoperations, engine₁, engine₂)
@inline update!(engine::Engine; kwargs...) = engine
@inline Base.repr(engine::Engine) = String(nameof(typeof(engine)))
@inline Base.show(io::IO, engine::Engine) = @printf io "%s" nameof(typeof(engine))

"""
    AbstractGenerator{E<:Entry, T<:Union{Table, Nothing}, B<:Boundary} <: Engine

Abstract generator for quantum operators.

By protocol, a concrete generator should have the following predefined contents:
* `operators::E`: the entry for the generated quantum operators
* `table::T`: the index-sequence table if it is not nothing
* `boundary::B`: boundary twist for the generated operators
"""
abstract type AbstractGenerator{E<:Entry, T<:Union{Table, Nothing}, B<:Boundary} <: Engine end
@inline contentnames(::Type{<:AbstractGenerator}) = (:operators, :table, :boundary)
@inline Base.eltype(gen::AbstractGenerator) = eltype(typeof(gen))
@inline Base.eltype(::Type{<:AbstractGenerator{E}}) where {E<:Entry} = eltype(E)

"""
    expand!(operators::Operators, gen::AbstractGenerator{E}) where {E<:Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}}}-> Operators

Expand the generator.
"""
@inline function expand!(operators::Operators, gen::AbstractGenerator{E}) where {E<:Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}}}
    expand!(operators, getcontent(gen, :operators), getcontent(gen, :boundary); Parameters(gen)...)
end

"""
    expand(gen::AbstractGenerator{E}) where {E<:Entry{<:Operators, <:NamedContainer{Operators}, <:NamedContainer{Operators}}} -> Operators

Expand the generator.
"""
@inline expand(gen::AbstractGenerator) = expand!(eltype(gen)(), gen)

"""
    Generator{E<:Entry, TS<:NamedContainer{Term}, BS<:Bonds, H<:Hilbert, T<:Union{Table, Nothing}, B<:Boundary} <: AbstractGenerator{E, T, B}

A generator of operators based on terms, bonds, Hilbert space, and boundary twist.
"""
struct Generator{E<:Entry, TS<:NamedContainer{Term}, BS<:Bonds, H<:Hilbert, T<:Union{Table, Nothing}, B<:Boundary} <: AbstractGenerator{E, T, B}
    operators::E
    terms::TS
    bonds::BS
    hilbert::H
    half::Bool
    table::T
    boundary::B
end
@inline contentnames(::Type{<:Generator}) = (:operators, :terms, :bonds, :hilbert, :half, :table, :boundary)

"""
    Generator(terms::Tuple{Vararg{Term}}, bonds::Bonds, hilbert::Hilbert;
        half::Bool=false, table::Union{Table,Nothing}=nothing, boundary::Boundary=plain
        )

Construct a generator of operators.
"""
@inline function Generator(terms::Tuple{Vararg{Term}}, bonds::Bonds, hilbert::Hilbert;
        half::Bool=false, table::Union{Table,Nothing}=nothing, boundary::Boundary=plain
        )
    return Generator(Entry(terms, bonds, hilbert; half=half, table=table), namedterms(terms), bonds, hilbert, half, table, boundary)
end
@generated function namedterms(terms::Tuple{Vararg{Term}})
    names = NTuple{fieldcount(terms), Symbol}(id(fieldtype(terms, i)) for i = 1:fieldcount(terms))
    return :(NamedContainer{$names}(terms))
end

"""
    Parameters(gen::Generator) -> Parameters

Get the parameters of the terms of a generator.
"""
@generated function Parameters(gen::Generator{<:Entry, TS}) where {TS<:NamedContainer{Term}}
    names, values = fieldnames(TS), [:(gen.terms[$i].value) for i = 1:fieldcount(TS)]
    return :(Parameters{$names}($(values...)))
end

"""
    expand(gen::Generator, name::Symbol) -> Operators
    expand(gen::Generator, i::Int) -> Operators
    expand(gen::Generator, name::Symbol, i::Int) -> Operators

Expand the operators of a generator:
1) the operators of a specific term;
2) the operators on a specific bond;
3) the operators of a specific term on a specific bond.
"""
function expand(gen::Generator, name::Symbol)
    term = getfield(gen.terms, name)
    optp = otype(term|>typeof, gen.hilbert|>typeof, gen.bonds|>eltype)
    result = Operators{idtype(optp), optp}()
    if ismodulatable(term)
        for opt in getfield(gen.operators.alterops, name)|>values add!(result, opt*term.value) end
    else
        expand!(result, term, filter(acrossbonds, gen.bonds, Val(:exclude)), gen.hilbert, half=gen.half, table=gen.table)
    end
    for opt in getfield(gen.operators.boundops, name)|>values
        add!(result, gen.boundary(opt)*term.value)
    end
    return result
end
@generated function expand(gen::Generator{<:Entry, TS}, i::Int) where {TS<:NamedContainer{Term}}
    exprs = []
    push!(exprs, quote
        bond = gen.bonds[i]
        result = eltype(gen)()
    end)
    for i = 1:fieldcount(TS)
        push!(exprs, :(expand!(result, gen.terms[$i], bond, gen.hilbert, half=gen.half, table=gen.table)))
    end
    push!(exprs, quote
        isintracell(bond) || for opt in values(result)
            result[id(opt)] = gen.boundary(opt)
        end
        return result
    end)
    return Expr(:block, exprs...)
end
function expand(gen::Generator, name::Symbol, i::Int)
    bond = gen.bonds[i]
    result = expand(getfield(gen.terms, name), bond, gen.hilbert, half=gen.half, table=gen.table)
    isintracell(bond) || for opt in values(result)
        result[id(opt)] = gen.boundary(opt)
    end
    return result
end

"""
    update!(gen::Generator; kwargs...) -> typeof(gen)

Update the coefficients of the terms in a generator.
"""
@generated function update!(gen::Generator{<:Entry, TS}; kwargs...) where {TS<:NamedContainer{Term}}
    exprs = [:(update!(gen.boundary; kwargs...))]
    for i = 1:fieldcount(TS)
        ismodulatable(fieldtype(TS, i)) && push!(exprs, :(update!(gen.terms[$i]; kwargs...)))
    end
    push!(exprs, :(return gen))
    return Expr(:block, exprs...)
end

"""
    empty!(gen::Generator) -> Generator

Empty the :bonds, :hilbert, :table and :operators of a generator.
"""
function Base.empty!(gen::Generator)
    empty!(gen.bonds)
    empty!(gen.hilbert)
    isnothing(gen.table) || empty!(gen.table)
    empty!(gen.operators)
    return gen
end

"""
    empty(gen::Generator) -> Generator

Get an empty copy of a generator.
"""
@inline function Base.empty(gen::Generator)
    Generator(
        empty(gen.operators),
        gen.terms,
        empty(gen.bonds),
        empty(gen.hilbert),
        gen.half,
        isnothing(gen.table) ? nothing : empty(gen.table),
        gen.boundary,
        )
end

"""
    reset!(gen::Generator, lattice::AbstractLattice) -> Generator

Reset a generator by a new lattice.
"""
function reset!(gen::Generator, lattice::AbstractLattice)
    reset!(gen.bonds, lattice)
    reset!(gen.hilbert, lattice.pids)
    isnothing(gen.table) || reset!(gen.table, gen.hilbert)
    reset!(gen.operators, Tuple(gen.terms), gen.bonds, gen.hilbert, half=gen.half, table=gen.table)
    return gen
end

"""
    SimplifiedGenerator{P<:Parameters, T<:Union{Table, Nothing}, B<:Boundary, E<:Entry} <: AbstractGenerator{T, B, E}

The simplified generator for the entry of quantum operators.
"""
mutable struct SimplifiedGenerator{P<:Parameters, E<:Entry, T<:Union{Table, Nothing}, B<:Boundary} <: AbstractGenerator{E, T, B}
    parameters::P
    operators::E
    table::T
    boundary::B
end
@inline contentnames(::Type{<:SimplifiedGenerator}) = (:parameters, :operators, :table, :boundary)

"""
    SimplifiedGenerator(parameters::Parameters, operators::Entry; table::Union{Table,Nothing}=nothing, boundary::Boundary=plain)

Construct an simplified generator of operators.
"""
@inline function SimplifiedGenerator(parameters::Parameters, operators::Entry; table::Union{Table,Nothing}=nothing, boundary::Boundary=plain)
    return SimplifiedGenerator(parameters, operators, table, boundary)
end

"""
    Parameters(gen::SimplifiedGenerator) -> Parameters

Get the parameters of the terms of a generator.
"""
@inline Parameters(gen::SimplifiedGenerator) = gen.parameters

"""
    empty!(gen::SimplifiedGenerator) -> SimplifiedGenerator

Empty the operators of an simplified generator.
"""
@inline function Base.empty!(gen::SimplifiedGenerator)
    empty!(gen.operators)
    isnothing(gen.table) || empty!(gen.table)
    return gen
end

"""
    empty(gen::SimplifiedGenerator) -> SimplifiedGenerator

Get an empty copy of an simplified generator.
"""
@inline function Base.empty(gen::SimplifiedGenerator)
    return SimplifiedGenerator(gen.parameters, empty(gen.operators), isnothing(gen.table) ? nothing : empty(gen.table), gen.boundary)
end

"""
    update!(gen::SimplifiedGenerator; kwargs...) -> typeof(gen)

Update the parameters of a generator.
"""
@generated function update!(gen::SimplifiedGenerator; kwargs...)
    names = fieldnames(fieldtype(gen, :parameters))
    values = [:(get(kwargs, $name, getfield(gen.parameters, $name))) for name in QuoteNode.(names)]
    return Expr(:block, :(update!(gen.boundary; kwargs...)), :(gen.parameters = Parameters{$names}($(values...))), :(return gen))
end

"""
    reset!(gen::SimplifiedGenerator, operators::Entry; table::Union{Table, Nothing}=nothing) -> SimplifiedGenerator

Reset a generator by a new lattice.
"""
function reset!(gen::SimplifiedGenerator, operators::Entry; table::Union{Table, Nothing}=nothing)
    isnothing(gen.table) || isnothing(table) || merge!(empty!(gen.table), table)
    merge!(empty!(gen.operators), operators)
    return gen
end

"""
    (transformation::Transformation)(gen::AbstractGenerator;
        table::Union{Table, Nothing}=getcontent(gen, :table),
        boundary::Boundary=getcontent(gen, :boundary)
        ) -> SimplifiedGenerator

Get the result of a transformation on the generator for the entry of quantum operators.
"""
function (transformation::Transformation)(gen::AbstractGenerator;
        table::Union{Table, Nothing}=getcontent(gen, :table),
        boundary::Boundary=getcontent(gen, :boundary)
        )
    return SimplifiedGenerator(Parameters(gen), transformation(getcontent(gen, :operators)), table=table, boundary=boundary)
end

"""
    Action

Abstract type for all actions.
"""
abstract type Action end
@inline Base.:(==)(action₁::Action, action₂::Action) = ==(efficientoperations, action₁, action₂)
@inline Base.isequal(action₁::Action, action₂::Action) = isequal(efficientoperations, action₁, action₂)
@inline prepare!(action::Action, engine::Engine) = nothing
@inline update!(action::Action; kwargs...) = action

"""
    Assignment{A<:Action, P<:Parameters, M<:Function, D<:Tuple{Vararg{Symbol}}, R}

An assignment associated with an action.
"""
mutable struct Assignment{A<:Action, P<:Parameters, M<:Function, D<:Tuple{Vararg{Symbol}}, R}
    id::Symbol
    action::A
    parameters::P
    map::M
    dependences::D
    data::R
    ismatched::Bool
    save::Bool
end
@inline Base.:(==)(assign₁::Assignment, assign₂::Assignment) = ==(efficientoperations, assign₁, assign₂)
@inline Base.isequal(assign₁::Assignment, assign₂::Assignment) = isequal(efficientoperations, assign₁, assign₂)

"""
    valtype(assign::Assignment)
    valtype(::Type{<:Assignment})

The type of the data(result) of an assignment.
"""
@inline Base.valtype(assign::Assignment) = valtype(typeof(assign))
@inline Base.valtype(::Type{<:Assignment{<:Action, <:Parameters, <:Function, <:Tuple{Vararg{Symbol}}, R}}) where R = R

"""
    update!(assign::Assignment; kwargs...) -> Assignment

Update the parameters of an assignment and the status of its associated action.
"""
@generated function update!(assign::Assignment; kwargs...)
    names = fieldnames(fieldtype(assign, :parameters))
    exprs = [:(get(kwargs, $name, getfield(assign.parameters, $name))) for name in QuoteNode.(names)]
    return quote
        assign.parameters = Parameters{$names}($(exprs...))
        update!(assign.action; assign.map(assign.parameters)...)
        return assign
    end
end

"""
    Algorithm{E<:Engine, P<:Parameters, M<:Function}

An algorithm associated with an engine.
"""
mutable struct Algorithm{E<:Engine, P<:Parameters, M<:Function}
    name::String
    engine::E
    din::String
    dout::String
    parameters::P
    map::M
    assignments::Dict{Symbol, Assignment}
    timer::TimerOutput
end
@inline run!(alg::Algorithm, assign::Assignment) = nothing
@inline function Base.:(==)(algorithm₁::Algorithm, algorithm₂::Algorithm)
    return ==(
        (algorithm₁.name, algorithm₁.engine, algorithm₁.din, algorithm₁.dout, algorithm₁.parameters, algorithm₁.map, algorithm₁.assignments),
        (algorithm₂.name, algorithm₂.engine, algorithm₂.din, algorithm₂.dout, algorithm₂.parameters, algorithm₂.map, algorithm₂.assignments),
    )
end
@inline function Base.isequal(algorithm₁::Algorithm, algorithm₂::Algorithm)
    return isequal(
        (algorithm₁.name, algorithm₁.engine, algorithm₁.din, algorithm₁.dout, algorithm₁.parameters, algorithm₁.map, algorithm₁.assignments),
        (algorithm₂.name, algorithm₂.engine, algorithm₂.din, algorithm₂.dout, algorithm₂.parameters, algorithm₂.map, algorithm₂.assignments),
    )
end
function Base.show(io::IO, alg::Algorithm)
    @printf io "%s(%s)" alg.name alg.engine
    for (name, value) in pairs(alg.parameters)
        @printf io "_%s" decimaltostr(value, 10)
    end
end

"""
    Algorithm(name::String, engine::Engine; din::String=".", dout::String=".", parameters::Parameters=Parameters(engine), map::Function=identity)

Construct an algorithm.
"""
@inline function Algorithm(name::String, engine::Engine; din::String=".", dout::String=".", parameters::Parameters=Parameters(engine), map::Function=identity)
    return Algorithm(name, engine, din, dout, parameters, map, Dict{Symbol, Assignment}(), TimerOutput())
end

"""
    update!(alg::Algorithm; kwargs...) -> Algorithm

Update the parameters of an algorithm and its associated engine.
"""
@generated function update!(alg::Algorithm; kwargs...)
    names = fieldnames(fieldtype(alg, :parameters))
    exprs = [:(get(kwargs, $name, getfield(alg.parameters, $name))) for name in QuoteNode.(names)]
    return quote
        alg.parameters = Parameters{$names}($(exprs...))
        update!(alg.engine; alg.map(alg.parameters)...)
        return alg
    end
end

"""
    repr(alg::Algorithm, f::Function=param->true; ndecimal::Int=10) -> String

Get the repr representation of an algorithm.

Optionally, some parameters of the algorithm can be filtered by specifying the `f` function.
Besides, the maximum number of decimals of the parameters can also be specified.
"""
function Base.repr(alg::Algorithm, f::Function=param->true; ndecimal::Int=10)
    result = [@sprintf "%s(%s)" alg.name alg.engine]
    for (name, value) in pairs(alg.parameters)
        f(name) && push!(result, decimaltostr(value, ndecimal))
    end
    return join(result, "_")
end

"""
    summary(alg::Algorithm)

Provide a summary of an algorithm.
"""
function Base.summary(alg::Algorithm)
    @info "Summary of $(alg.name)($(nameof(typeof(alg.engine)))):"
    @info string(alg.timer)
end

"""
    nameof(alg::Algorithm, assign::Assignment) -> String

Get the name of the combination of an algorithm and an assignment.
"""
@inline Base.nameof(alg::Algorithm, assign::Assignment) = @sprintf "%s_%s" repr(alg) assign.id

"""
    register!(alg::Algorithm, id::Symbol, action::Action; info::Bool=true, parameters::Parameters=Parameters{()}(), kwargs...) -> Algorithm

Add an assignment on a algorithm by providing the contents of the assignment, and run this assignment.
"""
@inline function register!(alg::Algorithm, id::Symbol, action::Action; info::Bool=true, parameters::Parameters=Parameters{()}(), kwargs...)
    add!(alg, id, action; parameters=parameters, kwargs...)
    run!(alg, id, info)
end

"""
    add!(alg::Algorithm, id::Symbol, action::Action; parameters::Parameters=Parameters{()}(), kwargs...) -> Algorithm

Add an assignment on a algorithm by providing the contents of the assignment.

The difference between `add!` and `register!` is that the `add!` function does not run the newly added assignment but the `register!` function does.
"""
function add!(alg::Algorithm, id::Symbol, action::Action; parameters::Parameters=Parameters{()}(), kwargs...)
    parameters = merge(alg.parameters, parameters)
    map = get(kwargs, :map, identity)
    dependences = get(kwargs, :dependences, ())
    data = prepare!(action, alg.engine)
    save = get(kwargs, :save, false)
    alg.assignments[id] = Assignment(id, action, parameters, map, dependences, data, false, save)
    return alg
end

"""
    run!(alg::Algorithm, id::Symbol, info::Bool=true) -> Algorithm

Run an assignment with the given id registered on an algorithm.

Optionally, the time of the run process can be informed by setting the `info` argument to be `true`.
"""
function run!(alg::Algorithm, id::Symbol, info::Bool=true)
    assign = alg.assignments[id]
    @timeit alg.timer string(id) runassignment!(alg, assign)
    info && @info "Action $id($(nameof(assign.action|>typeof))): time consumed $(TimerOutputs.time(alg.timer[string(id)]) / 10^9)s."
    assign.save && saveassignment(alg, assign)
    return alg
end
function runassignment!(alg::Algorithm, assign::Assignment)
    ismatched = match(assign.parameters, alg.parameters)
    if !(assign.ismatched && ismatched)
        ismatched || update!(alg; assign.parameters...)
        run!(alg, assign)
        assign.ismatched = true
    end
end
function saveassignment(alg::Algorithm, assign::Assignment)
    open(@sprintf("%s/%s.dat", alg.dout, nameof(alg, assign)), "w") do io
        write(io, assign.data)
    end
end

"""
    rundependences!(alg::Algorithm, assign::Assignment, f::Function=assign->true) -> Algorithm

Run the dependences of an assignment.

Optionally, some dependences can be filtered by specifying the `f` function.
"""
function rundependences!(alg::Algorithm, assign::Assignment, f::Function=assign->true)
    for id in assign.dependences
        dependence = alg.assignments[id]
        f(dependence) && rundependence!(alg, dependence)
    end
    return alg
end
function rundependence!(alg::Algorithm, assign::Assignment)
    ismatched = match(alg.parameters, assign.parameters)
    if !(assign.ismatched && ismatched)
        ismatched || update!(assign; alg.parameters...)
        run!(alg, assign)
        assign.ismatched = true
    end
end

end  # module


text:
using Test
using Blink
using WebIO

"""
Execute function f() with a timeout of `timeout` seconds. Returns the
result of f() or `nothing` in the case of a timeout.
"""
function with_timeout(f::Function, timeout)
    c = Channel{Any}(1)
    @async begin
        put!(c, f())
    end
    @async begin
        sleep(timeout)
        put!(c, nothing)
    end
    take!(c)
end

@testset "WebIO integration" begin
    @testset "mount test" begin
        w = Window(Dict(:show => false))
        mounted = Channel(false)
        setmounted = () -> push!(mounted, true)
        scope = Scope()
        onmount(scope, js"""
            function () {
                $setmounted()
            }
        """)
        body!(w, scope)
        did_mount = with_timeout(() -> take!(mounted), 5)
        @test did_mount
    end

    @testset "button click" begin
        w = Window(Dict(:show => false))
        scope = Scope()
        obs = Observable(scope, "obs", false)
        obschannel = Channel(1)
        on((x) -> push!(obschannel, x), obs)
        scope(dom"button#mybutton"(
            events=Dict(
                "click" => @js function()
                    $obs[] = true
                end
            )
        ))
        body!(w, scope)

        # Sleep to allow WebIO scope to mount in Electron
        sleep(0.25)

        @js w document.querySelector("#mybutton").click()
        did_click = with_timeout(() -> take!(obschannel), 5)
        @test did_click
    end
end


text:
using OptimTests, Optim, CUTEst, NLPModels
using Base.Test

ipoptdata = Float64[]
function intermediate(alg_mod::Int, iter_count::Int,
                      obj_value::Float64, inf_pr::Float64, inf_du::Float64, mu::Float64,
                      d_norm::Float64, regularization_size::Float64, alpha_du::Float64,
                      alpha_pr::Float64, ls_trials::Int)
    push!(ipoptdata, obj_value)
    return true
end

immutable Results
    converged::Bool
    obj_optim::Float64
    obj_ipopt::Float64
    obj_cutest::Float64
    cv_initial::Float64
    cv_optim::Float64
    cv_ipopt::Float64
    iter_optim::Int
    iter_ipopt::Int
    fcalls::Int
    objtrace_optim::Vector{Float64}
    objtrace_ipopt::Vector{Float64}
end

# unconstrained
options = Optim.Options(store_trace=true)
unc_problems = CUTEst.select(max_var=10,contype = :unc, custom_filter=x->x["derivative_order"]>=2)
unc_results = Dict{String,Any}()
for prob in unc_problems
    #if !(endswith(prob, "LS")) && !(prob in ("KOWOSBNE", "SSI"))
    local result
    @show prob
    nlp = CUTEstModel(prob)
    d, constraints = optim_problem(nlp)
    x0 = initial_x(nlp)
    try
        result = solve_problem(d, constraints, x0, GradientDescent(), options)
    catch
        unc_results[prob] = "failed"
        finalize(nlp)
        continue
    end
    if result == Inf
        println("Initial point is not in the interior")
        continue
    end
    unc_results[prob] = (Optim.converged(result), result.minimum, solution_optimum(prob), result.iterations, result.f_calls)
    finalize(nlp)
#    end
end
@test sum(v=="failed" for v in values(unc_results)) < length(unc_results)
#=
# constrained
con_problems = CUTEst.select(max_var=100,max_con=100,custom_filter=x->x["derivative_order"]>=2)
con_results = Dict{String,Any}()
for prob in con_problems
    local result
    @show prob
    nlp = CUTEstModel(prob)
    d, constraints = optim_problem(nlp)
    x0 = initial_x(nlp)
    if !isinterior(constraints, x0)
        println("Initial point is not in the interior")
        finalize(nlp)
        continue
    end
    try
        result = solve_problem(d, constraints, x0, IPNewton(), options)
    catch
        con_results[prob] = "failed"
        finalize(nlp)
        continue
    end
    # Compare against Ipopt
    model = NLPtoMPB(nlp, IpoptSolver())
    setIntermediateCallback(model.inner, intermediate)
    empty!(ipoptdata)
    MathProgBase.optimize!(model)
    # Collect statistics
    niter = min(result.iterations, length(ipoptdata))
    objOptim = [tr.value for tr in result.trace[1:niter]]
    objIpopt = ipoptdata[1:niter]
    con_results[prob] = Results(Optim.converged(result), result.minimum,
                                MathProgBase.getobjval(model), solution_optimum(prob),
                                eqconstraints_violation(nlp, nlp.meta.x0),
                                eqconstraints_violation(nlp, result.minimizer),
                                eqconstraints_violation(nlp, MathProgBase.getsolution(model)),
                                result.iterations, length(ipoptdata), result.f_calls,
                                objOptim, objIpopt)
    finalize(nlp)
end
@test sum(v=="failed" for v in values(con_results)) < length(con_results)
=#


text:
function MCMCDiagnosticTools.gelmandiag(
    chains::Chains{<:Real};
    sections = _default_sections(chains),
    transform = false,
    kwargs...,
)
    # Subset the chain.
    _chains = Chains(chains, _clean_sections(chains, sections))

    # Compute the potential scale reduction factor.
    psi = transform ? link(_chains) : _chains.value.data
    results = MCMCDiagnosticTools.gelmandiag(psi; kwargs...)

    # Create a data frame with the results.
    df = ChainDataFrame(
        "Gelman, Rubin, and Brooks diagnostic",
        merge((parameters = names(_chains),), results),
    )

    return df
end

function MCMCDiagnosticTools.gelmandiag_multivariate(
    chains::Chains{<:Real};
    sections = _default_sections(chains),
    transform = true,
    kwargs...
)
    # Subset the chain.
    _chains = Chains(chains, _clean_sections(chains, sections))

    # Compute the potential scale reduction factor.
    psi = transform ? link(_chains) : _chains.value.data
    results = MCMCDiagnosticTools.gelmandiag_multivariate(psi; kwargs...)

    # Create a data frame with the results.
    df = ChainDataFrame(
        "Gelman, Rubin, and Brooks diagnostic",
        (parameters = names(_chains), psrf = results.psrf, psrfci = results.psrfci),
    )

    return df, results.psrfmultivariate
end


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

mutable struct IoK8sApiAuditregistrationV1alpha1Policy <: SwaggerModel
    level::Any # spec type: Union{ Nothing, String } # spec name: level
    stages::Any # spec type: Union{ Nothing, Vector{String} } # spec name: stages

    function IoK8sApiAuditregistrationV1alpha1Policy(;level=nothing, stages=nothing)
        o = new()
        validate_property(IoK8sApiAuditregistrationV1alpha1Policy, Symbol("level"), level)
        setfield!(o, Symbol("level"), level)
        validate_property(IoK8sApiAuditregistrationV1alpha1Policy, Symbol("stages"), stages)
        setfield!(o, Symbol("stages"), stages)
        o
    end
end # type IoK8sApiAuditregistrationV1alpha1Policy

const _property_map_IoK8sApiAuditregistrationV1alpha1Policy = Dict{Symbol,Symbol}(Symbol("level")=>Symbol("level"), Symbol("stages")=>Symbol("stages"))
const _property_types_IoK8sApiAuditregistrationV1alpha1Policy = Dict{Symbol,String}(Symbol("level")=>"String", Symbol("stages")=>"Vector{String}")
Base.propertynames(::Type{ IoK8sApiAuditregistrationV1alpha1Policy }) = collect(keys(_property_map_IoK8sApiAuditregistrationV1alpha1Policy))
Swagger.property_type(::Type{ IoK8sApiAuditregistrationV1alpha1Policy }, name::Symbol) = Union{Nothing,eval(Meta.parse(_property_types_IoK8sApiAuditregistrationV1alpha1Policy[name]))}
Swagger.field_name(::Type{ IoK8sApiAuditregistrationV1alpha1Policy }, property_name::Symbol) =  _property_map_IoK8sApiAuditregistrationV1alpha1Policy[property_name]

function check_required(o::IoK8sApiAuditregistrationV1alpha1Policy)
    (getproperty(o, Symbol("level")) === nothing) && (return false)
    true
end

function validate_property(::Type{ IoK8sApiAuditregistrationV1alpha1Policy }, name::Symbol, val)
end


text:
using mjWrap
using LinearAlgebra

################################################## Experiment Centric Functions
## accepts model and trajectory data
## returns sum of trajectory rewards for each traj; saves per timestep reward
#function rewardfunctionvec(x::mjWrap.mjSet,
#                           s::mjWrap.TrajSamples)::Vector{Float64}
#    vel_x  = diff(s.state[1,:,:]) / (x.dt*x.skip)
#    angle  = -1e-1 * abs.(s.obs[1,:,:])
#    c_ctrl = -1e-4 * sum( s.ctrl.*s.ctrl, 1 )[1,:,:]
#    s.reward[:,:] = c_ctrl + angle
#    s.reward[2:end,:] += vel_x
#    return sum(s.reward, 1)[1,:]
#end

function rewardfunction(x::mjWrap.mjSet,
                        s0::AbstractVector{Float64}, # pass in states
                        s1::AbstractVector{Float64},
                        o0::AbstractVector{Float64}, # observations
                        o1::AbstractVector{Float64},
                        ctrl::AbstractVector{Float64})::Float64

   pos    = Float64( (s1[1] - s0[1]) / (x.dt*x.skip) ) # forward x direction
   angle  = -1e-1 * abs( o1[1] )
   c_ctrl = -1e-3 * norm(ctrl)^2

   reward = 2*pos + c_ctrl + angle
   return reward
end

function obsfunction(x::mjWrap.mjSet,
                     s::AbstractVector{Float64},
                     o::AbstractVector{Float64})
    o[1] = mjWrap.wraptopi(o[1])
end

## accepts model,
## default state,
## preallocated matrix of states (nqnv x K)
function initfunction!(x::mjWrap.mjSet,
                                   s::mjWrap.TrajSamples)
    s0 = s.s0
    mag = 0.2
    rot = pi #2pi #2*pi
    init_state = view( s.state, :, 1, : )
    for t=1:s.K
        init_state[:,t] = s0 + rand(x.nq+x.nv) * mag - mag/2.0
        #init_state[3,t] = s0[3] + rand() * rot - rot/2.0 # diverse
    end
end

function ctrlfunction!(x::mjWrap.mjSet, ctrl::Array{Float64})
    randn!(ctrl)
end

# modelfunc should takes in time index (iteration index) to know change curric
function modelfunction!(x::mjWrap.mjSet, iter::Int=0)
end

## evaluation function: reward function shapes exploration, evaluation
## function defines success or failure
function evalfunction(x::mjWrap.mjSet, d::mjWrap.TrajSamples)
    # swimmer success: max distance along x axis covered at end of T
    xaxis_index = 1
    return mean(d.state[xaxis_index,end,:]) # x position at time T, for all K
end

using ExpFunctions
myfuncs = FunctionSet(modelfunction!,
                      initfunction!,
                      ctrlfunction!,
                      obsfunction,
                      rewardfunction,
                      evalfunction)


text:
# calculate covariance matrix
# from eigen values and vectors

module CalcCovMat
    using LinearAlgebra

    function main()
        # x-y grid
        vx = 0:200
        vy = 0:100

        # mu, covariance matrix
        mu = [150; 50]
        cov = [100 -25*sqrt(3); -25*sqrt(3) 50]

        # calculate eigen values/vectors
        eig_vals, eig_vecs = eigen(cov)
        V = eig_vecs
        L = [eig_vals[1] 0; 0 eig_vals[2]]

        # calculate covariance matrix
        cov_mat_calc = V * L * inv(V)
        println("Calculated Covariance Matrix: $(cov_mat_calc)")
        println("Original Covariance Matrix: $(cov)")
    end
end

max_stars_repo_name: ShisatoYano/JuliaAutonomy

max_issues_repo_name: ShisatoYano/JuliaAutonomy

max_forks_repo_name: ShisatoYano/JuliaAutonomy