text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: elasticsearch_service
using AWS.Compat
using AWS.UUIDs
"""
    AcceptInboundCrossClusterSearchConnection()

Allows the destination domain owner to accept an inbound cross-cluster search connection request.

# Required Parameters
- `ConnectionId`: The id of the inbound connection that you want to accept.

"""

accept_inbound_cross_cluster_search_connection(ConnectionId; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("PUT", "/2015-01-01/es/ccs/inboundConnection/$(ConnectionId)/accept"; aws_config=aws_config)
accept_inbound_cross_cluster_search_connection(ConnectionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("PUT", "/2015-01-01/es/ccs/inboundConnection/$(ConnectionId)/accept", args; aws_config=aws_config)

"""
    AddTags()

Attaches tags to an existing Elasticsearch domain. Tags are a set of case-sensitive key value pairs. An Elasticsearch domain may have up to 10 tags. See  Tagging Amazon Elasticsearch Service Domains for more information.

# Required Parameters
- `ARN`:  Specify the ARN for which you want to add the tags.
- `TagList`:  List of Tag that need to be added for the Elasticsearch domain.

"""

add_tags(ARN, TagList; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/tags", Dict{String, Any}("ARN"=>ARN, "TagList"=>TagList); aws_config=aws_config)
add_tags(ARN, TagList, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/tags", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ARN"=>ARN, "TagList"=>TagList), args)); aws_config=aws_config)

"""
    AssociatePackage()

Associates a package with an Amazon ES domain.

# Required Parameters
- `DomainName`: Name of the domain that you want to associate the package with.
- `PackageID`: Internal ID of the package that you want to associate with a domain. Use DescribePackages to find this value.

"""

associate_package(DomainName, PackageID; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages/associate/$(PackageID)/$(DomainName)"; aws_config=aws_config)
associate_package(DomainName, PackageID, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages/associate/$(PackageID)/$(DomainName)", args; aws_config=aws_config)

"""
    CancelElasticsearchServiceSoftwareUpdate()

Cancels a scheduled service software update for an Amazon ES domain. You can only perform this operation before the AutomatedUpdateDate and when the UpdateStatus is in the PENDING_UPDATE state.

# Required Parameters
- `DomainName`: The name of the domain that you want to stop the latest service software update on.

"""

cancel_elasticsearch_service_software_update(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/serviceSoftwareUpdate/cancel", Dict{String, Any}("DomainName"=>DomainName); aws_config=aws_config)
cancel_elasticsearch_service_software_update(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/serviceSoftwareUpdate/cancel", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DomainName"=>DomainName), args)); aws_config=aws_config)

"""
    CreateElasticsearchDomain()

Creates a new Elasticsearch domain. For more information, see Creating Elasticsearch Domains in the Amazon Elasticsearch Service Developer Guide.

# Required Parameters
- `DomainName`: The name of the Elasticsearch domain that you are creating. Domain names are unique across the domains owned by an account within an AWS region. Domain names must start with a lowercase letter and can contain the following characters: a-z (lowercase), 0-9, and - (hyphen).

# Optional Parameters
- `AccessPolicies`:  IAM access policy as a JSON-formatted string.
- `AdvancedOptions`:  Option to allow references to indices in an HTTP request body. Must be false when configuring access to individual sub-resources. By default, the value is true. See Configuration Advanced Options for more information.
- `AdvancedSecurityOptions`: Specifies advanced security options.
- `CognitoOptions`: Options to specify the Cognito user and identity pools for Kibana authentication. For more information, see Amazon Cognito Authentication for Kibana.
- `DomainEndpointOptions`: Options to specify configuration that will be applied to the domain endpoint.
- `EBSOptions`: Options to enable, disable and specify the type and size of EBS storage volumes.
- `ElasticsearchClusterConfig`: Configuration options for an Elasticsearch domain. Specifies the instance type and number of instances in the domain cluster.
- `ElasticsearchVersion`: String of format X.Y to specify version for the Elasticsearch domain eg. \"1.5\" or \"2.3\". For more information, see Creating Elasticsearch Domains in the Amazon Elasticsearch Service Developer Guide.
- `EncryptionAtRestOptions`: Specifies the Encryption At Rest Options.
- `LogPublishingOptions`: Map of LogType and LogPublishingOption, each containing options to publish a given type of Elasticsearch log.
- `NodeToNodeEncryptionOptions`: Specifies the NodeToNodeEncryptionOptions.
- `SnapshotOptions`: Option to set time, in UTC format, of the daily automated snapshot. Default value is 0 hours.
- `VPCOptions`: Options to specify the subnets and security groups for VPC endpoint. For more information, see Creating a VPC in VPC Endpoints for Amazon Elasticsearch Service Domains
"""

create_elasticsearch_domain(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/domain", Dict{String, Any}("DomainName"=>DomainName); aws_config=aws_config)
create_elasticsearch_domain(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/domain", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DomainName"=>DomainName), args)); aws_config=aws_config)

"""
    CreateOutboundCrossClusterSearchConnection()

Creates a new cross-cluster search connection from a source domain to a destination domain.

# Required Parameters
- `ConnectionAlias`: Specifies the connection alias that will be used by the customer for this connection.
- `DestinationDomainInfo`: Specifies the DomainInformation for the destination Elasticsearch domain.
- `SourceDomainInfo`: Specifies the DomainInformation for the source Elasticsearch domain.

"""

create_outbound_cross_cluster_search_connection(ConnectionAlias, DestinationDomainInfo, SourceDomainInfo; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/ccs/outboundConnection", Dict{String, Any}("ConnectionAlias"=>ConnectionAlias, "DestinationDomainInfo"=>DestinationDomainInfo, "SourceDomainInfo"=>SourceDomainInfo); aws_config=aws_config)
create_outbound_cross_cluster_search_connection(ConnectionAlias, DestinationDomainInfo, SourceDomainInfo, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/ccs/outboundConnection", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ConnectionAlias"=>ConnectionAlias, "DestinationDomainInfo"=>DestinationDomainInfo, "SourceDomainInfo"=>SourceDomainInfo), args)); aws_config=aws_config)

"""
    CreatePackage()

Create a package for use with Amazon ES domains.

# Required Parameters
- `PackageName`: Unique identifier for the package.
- `PackageSource`: The customer S3 location PackageSource for importing the package.
- `PackageType`: Type of package. Currently supports only TXT-DICTIONARY.

# Optional Parameters
- `PackageDescription`: Description of the package.
"""

create_package(PackageName, PackageSource, PackageType; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages", Dict{String, Any}("PackageName"=>PackageName, "PackageSource"=>PackageSource, "PackageType"=>PackageType); aws_config=aws_config)
create_package(PackageName, PackageSource, PackageType, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("PackageName"=>PackageName, "PackageSource"=>PackageSource, "PackageType"=>PackageType), args)); aws_config=aws_config)

"""
    DeleteElasticsearchDomain()

Permanently deletes the specified Elasticsearch domain and all of its data. Once a domain is deleted, it cannot be recovered.

# Required Parameters
- `DomainName`: The name of the Elasticsearch domain that you want to permanently delete.

"""

delete_elasticsearch_domain(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/domain/$(DomainName)"; aws_config=aws_config)
delete_elasticsearch_domain(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/domain/$(DomainName)", args; aws_config=aws_config)

"""
    DeleteElasticsearchServiceRole()

Deletes the service-linked role that Elasticsearch Service uses to manage and maintain VPC domains. Role deletion will fail if any existing VPC domains use the role. You must delete any such Elasticsearch domains before deleting the role. See Deleting Elasticsearch Service Role in VPC Endpoints for Amazon Elasticsearch Service Domains.

"""

delete_elasticsearch_service_role(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/role"; aws_config=aws_config)
delete_elasticsearch_service_role(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/role", args; aws_config=aws_config)

"""
    DeleteInboundCrossClusterSearchConnection()

Allows the destination domain owner to delete an existing inbound cross-cluster search connection.

# Required Parameters
- `ConnectionId`: The id of the inbound connection that you want to permanently delete.

"""

delete_inbound_cross_cluster_search_connection(ConnectionId; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/ccs/inboundConnection/$(ConnectionId)"; aws_config=aws_config)
delete_inbound_cross_cluster_search_connection(ConnectionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/ccs/inboundConnection/$(ConnectionId)", args; aws_config=aws_config)

"""
    DeleteOutboundCrossClusterSearchConnection()

Allows the source domain owner to delete an existing outbound cross-cluster search connection.

# Required Parameters
- `ConnectionId`: The id of the outbound connection that you want to permanently delete.

"""

delete_outbound_cross_cluster_search_connection(ConnectionId; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/ccs/outboundConnection/$(ConnectionId)"; aws_config=aws_config)
delete_outbound_cross_cluster_search_connection(ConnectionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/es/ccs/outboundConnection/$(ConnectionId)", args; aws_config=aws_config)

"""
    DeletePackage()

Delete the package.

# Required Parameters
- `PackageID`: Internal ID of the package that you want to delete. Use DescribePackages to find this value.

"""

delete_package(PackageID; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/packages/$(PackageID)"; aws_config=aws_config)
delete_package(PackageID, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("DELETE", "/2015-01-01/packages/$(PackageID)", args; aws_config=aws_config)

"""
    DescribeElasticsearchDomain()

Returns domain configuration information about the specified Elasticsearch domain, including the domain ID, domain endpoint, and domain ARN.

# Required Parameters
- `DomainName`: The name of the Elasticsearch domain for which you want information.

"""

describe_elasticsearch_domain(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/domain/$(DomainName)"; aws_config=aws_config)
describe_elasticsearch_domain(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/domain/$(DomainName)", args; aws_config=aws_config)

"""
    DescribeElasticsearchDomainConfig()

Provides cluster configuration information about the specified Elasticsearch domain, such as the state, creation date, update version, and update date for cluster options.

# Required Parameters
- `DomainName`: The Elasticsearch domain that you want to get information about.

"""

describe_elasticsearch_domain_config(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/domain/$(DomainName)/config"; aws_config=aws_config)
describe_elasticsearch_domain_config(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/domain/$(DomainName)/config", args; aws_config=aws_config)

"""
    DescribeElasticsearchDomains()

Returns domain configuration information about the specified Elasticsearch domains, including the domain ID, domain endpoint, and domain ARN.

# Required Parameters
- `DomainNames`: The Elasticsearch domains for which you want information.

"""

describe_elasticsearch_domains(DomainNames; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/domain-info", Dict{String, Any}("DomainNames"=>DomainNames); aws_config=aws_config)
describe_elasticsearch_domains(DomainNames, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/domain-info", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DomainNames"=>DomainNames), args)); aws_config=aws_config)

"""
    DescribeElasticsearchInstanceTypeLimits()

 Describe Elasticsearch Limits for a given InstanceType and ElasticsearchVersion. When modifying existing Domain, specify the  DomainName  to know what Limits are supported for modifying.

# Required Parameters
- `ElasticsearchVersion`:  Version of Elasticsearch for which  Limits  are needed.
- `InstanceType`:  The instance type for an Elasticsearch cluster for which Elasticsearch  Limits  are needed.

# Optional Parameters
- `domainName`:  DomainName represents the name of the Domain that we are trying to modify. This should be present only if we are querying for Elasticsearch  Limits  for existing domain.
"""

describe_elasticsearch_instance_type_limits(ElasticsearchVersion, InstanceType; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/instanceTypeLimits/$(ElasticsearchVersion)/$(InstanceType)"; aws_config=aws_config)
describe_elasticsearch_instance_type_limits(ElasticsearchVersion, InstanceType, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/instanceTypeLimits/$(ElasticsearchVersion)/$(InstanceType)", args; aws_config=aws_config)

"""
    DescribeInboundCrossClusterSearchConnections()

Lists all the inbound cross-cluster search connections for a destination domain.

# Optional Parameters
- `Filters`:  A list of filters used to match properties for inbound cross-cluster search connection. Available Filter names for this operation are:  cross-cluster-search-connection-id source-domain-info.domain-name source-domain-info.owner-id source-domain-info.region destination-domain-info.domain-name
- `MaxResults`: Set this value to limit the number of results returned. If not specified, defaults to 100.
- `NextToken`:  NextToken is sent in case the earlier API call results contain the NextToken. It is used for pagination.
"""

describe_inbound_cross_cluster_search_connections(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/ccs/inboundConnection/search"; aws_config=aws_config)
describe_inbound_cross_cluster_search_connections(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/ccs/inboundConnection/search", args; aws_config=aws_config)

"""
    DescribeOutboundCrossClusterSearchConnections()

Lists all the outbound cross-cluster search connections for a source domain.

# Optional Parameters
- `Filters`:  A list of filters used to match properties for outbound cross-cluster search connection. Available Filter names for this operation are:  cross-cluster-search-connection-id destination-domain-info.domain-name destination-domain-info.owner-id destination-domain-info.region source-domain-info.domain-name
- `MaxResults`: Set this value to limit the number of results returned. If not specified, defaults to 100.
- `NextToken`:  NextToken is sent in case the earlier API call results contain the NextToken. It is used for pagination.
"""

describe_outbound_cross_cluster_search_connections(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/ccs/outboundConnection/search"; aws_config=aws_config)
describe_outbound_cross_cluster_search_connections(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/ccs/outboundConnection/search", args; aws_config=aws_config)

"""
    DescribePackages()

Describes all packages available to Amazon ES. Includes options for filtering, limiting the number of results, and pagination.

# Optional Parameters
- `Filters`: Only returns packages that match the DescribePackagesFilterList values.
- `MaxResults`: Limits results to a maximum number of packages.
- `NextToken`: Used for pagination. Only necessary if a previous API call includes a non-null NextToken value. If provided, returns results for the next page.
"""

describe_packages(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages/describe"; aws_config=aws_config)
describe_packages(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages/describe", args; aws_config=aws_config)

"""
    DescribeReservedElasticsearchInstanceOfferings()

Lists available reserved Elasticsearch instance offerings.

# Optional Parameters
- `maxResults`: Set this value to limit the number of results returned. If not specified, defaults to 100.
- `nextToken`: NextToken should be sent in case if earlier API call produced result containing NextToken. It is used for pagination.
- `offeringId`: The offering identifier filter value. Use this parameter to show only the available offering that matches the specified reservation identifier.
"""

describe_reserved_elasticsearch_instance_offerings(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/reservedInstanceOfferings"; aws_config=aws_config)
describe_reserved_elasticsearch_instance_offerings(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/reservedInstanceOfferings", args; aws_config=aws_config)

"""
    DescribeReservedElasticsearchInstances()

Returns information about reserved Elasticsearch instances for this account.

# Optional Parameters
- `maxResults`: Set this value to limit the number of results returned. If not specified, defaults to 100.
- `nextToken`: NextToken should be sent in case if earlier API call produced result containing NextToken. It is used for pagination.
- `reservationId`: The reserved instance identifier filter value. Use this parameter to show only the reservation that matches the specified reserved Elasticsearch instance ID.
"""

describe_reserved_elasticsearch_instances(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/reservedInstances"; aws_config=aws_config)
describe_reserved_elasticsearch_instances(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/reservedInstances", args; aws_config=aws_config)

"""
    DissociatePackage()

Dissociates a package from the Amazon ES domain.

# Required Parameters
- `DomainName`: Name of the domain that you want to associate the package with.
- `PackageID`: Internal ID of the package that you want to associate with a domain. Use DescribePackages to find this value.

"""

dissociate_package(DomainName, PackageID; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages/dissociate/$(PackageID)/$(DomainName)"; aws_config=aws_config)
dissociate_package(DomainName, PackageID, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/packages/dissociate/$(PackageID)/$(DomainName)", args; aws_config=aws_config)

"""
    GetCompatibleElasticsearchVersions()

 Returns a list of upgrade compatible Elastisearch versions. You can optionally pass a  DomainName  to get all upgrade compatible Elasticsearch versions for that specific domain.

# Optional Parameters
- `domainName`:
"""

get_compatible_elasticsearch_versions(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/compatibleVersions"; aws_config=aws_config)
get_compatible_elasticsearch_versions(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/compatibleVersions", args; aws_config=aws_config)

"""
    GetUpgradeHistory()

Retrieves the complete history of the last 10 upgrades that were performed on the domain.

# Required Parameters
- `DomainName`:

# Optional Parameters
- `maxResults`:
- `nextToken`:
"""

get_upgrade_history(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/upgradeDomain/$(DomainName)/history"; aws_config=aws_config)
get_upgrade_history(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/upgradeDomain/$(DomainName)/history", args; aws_config=aws_config)

"""
    GetUpgradeStatus()

Retrieves the latest status of the last upgrade or upgrade eligibility check that was performed on the domain.

# Required Parameters
- `DomainName`:

"""

get_upgrade_status(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/upgradeDomain/$(DomainName)/status"; aws_config=aws_config)
get_upgrade_status(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/upgradeDomain/$(DomainName)/status", args; aws_config=aws_config)

"""
    ListDomainNames()

Returns the name of all Elasticsearch domains owned by the current user's account.

"""

list_domain_names(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/domain"; aws_config=aws_config)
list_domain_names(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/domain", args; aws_config=aws_config)

"""
    ListDomainsForPackage()

Lists all Amazon ES domains associated with the package.

# Required Parameters
- `PackageID`: The package for which to list domains.

# Optional Parameters
- `maxResults`: Limits results to a maximum number of domains.
- `nextToken`: Used for pagination. Only necessary if a previous API call includes a non-null NextToken value. If provided, returns results for the next page.
"""

list_domains_for_package(PackageID; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/packages/$(PackageID)/domains"; aws_config=aws_config)
list_domains_for_package(PackageID, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/packages/$(PackageID)/domains", args; aws_config=aws_config)

"""
    ListElasticsearchInstanceTypes()

List all Elasticsearch instance types that are supported for given ElasticsearchVersion

# Required Parameters
- `ElasticsearchVersion`: Version of Elasticsearch for which list of supported elasticsearch instance types are needed.

# Optional Parameters
- `domainName`: DomainName represents the name of the Domain that we are trying to modify. This should be present only if we are querying for list of available Elasticsearch instance types when modifying existing domain.
- `maxResults`:  Set this value to limit the number of results returned. Value provided must be greater than 30 else it wont be honored.
- `nextToken`: NextToken should be sent in case if earlier API call produced result containing NextToken. It is used for pagination.
"""

list_elasticsearch_instance_types(ElasticsearchVersion; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/instanceTypes/$(ElasticsearchVersion)"; aws_config=aws_config)
list_elasticsearch_instance_types(ElasticsearchVersion, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/instanceTypes/$(ElasticsearchVersion)", args; aws_config=aws_config)

"""
    ListElasticsearchVersions()

List all supported Elasticsearch versions

# Optional Parameters
- `maxResults`:  Set this value to limit the number of results returned. Value provided must be greater than 10 else it wont be honored.
- `nextToken`:
"""

list_elasticsearch_versions(; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/versions"; aws_config=aws_config)
list_elasticsearch_versions(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/es/versions", args; aws_config=aws_config)

"""
    ListPackagesForDomain()

Lists all packages associated with the Amazon ES domain.

# Required Parameters
- `DomainName`: The name of the domain for which you want to list associated packages.

# Optional Parameters
- `maxResults`: Limits results to a maximum number of packages.
- `nextToken`: Used for pagination. Only necessary if a previous API call includes a non-null NextToken value. If provided, returns results for the next page.
"""

list_packages_for_domain(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/domain/$(DomainName)/packages"; aws_config=aws_config)
list_packages_for_domain(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/domain/$(DomainName)/packages", args; aws_config=aws_config)

"""
    ListTags()

Returns all tags for the given Elasticsearch domain.

# Required Parameters
- `arn`:  Specify the ARN for the Elasticsearch domain to which the tags are attached that you want to view.

"""

list_tags(arn; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/tags/", Dict{String, Any}("arn"=>arn); aws_config=aws_config)
list_tags(arn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("GET", "/2015-01-01/tags/", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("arn"=>arn), args)); aws_config=aws_config)

"""
    PurchaseReservedElasticsearchInstanceOffering()

Allows you to purchase reserved Elasticsearch instances.

# Required Parameters
- `ReservationName`: A customer-specified identifier to track this reservation.
- `ReservedElasticsearchInstanceOfferingId`: The ID of the reserved Elasticsearch instance offering to purchase.

# Optional Parameters
- `InstanceCount`: The number of Elasticsearch instances to reserve.
"""

purchase_reserved_elasticsearch_instance_offering(ReservationName, ReservedElasticsearchInstanceOfferingId; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/purchaseReservedInstanceOffering", Dict{String, Any}("ReservationName"=>ReservationName, "ReservedElasticsearchInstanceOfferingId"=>ReservedElasticsearchInstanceOfferingId); aws_config=aws_config)
purchase_reserved_elasticsearch_instance_offering(ReservationName, ReservedElasticsearchInstanceOfferingId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/purchaseReservedInstanceOffering", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ReservationName"=>ReservationName, "ReservedElasticsearchInstanceOfferingId"=>ReservedElasticsearchInstanceOfferingId), args)); aws_config=aws_config)

"""
    RejectInboundCrossClusterSearchConnection()

Allows the destination domain owner to reject an inbound cross-cluster search connection request.

# Required Parameters
- `ConnectionId`: The id of the inbound connection that you want to reject.

"""

reject_inbound_cross_cluster_search_connection(ConnectionId; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("PUT", "/2015-01-01/es/ccs/inboundConnection/$(ConnectionId)/reject"; aws_config=aws_config)
reject_inbound_cross_cluster_search_connection(ConnectionId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("PUT", "/2015-01-01/es/ccs/inboundConnection/$(ConnectionId)/reject", args; aws_config=aws_config)

"""
    RemoveTags()

Removes the specified set of tags from the specified Elasticsearch domain.

# Required Parameters
- `ARN`: Specifies the ARN for the Elasticsearch domain from which you want to delete the specified tags.
- `TagKeys`: Specifies the TagKey list which you want to remove from the Elasticsearch domain.

"""

remove_tags(ARN, TagKeys; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/tags-removal", Dict{String, Any}("ARN"=>ARN, "TagKeys"=>TagKeys); aws_config=aws_config)
remove_tags(ARN, TagKeys, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/tags-removal", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ARN"=>ARN, "TagKeys"=>TagKeys), args)); aws_config=aws_config)

"""
    StartElasticsearchServiceSoftwareUpdate()

Schedules a service software update for an Amazon ES domain.

# Required Parameters
- `DomainName`: The name of the domain that you want to update to the latest service software.

"""

start_elasticsearch_service_software_update(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/serviceSoftwareUpdate/start", Dict{String, Any}("DomainName"=>DomainName); aws_config=aws_config)
start_elasticsearch_service_software_update(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/serviceSoftwareUpdate/start", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DomainName"=>DomainName), args)); aws_config=aws_config)

"""
    UpdateElasticsearchDomainConfig()

Modifies the cluster configuration of the specified Elasticsearch domain, setting as setting the instance type and the number of instances.

# Required Parameters
- `DomainName`: The name of the Elasticsearch domain that you are updating.

# Optional Parameters
- `AccessPolicies`: IAM access policy as a JSON-formatted string.
- `AdvancedOptions`: Modifies the advanced option to allow references to indices in an HTTP request body. Must be false when configuring access to individual sub-resources. By default, the value is true. See Configuration Advanced Options for more information.
- `AdvancedSecurityOptions`: Specifies advanced security options.
- `CognitoOptions`: Options to specify the Cognito user and identity pools for Kibana authentication. For more information, see Amazon Cognito Authentication for Kibana.
- `DomainEndpointOptions`: Options to specify configuration that will be applied to the domain endpoint.
- `EBSOptions`: Specify the type and size of the EBS volume that you want to use.
- `ElasticsearchClusterConfig`: The type and number of instances to instantiate for the domain cluster.
- `LogPublishingOptions`: Map of LogType and LogPublishingOption, each containing options to publish a given type of Elasticsearch log.
- `SnapshotOptions`: Option to set the time, in UTC format, for the daily automated snapshot. Default value is 0 hours.
- `VPCOptions`: Options to specify the subnets and security groups for VPC endpoint. For more information, see Creating a VPC in VPC Endpoints for Amazon Elasticsearch Service Domains
"""

update_elasticsearch_domain_config(DomainName; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/domain/$(DomainName)/config"; aws_config=aws_config)
update_elasticsearch_domain_config(DomainName, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/domain/$(DomainName)/config", args; aws_config=aws_config)

"""
    UpgradeElasticsearchDomain()

Allows you to either upgrade your domain or perform an Upgrade eligibility check to a compatible Elasticsearch version.

# Required Parameters
- `DomainName`:
- `TargetVersion`: The version of Elasticsearch that you intend to upgrade the domain to.

# Optional Parameters
- `PerformCheckOnly`:  This flag, when set to True, indicates that an Upgrade Eligibility Check needs to be performed. This will not actually perform the Upgrade.
"""

upgrade_elasticsearch_domain(DomainName, TargetVersion; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/upgradeDomain", Dict{String, Any}("DomainName"=>DomainName, "TargetVersion"=>TargetVersion); aws_config=aws_config)
upgrade_elasticsearch_domain(DomainName, TargetVersion, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = elasticsearch_service("POST", "/2015-01-01/es/upgradeDomain", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DomainName"=>DomainName, "TargetVersion"=>TargetVersion), args)); aws_config=aws_config)


text:
using PtFEM, Test

data = Dict(
  # Frame(nels, nn, ndim, nst, nip, finite_element(nod, nodof))
  :struc_el => Frame(12, 10, 3, 1, 1, Line(2, 3)),
  :properties => [
    1.0 1.0e4 1.0e4 1.0 1.0 1.0 1.0e8;
     ],
  :gamma => [
    0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0],
  :x_coords => [
    0.0, 1.0, 1.0, 2.0,
    2.0, 2.0, 3.0, 3.0,
    3.0, 3.0],
  :y_coords => [
    0.0, 0.0, 1.0, 0.0,
    1.0, 2.0, 0.0, 1.0,
    2.0, 3.0],
  :z_coords => [
    0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0,
    0.0, 0.0],
  :g_num => [
    1 2 2 3 4 5 4 5 6 7 8  9;
    2 3 4 5 5 6 7 8 9 8 9 10],
  :support => [
    (1, [0 0 0 0 0 0]),
    (3, [0 0 0 0 0 0]),
    (6, [0 0 0 0 0 0]),
    (10, [0 0 0 0 0 0])
    ],
  :loaded_nodes => [
    (7, [0.0 0.0 1.0 0.0 0.0 0.0])
    ],
  :limit => 200,
  :tol => 0.00001,
  :incs => 5,
  :dload => [0.5; 0.5; 0.5; 0.05; 0.05]
)

@time m = p45(data)

@test m[2] ≈ [0.0,0.0,0.0,0.0007322670153097881,-0.0007520032370654202,
  -0.00046008115273005434]

ext: jl


text:
module AugmentedGPLikelihoods

using Reexport

using ArraysOfArrays
using ChainRulesCore: @ignore_derivatives
using Distributions
@reexport using GPLikelihoods
using GPLikelihoods: AbstractLikelihood, AbstractLink, BijectiveSimplexLink
using IrrationalConstants
using LogExpFunctions
using MeasureBase: MeasureBase, logdensity_def, marginals
using MeasureTheory: For
using Random: AbstractRNG, GLOBAL_RNG
using SpecialFunctions
using TupleVectors

export nlatent

export init_aux_variables, init_aux_posterior
export aux_sample, aux_sample!, aux_full_conditional
export aux_posterior, aux_posterior!
export auglik_potential,
    auglik_precision, auglik_potential_and_precision, auglik_potential_and_precision
export expected_auglik_potential,
    expected_auglik_precision, expected_auglik_potential_and_precision

export logtilt, expected_logtilt
export aux_prior
export aug_loglik, aux_kldivergence

export ScaledLogistic
export logisticsoftmax
export LogisticSoftMaxLink
export BijectiveSimplexLink

export LaplaceLikelihood, StudentTLikelihood

include("api.jl")
include("generic.jl")
include("SpecialDistributions/SpecialDistributions.jl")
using .SpecialDistributions

include("likelihoods/bernoulli.jl")
include("likelihoods/laplace.jl")
include("likelihoods/negativebinomial.jl")
include("likelihoods/categorical.jl")
include("likelihoods/poisson.jl")
include("likelihoods/studentt.jl")

include("utils.jl")
include("TestUtils.jl")

end


text:
using AMQPClient
using MbedTLS

compose_auth(user::String,password::String) =
    Dict{String,Any}("MECHANISM"=>"AMQPLAIN", "LOGIN"=>user, "PASSWORD"=>password)

struct AmqpConnectionDef
    virtualhost::String
    host::String
    port::Int64
    auth_params::Dict{String,Any}
    amqps::Union{Nothing, MbedTLS.SSLConfig}
    AmqpConnectionDef(host,auth_params) = new("/",host,AMQPClient.AMQP_DEFAULT_PORT,auth_params,Nothing())
end

connection!(conn_def::AmqpConnectionDef) =
    connection(; virtualhost=conn_def.virtualhost, host=conn_def.host, port=conn_def.port, auth_params=conn_def.auth_params, amqps=conn_def.amqps)

function close_connection!(conn)
    if isopen(conn)
        close(conn)
        AMQPClient.wait_for_state(conn, AMQPClient.CONN_STATE_CLOSED)
    end
end

function amqp_connection!(f::Function, conn_def::AmqpConnectionDef)
    conn = connection!(conn_def)
    try
        f(conn)
    finally
        close_connection!(conn)
    end
end

_conn_def = Nothing

function amqp_conn_define!(conn_def::AmqpConnectionDef)
    global _conn_def = conn_def
end


text:
################################################################################
# same with LLL
################################################################################

function single_env(c::ClassGrpCtx{T}, I::Hecke.SmallLLLRelationsCtx, rat::Float64, max_good::Int = 2) where {T}
  bad_norm = 0
  rk = rank(c.M)
  good = 0
  O = order(c)
  K = nf(O)
  while true
    e = class_group_small_lll_elements_relation_next(I)
    n = norm(e, c.normCtx, norm(I.A))
    if I.cnt > length(I.b)^2
      break
    end
    if n == nothing
      #@show "BadNorm"
      bad_norm += 1
      if I.cnt > 100  && bad_norm / I.cnt > 0.1
        @vprint :ClassGroup 2 "norm too large, $(I.cnt) \n"
        break
      end
      continue
    end
    bef = length(c.M.bas_gens) #+ length(c.M.rel_gens)
    fl, r = is_smooth!(c.FB.fb_int, n)
    fl || push_normStat!(c, n, fl)
    fl || (c.bad_rel += 1)
    fl = fl || (r < c.B2 && is_prime(r))
    if fl
      ee = K(O(fmpz[e[1, i] for i=1:degree(K)]))
      fl = class_group_add_relation(c, ee, fmpq(n), norm(I.A), integral = true)
    end
    if !fl  && I.cnt/(good+1) > 2*c.expect
      @vprint :ClassGroup 2 "not enough progress $(I.cnt) $(c.expect) $good\n"
      break
    end
    if fl
      good += length(c.M.bas_gens) #= + length(c.M.rel_gens) =# - bef
    end
    if fl && max_good > -1
      if max_good < good
        @vprint :ClassGroup 2 "found enough $(I.cnt)\n"
        break
      end
    end
    if false && fl && good > 0 && (rank(c.M)- rk+1)/good < rat
      @vprint :ClassGroup 2 "rank too slow $(I.cnt) $good $(rank(c.M)) $rk\n"
      break
    end
  end
  @vprint :ClassGroup 2 "delta rank: $(rank(c.M) - rk) found $good rels\n"
  return (rank(c.M) - rk), good
end

function class_group_via_lll(c::ClassGrpCtx{T}, rat::Float64 = 0.2) where {T}
  O = order(c.FB.ideals[1])

  rr1, rr2 = signature(O)
  rt = time_ns()
  I = class_group_small_lll_elements_relation_start(c, O)
  single_env(c, I, rat/10, -1)
  @vprint :ClassGroup 1 "search in order:  $((time_ns()-rt)*1e-9) rel mat:  $(c.M.bas_gens)\nin $(c.M)"

  @vtime :ClassGroup 1 h, piv = class_group_get_pivot_info(c)
  if h == 1 return c; end

#  for p = piv
#    I = class_group_small_lll_elements_relation_start(c, c.FB.ideals[p])
#    single_env(c, I, nb, rat, 1)
#  end
#
#  @vprint :ClassGroup 1 "search in ideals:  $((time_ns()-rt)*1e-9) rel mat:  $(c.M.bas_gens)\n"

#  @vtime :ClassGroup 1 h, piv = class_group_get_pivot_info(c)
#  if h == 1 return c; end

#  @vprint :ClassGroup 1 "Now with random...\n"
#  @vprint :ClassGroup 1 "length(piv) = $(length(piv)) and h = $h\n"
#  @vprint :ClassGroup 1 "$(piv)\n"

  #while iszero(h) || length(c.M.rel_gens) < rr1 + rr2 - 1
    class_group_new_relations_via_lll(c, rat, extra = -1)
  #end

  return c
end

function class_group_new_relations_via_lll(c::ClassGrpCtx{T}, rat::Float64 = 0.2; extra::Int = 5, rand_exp::Int = 1) where {T}

  O = order(c.FB.ideals[1])

  st = c.rel_cnt

  @vtime :ClassGroup 1 h, piv = class_group_get_pivot_info(c)

  @vprint :ClassGroup 1 "Now with random...\n"
  @vprint :ClassGroup 1 "length(piv) = $(length(piv)) and h = $h\n"
  @vprint :ClassGroup 1 "$(piv)\n"
  if length(piv) == 0
    for i=1:5
      push!(piv, rand(1:length(c.FB.ideals)))
    end
    @vprint :ClassGroup 1 "piv was empty, supplemented it to\n"
    @vprint :ClassGroup 1 "$(piv)\n"
  end

  start = max(1, max(div(length(c.FB.ideals), 2)+1, length(c.FB.ideals)-10*(1+div(rand_exp, 3))))
  stop = length(c.FB.ideals)
  if isdefined(c, :randomClsEnv)
    rand_env = c.randomClsEnv
    random_extend(rand_env, 2.0)
    @vprint :ClassGroup 1 "re-using and extending random: $(nbits(norm(rand_env.rand))) and $(rand_env.exp)\n"
  else
    @vprint :ClassGroup 1 "want $(stop-start) primes for random. Try distinct rational primes...\n"
#    @show start, stop
    JJ = NfOrdIdl[c.FB.ideals[stop]]
    start += length(c.FB.ideals) - stop
    stop  += length(c.FB.ideals) - stop
    i = stop
    minimums = fmpz[minimum(x, copy = false) for x = JJ]
    while i>1 && length(JJ) < stop - start
      i -= 1
      if degree(c.FB.ideals[i]) > 1 || minimum(c.FB.ideals[i], copy = false) in minimums
        continue
      end
      push!(JJ, c.FB.ideals[i])
      push!(minimums, minimum(c.FB.ideals[i], copy = false))
    end
    while length(JJ) < stop - start
      AA = rand(c.FB.ideals)
      if !(AA in JJ)
        push!(JJ, AA)
      end
    end
#    @show [ (norm(x), minimum(x)) for x = JJ]
    rand_env = random_init(JJ, lb = root(abs(discriminant(O)), 2)^1, ub = abs(discriminant(O))^1, reduce = false)
    c.randomClsEnv = rand_env
  end

  if h > 0
    rand_exp += 1
    while gcd(h, rand_exp) > 1
      rand_exp += 1
    end
  end

  n_idl = 0
  while true
    st = c.rel_cnt
    while (c.rel_cnt - st < 2)
      sort_rev = rank(c.M) < length(c.FB.ideals) *0.9
      for p = sort(collect(piv), rev = sort_rev)
        @vprint :ClassGroup 1 "$(set_cursor_col())$(clear_to_eol())#ideals tested: $n_idl, pivot ideal: $p, exp: $rand_exp, #rand base: $(length(rand_env.base))"
        @vprint :ClassGroup 2 "\n" #otherwise the printing above is horrible
        @vtime :ClassGroup 3 J = random_get(rand_env, reduce = false)
        #@show nbits(norm(J)), rand_env.exp, rand_exp
        @vtime :ClassGroup 3 J *= c.FB.ideals[p]^rand_exp
        @vtime :ClassGroup 3 I = class_group_small_lll_elements_relation_start(c, J)
        n_idl += 1
        @vtime :ClassGroup 3 single_env(c, I, 0.8, length(c.FB.ideals)/rank(c.M) < 2 ? 1 : -1)
        if h == 0 && rank(c.M) == length(c.FB.ideals)
          #reached full rank for the 1st time!!
          h, p = class_group_get_pivot_info(c)
          if h > 0
            break
          end
        end
        if h > 0 && c.rel_cnt - st > 1
          return nothing
        else
          if h == 0 && c.rel_cnt - st > length(piv) + 2
            break
          end
        end
      end
    end

    @vprint :ClassGroup 1 "eval info\n"
    @vtime :ClassGroup 1 h, piv_new = class_group_get_pivot_info(c)
    @vprint :ClassGroup 1 "length(piv) = $(length(piv_new)) and h = $h\n"
    @vprint :ClassGroup 1 "$(piv_new)\n"

    if piv_new == piv
      if h > 0
        extra = 2
#        J = [rand(c.FB.ideals) for x=1:10]
        #println("extending rand")
#        random_extend(rand_env, J)
        random_extend(rand_env, root(abs(discriminant(O)), 2))
      end
      rand_exp += 1
      rand_exp = min(rand_exp, 13)
      if h>0
        while gcd(rand_exp, h) > 1
          rand_exp += 1
        end
      end

      if rand_exp % 3 == 0
        start = max(start -10, 1)
      end
    end
    piv = piv_new
    if h == 1
      return nothing
    end
  end
end


text:
using DelimitedFiles
using Printf

puzzleinput = readdlm("inputs/day14.txt", ' ')
puzzleinput = hcat(puzzleinput[:, 1], puzzleinput[:, 3])

struct Program
    mask::String
    instrs::Vector{NamedTuple{(:adr, :val),Tuple{UInt64,UInt64}}}
end

function readinput(a::Array{Any,2})
    # Parse the input filling an array of Program structures

    progs = Program[]

    for (lbl, val) in eachrow(a)
        if lbl == "mask"
            push!(progs, Program(val, []))
        else
            adr = parse(Int, lbl[5:end - 1])
            push!(progs[end].instrs, (adr = adr, val = val))
        end
    end
    progs
end

progs = readinput(puzzleinput)

# First Half

Base.:|(mc::Char, c::Char) = (mc == 'X') ? c : mc
# Redefines | operator on chars to work as the mask
# described in the 1st half of the puzzle

function execute₁(progs)
    # Executes all the programs in progs filling the memory,then returns it

    function masked(mask::String, n::UInt64)
        s = collect(bitstring(n)[29:end])
        parse(UInt64, String(collect(mask) .| s), base=2)
    end

    mem = Dict()
    for p in progs
        for i in p.instrs
            mem[i.adr] = masked(p.mask, i.val)
        end
    end
    mem
end

mem = execute₁(progs)

sumofvals = let acc = 0
    for (adr, val) in mem
        acc += val
    end
    acc
end

@sprintf("%d", sumofvals)

# Seconf Half

function Base.:&(mc::Char, c::Char)
    if mc == 'X'
        'X'
    else
        mc == '0' ? c : '1'
    end
end
# Redefines & operator on chars to work as the mask
# described in the 2nd half of the puzzle

function execute₂(progs)
    # Executes all the programs in progs filling the memory,then returns it

    function apply(mask::String, n::UInt64)
        # Apply the mask to the address n and generate a pool
        # of address with all the possible values of the floating bits

        s = collect(bitstring(n)[29:end])
        masked = String(collect(mask) .& s)
        nX = count("X", masked)

        pool = []
        for b in 0:(2^nX - 1)
            # Every possible combination of bits of length nX is
            # all the binary number from 0 to 2^nX - 1

            d = bitstring(b)[(end - nX + 1):end]
            # Converts the binary number in a
            s = masked
            i = 1
            while true
                # For every possible combination builds the address
                # replacing every floating bit (X) with the corresponding
                # bit in the combination

                pos = findnext('X', s, 1)
                if isnothing(pos)
                    break
                end
                s = s[1:pos - 1] * "$(d[i])" * s[pos + 1:end]
                i += 1
            end
            push!(pool, parse(UInt64, s, base=2))
            # The built address is added to the pool

        end
        pool
    end

    mem = Dict()
    for p in progs
        println(p.mask)
        for i in p.instrs
            adrs = apply(p.mask, i.adr)
            for a in adrs
                mem[a] = i.val
            end
        end
    end
    mem
end

mem = execute₂(progs)

sumofvals = let acc = 0
    for (adr, val) in mem
        acc += val
    end
    acc
end

@sprintf("%d", sumofvals)


text:
######################
# Characters
######################

# Basic characters
c1 = 'b'

println(c1) # b
println(Int(c1)) # 98

# Char from int
c2 = Char(97)

println(c2) # a
println(Int(c2)) # 97

c3 = c1+1 # 'b' + 1 = 'c'
println(c3 == 'c') # true

println(c2 < c1) # true

# Char from hexadecimal number ( 0x..., '\u...', '\U...')
# \u is followed by up to four hexadecimal digits
# \U is followed by up to eight hexadecimal digits
hex_c1 = Char(0x63)
hex_c2 = Char('\u64')
hex_c3 = '\U101234'

println(hex_c1) # c
println(hex_c2) # d
println(hex_c3) # 􁈴
println(Int(hex_c1)) # 99
println(Int(hex_c2)) # 100
println(Int(hex_c3)) # 1053236

######################
# Strings
######################

# Basic string
str = "This is a string. \n"
println(str)

# Breaking up a string
broken_str = "This is a \
             broken string"
println(broken_str) # This is a broken string

# Strings with quotes
quote_str1 = """This string contains "quotes". \n""" # """ + str + """
quote_str2 = "This string contains \"quotes\" also. \n" # str + \" + str
println(quote_str1) # This string contains "quotes".
println(quote_str2) # This string contains "quotes" also.

# Accessing string
first_char = str[begin]
println(first_char) # T

first_char = str[1] # INDEX STARTS AT 1
println(first_char) # T

last_char = str[end]
println(last_char) # \n

middle_char = str[div(end,2)] # div(x,y) -> truncated division
println(middle_char) # a

this_part = str[1:4]
println(this_part) # This

# String indices in Julia refer to code units (= bytes for UTF-8),
# the fixed-width building blocks that are used to encode arbitrary characters (code points).
# This means that not every index into a String is necessarily a valid index for a character.

big_chars = "\u2200 x \u2203 Y"
println(big_chars) # ∀ x E y

for i = firstindex(big_chars):lastindex(big_chars)
    try
        println("str[",i,"] : ",big_chars[i])
    catch
        println("str[",i,"] is a part of the previous character")
    end
end

# To avoid indexing problems in string with large characters
for char in big_chars
    print(char)
end
println() # ∀ x ∃ Y

# List of falid indices
valid_indices = eachindex(big_chars)

for idx in valid_indices
    print(big_chars[idx])
end
println() # ∀ x ∃ Y

# Concatenation
str1 = "string 1"
str2 = "string 2"

concat_str = string("1: ",str1, " ", str2)
println(concat_str)

concat_str = "2: " * str1 * " " * str2
println(concat_str)

# Interpolation ( $ )

# for concatenation
concat_str = "$str1 $str2"
println(concat_str)

# With expressions
exp = "(3 + 4) * (5 - 2) = $((3 - 4) * (5 - 2))"
println(exp)

# With arrays
arr = [1, -4, 3, 5]
println("Array : $arr")

# Strings are comparable
abc = "abc"
println(abc < "abd") # true
println(abc == "abc") # true

# findfirst(), findlast(), findnext() and findprev()
word = "xylophone"
first_o = findfirst(isequal('o'), word)
last_o = findlast(isequal('o'), word)

first_after_5 = findnext(isequal('o'), word, 5);
first_before_5 = findprev(isequal('o'), word, 5);

println("First o at: $first_o")
println("Second o at: $last_o")
println("There is o at $first_before_5 and $first_after_5")

# Check if the string contains another string or char
println(occursin("phon", "xylophon")) # true
println(occursin('a', "xylophon")) # false

max_stars_repo_name: Nikola-Mircic/julia-cheatsheet

max_issues_repo_name: Nikola-Mircic/julia-cheatsheet

max_forks_repo_name: Nikola-Mircic/julia-cheatsheet


text:
@testset "Deprecations" begin
    # RodriguesVec
    @test Base.isdeprecated(Rotations, :UnitQuaternion)
end


text:
# EXCLUDE FROM TESTING
using VectorEngine

#
# pass a char string
#

function pass_string(r::Ptr{UInt8})
      @veshow(r)
      @veprintf("[VE string]: %s\n", r)
      return
end

veps = VectorEngine.vefunction(pass_string, Tuple{Ptr{UInt8}})
veps("1234567890abcdefghij")

synchronize()


text:
"""
    AbstractProjected <: AbstractSampled

Abstract supertype for projected index lookups.
"""
abstract type AbstractProjected{T,O,Sp,Sa} <: AbstractSampled{T,O,Sp,Sa} end

struct AutoDim end

# For now we just remove CRS on GPU - it often contains strings
function Adapt.adapt_structure(to, l::AbstractProjected)
    sampled = Sampled(parent(l), order(l), span(l), sampling(l), metadata(l))
    return Adapt.adapt_structure(to, sampled)
end

crs(lookup::LookupArray) = nothing
mappedcrs(lookup::LookupArray) = nothing

# When the lookup is formatted with an array we match the `dim` field with the
# wrapper dimension. We will need this later for e.g. projecting with GDAL,
# where we need to know which lookup is X and which is Y
function Dimensions.format(m::AbstractProjected, D::Type, index, axis::AbstractRange)
    i = Dimensions._format(index, axis)
    o = Dimensions._format(order(m), D, index)
    sp = Dimensions._format(span(m), D, index)
    sa = Dimensions._format(sampling(m), sp, D, index)
    dim = Dimensions.basetypeof(D)()
    x = rebuild(m; data=i, order=o, span=sp, sampling=sa, dim=dim)
    return x
end

"""
    Projected <: AbstractProjected

    Projected(order, span, sampling, crs, mappedcrs)
    Projected(; order=AutoOrder(), span=AutoSpan(), sampling=AutoSampling(), crs, mappedcrs=nothing)

An [`AbstractSampled`]($DDabssampleddocs) `LookupArray` with projections attached.

Fields and behaviours are identical to [`Sampled`]($DDsampleddocs)
with the addition of `crs` and `mappedcrs` fields.

If both `crs` and `mappedcrs` fields contain CRS data (in a `GeoFormat` wrapper
from GeoFormatTypes.jl) the selector inputs and plot axes will be converted
from and to the specified `mappedcrs` projection automatically. A common use case
would be to pass `mappedcrs=EPSG(4326)` to the constructor when loading eg. a GDALarray:

```julia
GDALarray(filename; mappedcrs=EPSG(4326))
```

The underlying `crs` will be detected by GDAL.

If `mappedcrs` is not supplied (ie. `mappedcrs=nothing`), the base index will be
shown on plots, and selectors will need to use whatever format it is in.
"""
struct Projected{T,A<:AbstractVector{T},O<:Order,Sp<:Span,Sa<:Sampling,MD,PC,MC,D} <: AbstractProjected{T,O,Sp,Sa}
    data::A
    order::O
    span::Sp
    sampling::Sa
    metadata::MD
    crs::PC
    mappedcrs::MC
    dim::D
end
function Projected(data=AutoIndex();
    order=AutoOrder(), span=AutoSpan(), sampling=AutoSampling(),
    metadata=NoMetadata(), crs, mappedcrs=nothing, dim=AutoDim()
)
    Projected(data, order, span, sampling, metadata, crs, mappedcrs, dim)
end
function Projected(l::Sampled;
    order=order(l), span=span(l), sampling=sampling(l),
    metadata=metadata(l), crs, mappedcrs=nothing, dim=AutoDim()
)
    Projected(parent(l), order, span, sampling, metadata, crs, mappedcrs, dim)
end

crs(lookup::Projected) = lookup.crs
mappedcrs(lookup::Projected) = lookup.mappedcrs
dim(lookup::Projected) = lookup.dim

function LA.selectindices(l::Projected, sel::Contains)
    selval = reproject(mappedcrs(l), crs(l), dim(l), val(sel))
    LA.contains(l, rebuild(sel; val=selval))
end
function LA.selectindices(l::Projected, sel::At)
    selval = reproject(mappedcrs(l), crs(l), dim(l), val(sel))
    LA.at(l, rebuild(sel; val=selval))
end
function LA.selectindices(l::Projected, sel::Between)
    selval = map(v -> reproject(mappedcrs(l), crs(l), dim(l), v), val(sel))
    LA.between(l, rebuild(sel; val=selval))
end

"""
    Mapped <: AbstractProjected

    Mapped(order, span, sampling, crs, mappedcrs)
    Mapped(; order=AutoOrder(), span=AutoSpan(), sampling=AutoSampling(), crs=nothing, mappedcrs)

An [`AbstractSampled`]($DDabssampleddocs) `LookupArray`, where the dimension index has
been mapped to another projection, usually lat/lon or `EPSG(4326)`.

Fields and behaviours are identical to [`Sampled`]($DDsampleddocs) with the addition of
`crs` and `mappedcrs` fields.

The mapped dimension index will be used as for [`Sampled`]($DDsampleddocs),
but to save in another format the underlying `projectioncrs` may be used.
"""
struct Mapped{T,A<:AbstractVector{T},O<:Order,Sp<:Span,Sa<:Sampling,MD,PC,MC,D} <: AbstractProjected{T,O,Sp,Sa}
    data::A
    order::O
    span::Sp
    sampling::Sa
    metadata::MD
    crs::PC
    mappedcrs::MC
    dim::D
end
function Mapped(data=AutoIndex();
    order=AutoOrder(), span=AutoSpan(), sampling=AutoSampling(),
    metadata=NoMetadata(), crs=nothing, mappedcrs, dim=AutoDim()
)
    Mapped(data, order, span, sampling, metadata, crs, mappedcrs, dim)
end
function Mapped(l::Sampled;
    order=order(l), span=span(l), sampling=span(l),
    metadata=metadata(l), crs=nothing, mappedcrs, dim=AutoDim()
)
    Mapped(parent(l), order, span, sampling, metadata, crs, mappedcrs, dim)
end

crs(lookup::Mapped) = lookup.crs
mappedcrs(lookup::Mapped) = lookup.mappedcrs
dim(lookup::Mapped) = lookup.dim

"""
    convertlookup(dstlookup::Type{<:LookupArray}, x)

Convert the dimension lookup between `Projected` and `Mapped`.
Other dimension lookups pass through unchanged.

This is used to e.g. save a netcdf file to GeoTiff.
"""
convertlookup(T::Type{<:LookupArray}, A::AbstractDimArray) =
    rebuild(A; dims=convertlookup(T, dims(A)))
convertlookup(T::Type{<:LookupArray}, dims::Tuple) = map(d -> convertlookup(T, d), dims)
convertlookup(T::Type{<:LookupArray}, d::Dimension) = rebuild(d, convertlookup(T, lookup(d)))
# Non-projected LookupArray lookupss pass through
convertlookup(::Type, lookup::LookupArray) = lookup
# AbstractProjected passes through if it's the same as dstlookup
convertlookup(::Type{T1}, lookup::T2) where {T1,T2<:T1} = lookup
# Otherwise AbstractProjected needs ArchGDAL
function convertlookup(::Type{<:Mapped}, l::Projected)
    newindex = reproject(crs(l), mappedcrs(l), dim(l), index(l))
    # We use Explicit mode and make a bounds matrix
    # This way the bounds can be saved correctly to NetCDF
    newbounds = reproject(crs(l), mappedcrs(l), dim(l), Dimensions.dim2boundsmatrix(l))
    return Mapped(newindex,
        order=order(l),
        span=Explicit(newbounds),
        sampling=sampling(l),
        metadata=metadata(l),
        crs=crs(l),
        mappedcrs=mappedcrs(l),
        dim=dim(l),
    )
end
function convertlookup(::Type{<:Projected}, l::Mapped)
    newindex = _projectedrange(l)
    return Projected(newindex;
        order=order(l),
        span=Regular(step(newindex)),
        sampling=sampling(l),
        metadata=metadata(l),
        crs=crs(l),
        mappedcrs=mappedcrs(l),
        dim=dim(l),
    )
end

_projectedrange(l::Projected) = LinRange(first(l), last(l), length(l))
_projectedrange(l::Mapped) = _projectedrange(span(l), crs(l), l)
function _projectedrange(span, crs, l::Mapped)
    start, stop = reproject(mappedcrs(l), crs, dim(l), [first(l), last(l)])
    LinRange(start, stop, length(l))
end
_projectedrange(::Regular, crs::Nothing, l::Mapped) = LinRange(first(l), last(l), length(l))
function _projectedrange(::T, crs::Nothing, l::Mapped) where T<:Union{Irregular,Explicit}
    error("Cannot convert a Mapped $T index to Projected when crs is nothing")
end

"""
    setcrs(x, crs)

Set the crs of a `Raster`, `RasterStack`, `Tuple` of `Dimension`,or a `Dimension`.
"""
setcrs(dims::DimTuple, crs) = map(d -> setcrs(d, crs), dims)
function setcrs(dim::Dimension, crs)
    rebuild(dim, setcrs(parent(dim), crs; dim=basetypeof(dim)()))
end
setcrs(l::AbstractProjected, crs) = rebuild(l; crs)
function setcrs(l::Sampled, crs; dim)
    dim isa Union{XDim,YDim} ? Projected(l; crs, dim) : l
end
setcrs(A::AbstractArray, crs) = A

"""
    setmappedcrs(x, crs)

Set the mapped crs of a `Raster`, a `RasterStack`, a `Tuple`
of `Dimension`, or a `Dimension`.
"""
setmappedcrs(dims::DimTuple, mappedcrs) = map(d -> setmappedcrs(d, mappedcrs), dims)
function setmappedcrs(dim::Dimension, mappedcrs)
    rebuild(dim, setmappedcrs(parent(dim), mappedcrs; dim))
end
setmappedcrs(l::AbstractProjected, mappedcrs; dim) = rebuild(l; mappedcrs, dim=basetypeof(dim)())
setmappedcrs(A::AbstractArray, mappedcrs; dim=nothing) = A
function setmappedcrs(l::Sampled, mappedcrs; dim)
    dim isa Union{XDim,YDim} ? Mapped(l; mappedcrs, dim) : l
end

"""
    mappedbounds(x)

Get the bounds converted to the [`mappedcrs`](@ref) value.

Whithout ArchGDAL loaded, this is just the regular bounds.
"""
function mappedbounds end

mappedbounds(dims::Tuple) = map(mappedbounds, dims)
mappedbounds(dim::Dimension) = mappedbounds(parent(dim), dim)
mappedbounds(::LookupArray, dim) = bounds(dim)
mappedbounds(lookup::Projected, dim) = mappedbounds(mappedcrs(lookup), lookup, dim)
mappedbounds(mappedcrs::Nothing, lookup::Projected, dim) =
    error("No mappedcrs attached to $(name(dim)) dimension")
mappedbounds(mappedcrs::GeoFormat, lookup::Projected, dim) =
    _sort(reproject(crs(lookup), mappedcrs, dim, bounds(dim)))

projectedbounds(dims::Tuple) = map(projectedbounds, dims)
projectedbounds(dim::Dimension) = projectedbounds(parent(dim), dim)
projectedbounds(::LookupArray, dim) = bounds(dim)
projectedbounds(lookup::Mapped, dim) = projectedbounds(crs(lookup), lookup, dim)
projectedbounds(crs::Nothing, lookup::Mapped, dim) =
    error("No projection crs attached to $(name(dim)) dimension")
projectedbounds(crs::GeoFormat, lookup::Mapped, dim) =
    _sort(reproject(mappedcrs(lookup), crs, dim, bounds(dim)))

_sort((a, b)) = a <= b ? (a, b) : (b, a)

"""
    mappedindex(x)

Get the index value of a dimension converted to the `mappedcrs` value.

Whithout ArchGDAL loaded, this is just the regular dim value.
"""
function mappedindex end

mappedindex(dims::Tuple) = map(mappedindex, dims)
mappedindex(dim::Dimension) = _mappedindex(parent(dim), dim)

_mappedindex(::LookupArray, dim::Dimension) = index(dim)
_mappedindex(lookup::Projected, dim::Dimension) = _mappedindex(mappedcrs(lookup), lookup, dim)
_mappedindex(mappedcrs::Nothing, lookup::Projected, dim) =
    error("No mappedcrs attached to $(name(dim)) dimension")
_mappedindex(mappedcrs::GeoFormat, lookup::Projected, dim) =
    reproject(crs(dim), mappedcrs, dim, index(dim))

projectedindex(dims::Tuple) = map(projectedindex, dims)
projectedindex(dim::Dimension) = _projectedindex(parent(dim), dim)

_projectedindex(::LookupArray, dim::Dimension) = index(dim)
_projectedindex(lookup::Mapped, dim::Dimension) = _projectedindex(crs(lookup), lookup, dim)
_projectedindex(crs::Nothing, lookup::Mapped, dim::Dimension) =
    error("No projection crs attached to $(name(dim)) dimension")
_projectedindex(crs::GeoFormat, lookup::Mapped, dim::Dimension) =
    reproject(mappedcrs(dim), crs, dim, index(dim))


text:
"""
    Base.fft(td::TimeDomainData)

    Override Base.fft for the type CircuitSimulation.TimeDomainData.
"""
function Base.fft(td::TimeDomainData)
    Y = fft(td.vals)
    fs = fft_freqs(td.ts)
    return FrequencyDomainData(fs, Y)
end
"""
    Base.ifft(fd::FrequencyDomainData)

    Override Base.fft for the type CircuitSimulation.FrequencyDomainData.
"""
function Base.ifft(fd::FrequencyDomainData)
    y = ifft(fd.vals)
    ts = ifft_times(fd.fs)
    if any(abs.(angle.(y).%pi).>1e-1)
        error("Transformed data is not a vector of real values. Check to ensure
              your frequency-domain data is hermitian (complex conjugate
              symmetric about DC).")
    end
    return TimeDomainData(ts, real(y))
end


text:
struct TimeDateZone <: NanosecondBasis
    timestamp::TimeDate
    inzone::AkoTimeZone               # one of {FixedTimeZone, VariableTimeZone}
    atzone::FixedTimeZone

    # ensure other constructors will be give explictly

    function TimeDateZone(attime::Time, ondate::Date, inzone::VariableTimeZone, atzone::FixedTimeZone)
        tmdt = TimeDate(attime, ondate)
        return new(tmdt, inzone, atzone)
    end
    function TimeDateZone(attime::Time, ondate::Date, inzone::FixedTimeZone, atzone::FixedTimeZone)
        tmdt = TimeDate(attime, ondate)
        return new(tmdt, inzone, atzone)
    end
end

@inline timestamp(x::TimeDateZone) = x.timestamp
@inline at_time(x::TimeDateZone) = x.timestamp.attime
@inline on_date(x::TimeDateZone) = x.timestamp.ondate
@inline in_zone(x::TimeDateZone) = x.inzone
@inline at_zone(x::TimeDateZone) = x.atzone

@inline timestamp(x::ZonedDateTime) = DateTime(x)
@inline at_time(x::ZonedDateTime) = Time(DateTime(x))
@inline on_date(x::ZonedDateTime) = Date(DateTime(x))
@inline in_zone(x::ZonedDateTime) = x.timezone
@inline at_zone(x::ZonedDateTime) = x.zone

@inline timestamp(x::TimeDate) = x
@inline timestamp(x::DateTime) = x
@inline timestamp(x::Date) = x + Time(0)

TimeDateZone(x::TimeDateZone) = x
TimeDate(x::TimeDateZone) = x.timestamp

function TimeDateZone(attime::Time, ondate::Date, atzone::FixedTimeZone)
    return TimeDateZone(attime, ondate, atzone, atzone)
end

function TimeDateZone(attime::Time, ondate::Date, inzone::VariableTimeZone)
    fast_time, slow_time = fastpart(attime), slowpart(attime)
    datetime = ondate + slow_time
    zdt = ZonedDateTime(datetime, inzone)
    atzone = at_zone(zdt)
    tim, dat = timedate(zdt)
    tim = tim + fast_time
    tdz = TimeDateZone(tim, dat, inzone, atzone)
    return tdz
end

TimeDateZone(ondate::Date, attime::Time, inzone::VariableTimeZone) =
    TimeDateZone(attime, ondate, inzone)

TimeDateZone(ondate::Date, attime::Time, atzone::FixedTimeZone) =
    TimeDateZone(attime, ondate, atzone, atzone)

@inline function ZonedDateTime(tdz::TimeDateZone)
    datetime = DateTime(timestamp(tdz))
    inzone = in_zone(tdz)
    zdt = ZonedDateTime(datetime, inzone)
    return zdt
end

@inline function TimeDateZone(zdt::ZonedDateTime)
    tim, dat = timedate(DateTime(zdt))
    inzone = in_zone(zdt)
    atzone = at_zone(zdt)
    tdz = TimeDateZone(tim, dat, inzone, atzone)
    return tdz
end

@inline function TimeDateZone(datetime::DateTime, tzone::T, idx::Int) where {T<:AkoTimeZone}
    zdt = ZonedDateTime(datetime, tzone, idx)
    return TimeDateZone(zdt)
end

TimeDateZone(datetime::DateTime, inzone::T) where {T<:AkoTimeZone} =
    TimeDateZone(ZonedDateTime(datetime, inzone))

TimeDateZone(tmdt::TimeDate, inzone::T) where {T<:AkoTimeZone} =
    TimeDateZone(Time(tmdt), Date(tmdt), inzone)

TimeDateZone(dat::Date, inzone::T) where {T<:AkoTimeZone} =
    TimeDateZone(ZonedDateTime(dat+Time(0), inzone))

function astimezone(tdz::TimeDateZone, tzone::T) where {T<:AkoTimeZone}
    TimeDateZone(astimezone(ZonedDateTime(tdz), tzone)) + fastpart(tdz)
end

"""
    utcoffset(FixedTimeZone)

    offset from UT to LocalTime in Seconds
"""
@inline function utcoffset(tz::FixedTimeZone)
    ofs = tz.offset
    return ofs.std + ofs.dst
end

@inline utcoffset(zdt::ZonedDateTime) = utcoffset(at_zone(zdt))
@inline utcoffset(tdz::TimeDateZone) = utcoffset(at_zone(tdz))
@inline utcoffset(tz::VariableTimeZone) = utcoffset(TimeZones.now(tz))

TimeDateZone(cperiod::CompoundPeriod, tzone::T) where {T<:AkoTimeZone} =
    TimeDateZone(TimeDate(cperiod), tzone)

@inline function Date(tdz::TimeDateZone)
    timedate = timestamp(tdz)
    dt = on_date(timedate)
    return dt
end

@inline function Time(tdz::TimeDateZone)
    timedate = timestamp(tdz)
    tm = at_time(timedate)
    return tm
end

@inline function DateTime(tdz::TimeDateZone)
    timedate = timestamp(tdz)
    tm = slowpart(at_time(timedate))
    dt = on_date(timedate)
    return dt+tm
end

@inline function fastpart(tdz::TimeDateZone)
    fast_time = fastpart(at_time(tdz))
    return isempty(fast_time) ? Nanosecond(0) : fast_time
end

@inline slowpart(tdz::TimeDateZone) = slowpart(at_time(tdz))

@inline fastpart(zdt::ZonedDateTime) = Nanosecond(0)
@inline slowpart(zdt::ZonedDateTime) = Time(DateTime(zdt))


text:
for (mac, lvl) in ((:debug, Debug.DEBUG),
                  (:info, Debug.INFO),
                  (:warn, Debug.WARN),
                  (:error, Debug.ERROR))
    @eval macro $mac(msg)
        if !isdefined(:level)
            level = Debug.OFF
        end
        if level >= $lvl
            return :(Expr(:call, Debug.log, [$lvl, msg]...))
        end
        return :()
    end
end


text:
Base.Broadcast._containertype(::Type{<:DataValue}) = DataValue

Base.Broadcast.promote_containertype(::Type{Any}, ::Type{DataValue}) = DataValue
Base.Broadcast.promote_containertype(::Type{DataValue}, ::Type{Any}) = DataValue
Base.Broadcast.promote_containertype(::Type{Tuple}, ::Type{DataValue}) = Tuple
Base.Broadcast.promote_containertype(::Type{DataValue}, ::Type{Tuple}) = Tuple

Base.Broadcast.broadcast_indices(::Type{DataValue}, A) = ()

Base.Broadcast._unsafe_get_eltype(x::DataValue) = eltype(x)

Base.Broadcast._broadcast_getindex_eltype(::Type{DataValue}, T::Type) = Type{T}
Base.Broadcast._broadcast_getindex_eltype(::Type{DataValue}, A) = typeof(A)

Base.@propagate_inbounds Base.Broadcast._broadcast_getindex(::Type{DataValue}, A, I) = A

@inline function Base.Broadcast.broadcast_c(f, ::Type{DataValue}, a...)
    nonnull = all(hasvalue, a)
    S = _nullable_eltype(f, a...)
    if isleaftype(S) && Base.null_safe_op(f, maptoTuple(_unsafe_get_eltype,a...).types...)
        DataValue{S}(f(map(unsafe_get, a)...), nonnull)
    else
        if nonnull
            DataValue(f(map(unsafe_get, a)...))
        else
            DataValue{nullable_returntype(S)}()
        end
    end
end


text:
#!/usr/bin/env julia

using DataFrames
import FITSIO

import Celeste.AccuracyBenchmark
import Celeste.ArgumentParse
import Celeste.SDSSIO

const OUTPUT_DIRECTORY = joinpath(splitdir(Base.source_path())[1], "output")

parser = Celeste.ArgumentParse.ArgumentParser()
ArgumentParse.add_argument(
    parser,
    "--run",
    help="SDSS run #",
    arg_type=Int,
    default=AccuracyBenchmark.STRIPE82_RCF.run,
)
ArgumentParse.add_argument(
    parser,
    "--camcol",
    help="SDSS camcol #",
    arg_type=Int,
    default=AccuracyBenchmark.STRIPE82_RCF.camcol,
)
ArgumentParse.add_argument(
    parser,
    "--field",
    help="SDSS field #",
    arg_type=Int,
    default=AccuracyBenchmark.STRIPE82_RCF.field,
)
parsed_args = ArgumentParse.parse_args(parser, ARGS)

run_camcol_field = SDSSIO.RunCamcolField(
    parsed_args["run"],
    parsed_args["camcol"],
    parsed_args["field"],
)
@printf("Reading %s...\n", run_camcol_field)
catalog_df = AccuracyBenchmark.load_primary(run_camcol_field, AccuracyBenchmark.SDSS_DATA_DIR)
@printf("Loaded %d objects from catalog\n", size(catalog_df, 1))

output_filename = @sprintf(
    "sdss_%s_%s_%s_primary.csv",
    run_camcol_field.run,
    run_camcol_field.camcol,
    run_camcol_field.field,
)
output_path = joinpath(OUTPUT_DIRECTORY, output_filename)
AccuracyBenchmark.write_catalog(output_path, catalog_df)
AccuracyBenchmark.append_hash_to_file(output_path)


text:
module HamiltonianTest

using Elsa
using Test
using Elsa: Hamiltonian

@testset "basic hamiltonians" begin
    presets = (LatticePresets.linear, LatticePresets.square, LatticePresets.triangular,
               LatticePresets.honeycomb, LatticePresets.cubic, LatticePresets.fcc,
               LatticePresets.bcc)
    ts = (1, 2.0, @SMatrix[1 2; 3 4])
    for preset in presets, lat in (preset(), unitcell(preset()))
        E, L = dims(lat)
        dn0 = ntuple(_ -> 1, Val(L))
        for t in ts
            @test hamiltonian(lat, onsite(t) + hopping(t; range = 1)) isa Hamiltonian
            @test hamiltonian(lat, onsite(t) - hopping(t; dn = dn0)) isa Hamiltonian
            @test hamiltonian(lat, onsite(t) + hopping(t; dn = dn0, forcehermitian = false)) isa Hamiltonian
        end
    end
end

@testset "orbitals and sublats" begin
    orbs = (:a, (:a,), (:a, :b, 3), ((:a, :b), :c), ((:a, :b), (:c,)), (Val(2), Val(1)),
            (:A => (:a, :b), :D => :c), :D => Val(4))
    lat = LatticePresets.honeycomb()
    for orb in orbs
        @test hamiltonian(lat, onsite(1), orbitals = orb) isa Hamiltonian
    end
    @test hamiltonian(lat, onsite(1) + hopping(@SMatrix[1 2], sublats = (:A,:B)),
                      orbitals = :B => Val(2)) isa Hamiltonian
    h1 = hamiltonian(lat, onsite(1) + hopping(@SMatrix[1 2], sublats = (:A,:B)),
                      orbitals = :B => Val(2))
    h2 = hamiltonian(lat, onsite(1) + hopping(@SMatrix[1 2], sublats = ((:A,:B),)),
                      orbitals = :B => Val(2))
    @test bloch(h1, 1, 2) == bloch(h2, 1, 2)
end

end # module


text:
# iterating over on-the-fly generated random values

## Rand

struct Rand{R<:AbstractRNG,S<:Sampler}
    rng::R
    sp::S
end

# X can be an explicit Distribution, or an implicit one like 1:10
Rand(rng::AbstractRNG, X) = Rand(rng, Sampler(rng, X))
Rand(rng::AbstractRNG, ::Type{X}=Float64) where {X} = Rand(rng, Sampler(rng, X))

Rand(X) = Rand(GLOBAL_RNG, X)
Rand(::Type{X}=Float64) where {X} = Rand(GLOBAL_RNG, X)

(R::Rand)(args...) = rand(R.rng, R.sp, args...)

Base.start(R::Rand) = R

function Base.next(::Union{Rand,Distribution}, R::Rand)
    e = R()
    e, R
end

Base.done(::Union{Rand,Distribution}, ::Rand) = false

Base.IteratorSize(::Type{<:Rand}) = Base.IsInfinite()

Base.IteratorEltype(::Type{<:Rand}) = Base.HasEltype()
Base.eltype(::Type{<:Rand{R, <:Sampler{T}}}) where {R,T} = T

# convenience iteration over distributions

Base.start(d::Distribution) = Rand(GLOBAL_RNG, d)
Base.IteratorSize(::Type{<:Distribution}) = Base.IsInfinite()


text:
using Documenter, CleanDung

makedocs(
    modules = [CleanDung],
    format = :html,
    sitename = "CleanDung.jl",
    pages = Any["index.md"]
)

deploydocs(
    repo = "github.com/yakir12/CleanDung.jl.git",
    target = "build",
    deps = nothing,
    make = nothing,
)


text:
"""
Save figures.
"""
function save_figure(plot_ref, results_path, plot_title; extension=".png" )
    full_path = results_path*plot_title*extension
    savefig(plot_ref, full_path)
    @info "File saved under: " full_path
end

"""
Save betti curves.
"""
function save_betti(plot_ref, results_path, plot_title)
    full_title =  "betti_curves_"*plot_title;
    save_figure(plot_ref, results_path, full_title)
end

"""
Save figures with set of parameters given as 'kwargs'.
"""
function save_figure_with_params(plot_reference, results_path; extension=".png", prefix="", kwargs... )
    plot_title = ""
    kwargs_kyes = keys(kwargs)
    kwargs_vals = values(kwargs)

    total_params = size(collect(kwargs_vals),1)

    for param = 1:total_params
        plot_title *= "$(kwargs_kyes[param])_$(kwargs_vals[param])_"
    end

    full_path = results_path*prefix*plot_title*extension

    # savefig(plot_ref, full_path)
    @info "File saved as: " full_path
end


text:
"""
postprocessmageq(mag_eq_inds::Vector{Vector{Int}}, dict_H_inds_to_css;
    atol = 1e-6)

`mag_eq_inds` contains the magnetic equivalent nuclei indices for a spin system.
This function combine the groups of equivalent nuclei if they have the same chemical shift.

mag_eq_inds[k][l] is the k-th equivalent nuclei, l-th nuclei index.
if mag_eq_inds is not empty, then the array mag_eq_inds[k] must not be empty, for all k.
"""
function postprocessmageq(mag_eq_inds::Vector{Vector{Int}},
    dict_H_inds_to_css;
    atol = 1e-6)

    if isempty(mag_eq_inds)
        return Vector{Vector{Int}}(undef, 0)
    end

    cs = collect( dict_H_inds_to_css[mag_eq_inds[k][1]] for k = 1:length(mag_eq_inds) )

    inds = mag_eq_inds

    # check if all cs are the same.
    if abs(sum(cs) / length(cs) - cs[1]) < atol

        inds, cs_unique = NMRSpectraSimulator.removeredundantsinglets(mag_eq_inds, cs)

        inds = collect( unique(inds[i]) for i = 1:length(inds) )
    end

    return inds
end


text:
#Demo 3: Plotting multi-dimensional datasets
#-------------------------------------------------------------------------------

using CMDimData
using CMDimData.MDDatasets
using CMDimData.EasyPlot

#==Attributes
===============================================================================#
vvst = cons(:a, labels = set(xaxis="Time (s)", yaxis="Amplitude (V)"))
lcolor1 = cons(:a, line = set(color=2)) #Example: ordinal color selection

#==Input data
===============================================================================#
tfund = 1e-9 #Fundamental
osr = 20 #(fundamental) oversampling ratio
nfund = 20 #cycles of the fundamental

#==Computations
===============================================================================#
t = DataF1(0:(tfund/osr):(nfund*tfund))
tmax = maximum(t)

#Generate parameter sweeps:
sweeplist = PSweep[
	PSweep("period", [1,2,3]*tfund)
	PSweep("slope", [-1,0,1]*(1/tmax))
	PSweep("offset", [-.4, 0, .9])
]

#Generate data:
lines = DataHR{DataF1}(sweeplist) #Create empty dataset
tones = DataHR{DataF1}(sweeplist) #Create empty dataset
for inds in subscripts(lines)
	(T, m, b) = coordinates(lines, inds)
	lines.elem[inds...] = DataF1(t.x, (x)->m*x+b)
	tones.elem[inds...] = sin(t*(2pi/T))
end

#==Generate plot
===============================================================================#
_legend = false #Too busy with GracePlot
plot1 = push!(cons(:plot, vvst, title="Lines", legend=_legend),
	cons(:wfrm, lines),
)
#Plot reduced dataset:
plot2 = push!(cons(:plot, vvst, title="maximum(Lines)", legend=_legend),
	cons(:wfrm, maximum(lines), line=set(style=:solid), glyph=set(shape=:o)),
)
plot3 = push!(cons(:plot, vvst, title="Tones", legend=_legend),
	cons(:wfrm, tones),
)
plot4 = push!(cons(:plot, vvst, title="Sum", legend=_legend),
	cons(:wfrm, lines+tones),
)

pcoll = cons(:plot_collection, title="Multi-Dataset Tests")
	push!(pcoll, plot1, plot2, plot3, plot4)

#==Return pcoll to user (call evalfile(...))
===============================================================================#
pcoll


text:
"""
Modify an .srt (subrip) subtitle file.

    using SubtitleStretcher
    cd("/Movies/")
    st = loadsubtitles("Pirates of the Caribbean The Curse of the Black Pearl (2003).eng.srt")
    stretch!(st, ["00:01:18" => "00:01:24", "2:03:08" => 7400])
"""
module SubtitleStretcher

using IterTools

import Base: convert

export loadsubtitles, savesubtitles, stretch!, Timestamp

# hold start and finish times for each subtitle
mutable struct Timestamp
    starttime::Float64  # seconds
    finishtime::Float64 #
end

# convert a timestring ("00:18:29,040") into Float64 seconds
function convert(::Type{Float64}, timestring::S where S <: AbstractString)
    if occursin(",", timestring)
        starttime, startmilli = split(timestring, ",")
    else
        starttime, startmilli = timestring, "0"
    end
    h1, m1, s1 = map(Meta.parse, split(starttime, ":"))
    return ((h1 * 60) + m1) * 60 + s1 + Meta.parse(startmilli)/1000
end

# convert two timestrings to a Timestamp
# eg "00:18:29,040 --> 00:18:30,201" -> Timestamp(1109.04, 1110.201)
function convert(::Type{Timestamp}, tstamp::S where S <: AbstractString)
    rawstarttime, rawfinishtime = split(tstamp, " --> ")
    return Timestamp(
        convert(Float64, rawstarttime),
        convert(Float64, rawfinishtime))
end

# forgot why I needed this
#convert(::Type{Timestamp}, t::Timestamp) = (t.starttime, t.finishtime)

"""
    timestamptostring(timecode)

Take part of a Timestamp and format it to the required string.

Example:

    timestamptostring(1528.32) -> "02:13:28,320"
"""
function timestamptostring(timecode)
    _min, _sec = divrem(timecode, 60)
    hr, mn    = divrem(_min, 60)
    sc, milli   = fldmod(_sec, 1)
    h = lpad(Int(floor(hr)), 2, "0")
    m = lpad(Int(floor(mn)), 2, "0")
    s = lpad(Int(floor(sc)), 2, "0")
    mill = lpad(convert(Int, round(milli, digits=3) * 1000), 3, "0")
    return "$h:$m:$s,$mill"
end

# convert both parts of a Timestamp to the full timestring
# Eg Timestamp(1528.32, 1530.163) -> "02:13:28,320 --> 02:13:30,163"
function convert(::Type{String}, tstamp::Timestamp)
    return timestamptostring(tstamp.starttime) * " --> " * timestamptostring(tstamp.finishtime)
end

"""
    loadsubtitles(filename)

Read subtitles from a file and load them into a dictionary. Files can be `.srt`
or `.sub` format.
"""
function loadsubtitles(filename)
    fn, ext = splitext(filename)
    file = open(filename) do f
        map(chomp, readlines(f)) # get rid of \r\n
    end
    subd = Dict{Int64, Tuple}()
    if ext == ".srt"
        #redo the numbers, this will correct any errors in the original
        global subcounter = 1
        # each group of lines is a subtitle
        for s in groupby(x -> x == "", file) # look for the blank line
            try # some problems with non-Unicode chars?
                if s[1] != ""
                    ts = convert(Timestamp, s[2])
                    l = length(s)
                    # more than one text line is possible
                    subd[subcounter] = (ts, [s[n] for n in 3:l])
                    subcounter += 1
                end
            catch
               println("skipping content at subtitle #$subcounter")
            end
        end
    elseif ext == ".sub"
        # each line is a subtitle
        for n in 1:length(file)
            l = file[n]
            g1, g2, g3, g4 = ["", "", "", ""]
            try # some problems with non-Unicode chars?
                # look for 3
                rm = match(r"\{(.*?)\}\{(.*?)\}(\{.*?\})(.*$)?", l)
                if rm == nothing
                    # look for 2 instead
                    rm = match(r"\{(.*?)\}\{(.*?)\}(.*$)?", l)
                    g1, g2, g4     = rm.captures
                else
                    g1, g2, g3, g4 = rm.captures
                end
            catch
                println("skipping content at line #$n")
            end
            fromtime = Meta.parse(g1)/25
            totime   = Meta.parse(g2)/25
            textlines = split(g4, "|")
            subd[n] = (Timestamp(fromtime, totime), [textlines...])
        end
    end
    return subd
end

# change value depending on one scale, to another
# eg rescale(0.5, 0, 1, 100, 200) changes the value 0.5
# (midway between 0 and 1) to 150, midway between 100 and 200
rescale(x, from_min, from_max, to_min, to_max) =
    ((x - from_min) / (from_max - from_min)) * (to_max - to_min) + to_min
rescale(x, from::NTuple{2,Number}, to::NTuple{2, Number}) =
    ((x - from[1]) / (from[2] - from[1])) * (to[2] - to[1]) + to[1]

"""
    stretch!(subd, markers)

Stretch all timestamps in a subtitle dictionary `subd`.

The `markers` argument is an array of 2 pairs of numbers:

    [oldsubtitletime1 => actualmovietime1, oldsubtitletime2 => actualmovietime2]

Example

    stretch!(subd, [1 => 1.0,  200.0 => 201.0]))
"""
function stretch!(subd, markers::Vector{Pair{T1, T2}} where T1 <: Real where T2 <: Real)
    println("...adjusting subtitle times")
    length(markers) < 2 && error("not enough markers to stretch the timecodes!")
    currentstart, requiredstart = markers[1]
    currentfinish, requiredfinish = markers[2]
    for key in sort(collect(keys(subd)))
           ts = subd[key][1]
           ttxt = subd[key][2]
           newstart = rescale(ts.starttime, currentstart, currentfinish, requiredstart, requiredfinish)
           newfinish = rescale(ts.finishtime, currentstart, currentfinish, requiredstart, requiredfinish)
           newts = Timestamp(newstart, newfinish)
           subd[key] = (newts, ttxt)
    end
    println("...subtitle times adjusted")
    return (requiredfinish - requiredstart)/(currentfinish - currentstart)
end

"""
    stretch!(subd, markers::Vector{Pair{T, T}} where T <: String)

Stretch all timestamps in a subtitle dictionary, using timestrings:

    stretch!(subd, ["00:06:44,580" =>"00:06:48",  "01:48:35,820" => "01:48:37"]

Timestrings are H:M:S:

    "00:18:29,040"
    "00:18:29"
"""
function stretch!(subd, markers::Vector{Pair{T, T}} where T <: String)
    length(markers) < 2 && error("not enough markers to stretch the timecodes!")
    cstart,  rstart = map(s -> convert(Float64, s), markers[1])
    cfinish, rfinish = map(s -> convert(Float64, s), markers[2])
    stretch!(subd, [cstart => rstart, cfinish => rfinish])
end

"""
    stretch!(subd, markers::Vector)

Stretch using mixture of numeric timestamps and strings.

    stretch!(st, [100 => "00:01:18", "2:03:08" => 7400])
"""
function stretch!(subd, markers::Vector)
    length(markers) !== 2 && error("not enough markers to stretch the timecodes!")
    r = []
    for a in [markers[1][1], markers[1][2], markers[2][1], markers[2][1]]
        if typeof(a) <: Real
            push!(r, a)
        else
            push!(r, convert(Float64, a))
        end
    end
    cstart, rstart, cfinish, rfinish = r
    stretch!(subd, [cstart => rstart, cfinish => rfinish])
end

"""
    savesubtitles(subd, filename)

Save a dictionary of subtitles to a file.
"""
function savesubtitles(subd, filename)
    open(filename, "w") do f
        for key in sort(collect(keys(subd)))
           println(f, key)
           println(f, convert(String, subd[key][1]))
           for i in subd[key][2]
               println(f, i)
           end
           println(f)
       end
    end
end

end # module


text:
# Uses the same optimiser as the base instance. Only works if that solver
# understands SOCP.
mutable struct OSSBExact <: OSSBOptimisationAlgorithm
  silent_solver::Bool
end

OSSBExact() = OSSBExact(true)

function optimise_graves_lai(instance::CombinatorialInstance{T},
                             algo::OSSBExact,
                             θ::Dict{T, Float64}; with_trace::Bool=true) where T
  # Must be able to get a LP formulation.
  if ! has_lp_formulation(instance)
    # TODO: with the inexact algorithm, the instance's algorithm will no more be forced to be LP-based! But still, the convex decomposition step requires an LP formulation.
    error("The exact formulation for OSSB relies on a LP formulation, " *
          "which the solver associated to this instance cannot provide.")
  end

  # This function deals with copying the instance as many times as necessary.
  # Called functions should not deal with these details.
  # TODO: rework the interface so that get_lp_formulation always returns a *new* formulation? Currently, these functions always return the same model...
  t0 = time_ns()

  # Precompute useful results.
  d = dimension(instance)
  x_max = solve_linear(copy(instance), θ)
  r_max = sum(θ[arm] for arm in x_max)

  arm_to_idx = Dict(arm => idx for (idx, arm) in enumerate(all_arm_indices(instance)))
  idx_to_arm = Dict(idx => arm for (idx, arm) in enumerate(all_arm_indices(instance)))

    I = _ossb_confusing_arms(instance, θ)
    # I = all_arm_indices(instance)

  # TODO: make it an external function that can be overridden by combinatorial instances.
  # TODO: probably wrong when order has some importance, like in paths.
  # TODO: copied from OSSBExactNaive.
  # Generate a list of solutions. Probably highly inefficient, but it should work for any kind of combinatorial
  # instance, as long as is_feasible and is_partially_acceptable are correctly implemented.
  # Technique: for each item, consider that it may be a part of a solution or not. Hence, build all combinations,
  # stop one branch of computations as soon as a set of arms can no longer lead to a solution (something is already
  # violating a constraint).
  # Initially: either take the first item or not. Maybe the first item cannot lead to a feasible solution.
  potential_solutions = Vector{T}[T[], T[first(all_arm_indices(instance))]]
  filter!(p -> is_partially_acceptable(instance, p), potential_solutions)

  solutions = Vector{T}[]
  for i in all_arm_indices(instance)[2:end]
    # Rebuild the set of potential solutions in this loop: for each existing element, try adding i or keeping the same thing.
    partial_solutions = copy(potential_solutions)
    deleteat!(potential_solutions, 1:length(potential_solutions)) # Empty the vector, so that it can be filled again.

    for p in partial_solutions
      # If this is already a feasible solution, put it aside.
      if length(p) > 0 && is_feasible(instance, p) && !(p in solutions)
        push!(solutions, p)
      end

      # The current potential solution is already known to be... a potential solution.
      push!(potential_solutions, p)

      # The other part of the recursion: try adding the new item i.
      p2 = [p..., i]
      if is_feasible(instance, p2)
        push!(solutions, p2)
      end
      if is_partially_acceptable(instance, p2)
        push!(potential_solutions, p2)
      end
    end
  end

  potential_solutions = nothing # Free some memory.

  # Reduced formulation:
  #   min   v (θ^T x^\star) - θ^T t
  #   s.t.  A t ≤ v b
  #         ∑ x_i / t_i ≤ Δ_x^2     ∀ x ∈ X
  # v could be removed if the polytope is described as A t = b, but that would require fiddling
  # with the formulation quite a lot if that is not the case.

  # TODO: copied from OSSBExactSmart.
  instance = copy(instance)
  m, _, vars = get_lp_formulation(instance, θ) # A t ≤ b
  @variable(m, v >= 0)
  # @variable(m, inv_t[arm in I] >= 0)
  @variable(m, inv_t[arm in keys(θ)] >= 0) # TODO: param for this upper bound, if it is really needed.
  # @variable(m, 0 <= inv_t[arm in keys(θ)] <= 10_000) # TODO: param for this upper bound, if it is really needed.

  # TODO: copied from OSSBExactSmart.
  # From "A t ≤ b" to "A t - v b ≤ 0".
  # Multiply the right-hand side by v, for all constraints.
  for (F, S) in JuMP.list_of_constraint_types(m)
    # Remove integrality constraints. (In this case, F is a single variable.)
    # This could be replaced by relax_integrality() of newer JuMP versions, but it would be slower
    # (relax_integrality is reversible, which is not needed here).
    if S in [MOI.ZeroOne, S == MOI.Integer]
      for c in all_constraints(m, F, S)
        JuMP.delete(m, c)
      end
    end
    # Ignore the case where this is a simple bounds.
    if F == JuMP.VariableRef || F == MOI.SingleVariable
      continue
    end

    # If there is a RHS (JuMP normalises everything so that the RHS has only
    # constants), multiply by v. Due to normalisation, this term goes to the
    # LHS, and must see its sign reversed.
    for c in all_constraints(m, F, S)
      if ! iszero(normalized_rhs(c))
        coeff = normalized_rhs(c)
        set_normalized_rhs(c, 0.0)
        set_normalized_coefficient(c, v, -coeff)
      end
    end
  end

  try
    set_optimizer_attribute(m, "NonConvex", 2)
  catch e
    set_optimizer_attribute(m, "CPX_OPTIMALITYTARGET_OPTIMALGLOBAL", 3)
  end

  # TODO: copied from OSSBExactSmart.
  # Define the inv_t variable (only used for the added constraints).
  for arm in keys(θ) # I
    # Encode the constraint inv_t[i] = 1 / t[i] = 1 / vars[i]. Relax it:
    #     t[i] ≥ 1 / inv_t[i]
    # This corresponds to a rotated second-order cone:
    #     (t[i], inv_t[i], √2) ∈ RSOC(3)
    # By definition, an RSOC is:
    #     2 × t[i] × inv_t[i] ≥ (√2)² = 2.
    #     t[i] × inv_t[i] ≥ 1
    # @constraint(m, [inv_t[arm] + vars[arm], inv_t[arm] - vars[arm], 2] in SecondOrderCone())
    # @constraint(m, [vars[arm], inv_t[arm], sqrt(2)] in RotatedSecondOrderCone())
    @constraint(m, vars[arm] * inv_t[arm] == 1) # NON CONVEX THING!
  end

  # TODO: copied from OSSBExactSmart.
  # Set the objective function.
  obj_best = v * r_max
  obj_actual = sum(θ[arm] * vars[arm] for arm in keys(θ))
  @objective(m, Min, obj_best - obj_actual)
  @constraint(m, obj_best - obj_actual >= 0) # Avoid numerical problems with some polytopes.

  # TODO: copied from OSSBExactSmart.
  if algo.silent_solver
    set_silent(m)
  else
    unset_silent(m)
  end

  # Generate all gap constraints, even if that's a lof of things to feed the solver with.
  for x in solutions
    Δx² = (r_max - sum(θ[arm] for arm in x))^2
    @show Δx²

    if Δx² >= 1.0e-5
      c = @constraint(m, sum(inv_t[arm] for arm in x) <= Δx²)
      println(c)
    end
  end

  # Solve the (very large) formulation.
  optimize!(m)

  if termination_status(m) != MOI.OPTIMAL
    @show termination_status(m)
    println(m)
    error(termination_status(m))
  end

  @show value(v)
  for arm in keys(θ)
    @show value(vars[arm])
    @show value(inv_t[arm])
  end

  # TODO: copied from OSSBExactSmart.
  # Retrieve the solution and normalise with respect to v.
  # value. doesn't broadcast on dictionaries.
  v_val = value(v)
  t_val = Dict{T, Float64}(arm => value(vars[arm]) / v_val for arm in keys(θ))

  # TODO: copied from OSSBExactSmart.
  # If the solution t_val is zero, there is not much to do. This should never happen, unless there is a serious
  # bug in the rest of the function.
  if iszero(collect(values(t_val)))
    println("Uh ho...")
    return solve_linear(instance, θ) # Perform pure exploitation.
  end

  # TODO: copied from OSSBExactSmart.
  # Return the solution that has been found.
  res = GravesLaiResults(instance, t_val, objective_value(m))
  if with_trace
    return res, GravesLaiDetails((time_ns() - t0) / 1_000_000, objective_value(m))
  else
    return res
  end
end


text:
using SpecialFunctions, LinearAlgebra

struct CCSim
    n_total
    n_open
    n_closed
    kz2
    g
    gi
end

function Base.show(result::CCSim)
    print("total channels: ",result.n_total)
    print(" (open: ",result.n_open)
    print(", closed: ",result.n_closed,")")
    print(", kz2: ",result.kz2,")")
end

function h2m(m)
    hb = 1.054571800e-34
    hb^2/(2*m)
end

function fox_goodwin_step!(w_p1,w_0,w_m1,r)
        aa = 2*I+10*w_0
        bb = (I-w_m1)*r
        cc = I-w_p1
        r .= (aa.-bb)\cc
        w_m1 .= w_0
        w_0 .= w_p1
        r
end

function fox_goodwin(z,w!,n)
    h = z[2]-z[1]
    c1 = h^2/12

    w_m1 = Array{typeof(z[1]),2}(undef,n,n)
    w_0 = similar(w_m1)
    w_p1 = similar(w_m1)
    r  = fill!(similar(w_0),0)

    w!(w_m1,z[1])
    lmul!(c1,w_m1)
    w!(w_0,z[2])
    lmul!(c1,w_0)

    for i in 3:length(z)
        w!(w_p1,z[i])
        lmul!(c1,w_p1)
        fox_goodwin_step!(w_p1,w_0,w_m1,r)
    end
    r
end

function coupling(a,h,xi,g,divide = 1 ;kmax = 10)
    α = 2*xi*h*a/3
    VG = 0.0
    for k in -kmax:kmax
        VG += besseli(k,α)*(besseli(k+g[2],α)*besseli(k-g[1],α) + besseli(k-g[2],α)*besseli(k+g[1],α))
    end
    VG / divide
end

function get_w_fun(channels,a,h,D,xi,m)
	n = length(channels.kz2)
	V0(z) = D*(exp(-2*xi*z) - 2*exp(-xi*z))
	V1(z) = D*exp(-2*xi*z)
        M_E = Array{Float64,2}(undef,n,n)
	c0 = coupling(a,h,xi,(0,0))
	for i in 1:size(M_E,1), j in 1:size(M_E,2)
	    g1,g2 = channels.gi[i].-channels.gi[j]
	    M_E[j,i] = coupling(a,h,xi,(g1,g2),c0)
	end
	for i in 1:size(M_E,1)
	    M_E[i,i] = 0
	end
        function w!(w,z)
            v0 = V0(z)/h2m(m)
            v1 = V1(z)/h2m(m)
            for i in 1:size(w,1), j in 1:size(w,2)
                w[i,j] = v1 * M_E[i,j]
            end
            for i in 1:size(w,1)
                w[i,i] = v0 - channels.kz2[i]
            end
        end
end

function cc(channels::ScatteringChannels,a,h,D,xi, theta,m,z;st_p_wl=100)
    #find lowest open channel - split open close channels
    last_open = findlast(x -> x > 0, channels.kz2)

    open_index = 1:last_open
    close_index = last_open+1:length(channels.kz2)

    open_kz2 = @view channels.kz2[open_index]
    open_gi = @view channels.gi[open_index,:]

    # calculate z steps
    step = 2*π / (st_p_wl*sqrt(maximum(channels.kz2)))
	zrange = collect(z[1]:step:z[2])

	# get W matrix function
    w = get_w_fun(channels,a,h,D,xi,m)

	# solve trasition matrix r
    r = fox_goodwin(zrange,w,length(channels.kz2))

    S0_m1 = zeros(size(r,1))
    S0_00 = zeros(size(r,1))
    CE_m1 = zeros(size(r,1))
    CE_00 = zeros(size(r,1))

    for (i,erg) in enumerate(channels.kz2)
        if erg > 0 # open
            kz = sqrt(erg)
            S0_m1[i] = sin(kz*zrange[end-1])/sqrt(kz)
            S0_00[i] = sin(kz*zrange[end])/sqrt(kz)
            CE_m1[i] = cos(kz*zrange[end-1])/sqrt(kz)
            CE_00[i] = cos(kz*zrange[end])/sqrt(kz)
        else # closed
            kz = sqrt(abs(erg))
            CE_m1[i] = exp(-kz*zrange[end-1])/sqrt(kz)
            CE_00[i] = exp(-kz*zrange[end])/sqrt(kz)
        end
    end

    S0_m1 = Diagonal(S0_m1)
    S0_00 = Diagonal(S0_00)
    CE_m1 = Diagonal(CE_m1)
    CE_00 = Diagonal(CE_00)

    SS = S0_m1 .- r*S0_00
    CC = r*CE_00 .- CE_m1

    KK = CC\SS

    K_open = KK[open_index,open_index]
    S = broadcast(abs2,(I+K_open*(0+1im))\(I-K_open*(0+1im)))

    spec = findfirst(x -> x==[0,0], channels.g[open_index])

    result = hcat(collect(S[spec,:]),open_gi,open_kz2)
end


text:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Description
# ==============================================================================
#
#   Functions to perform searches in strings.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

export string_search, string_search_per_line

"""
    string_search(str::AbstractString, r::Regex)

Search for the pattern in regex `r` in the string `str`. The result will be a
vector of `Tuple{Int, Int}` with the beginning of the match and its length,
where both informations are related to the text width of printable characters.
"""
function string_search(str::AbstractString, r::Regex)

    # Remove the decorations so that we can search by regex.
    undecorated_str = remove_decorations(str)

    # Vector with the match tuples.
    search_result = Tuple{Int, Int}[]

    # Find all matches.
    for m in eachmatch(r, undecorated_str)
        # `m.offset` contains the byte in which the match starts. However,
        # we need to obtain the character. Hence, it is necessary to compute
        # the text width from the beginning to the offset.
        push!(search_result, (
            textwidth(undecorated_str[1:m.offset]),
            textwidth(m.match)
        ))
    end

    return search_result
end

"""
    string_search_per_line(str::AbstractString, r::Regex)

Search for the pattern in regex `r` in each line of the string `str`, which can
also be passed as a vector of string `lines`. The result will be a vector of
`NTuple{3, Int}` with the line, beginning of the match in this line, and its
length, where the two last information are related to the text width of
printable characters.
"""
function string_search_per_line(str::AbstractString, r::Regex)
    tokens = split(str, '\n')
    return string_search_per_line(tokens, r)
end

function string_search_per_line(
    lines::AbstractVector{T},
    r::Regex
) where T<:AbstractString

    search_results = Dict{Int, Vector{Tuple{Int, Int}}}()

    # Find the matches in each line.
    for l in 1:length(lines)
        search_results_l = string_search(lines[l], r)

        if !isempty(search_results_l)
            search_results[l] = search_results_l
        end
    end

    return search_results
end