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

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

Adds one or more attachments to an attachment set.  An attachment set is a temporary
container for attachments that you add to a case or case communication. The set is
available for 1 hour after it's created. The expiryTime returned in the response is when
the set expires.     You must have a Business, Enterprise On-Ramp, or Enterprise Support
plan to use the Amazon Web Services Support API.    If you call the Amazon Web Services
Support API from an account that does not have a Business, Enterprise On-Ramp, or
Enterprise Support plan, the SubscriptionRequiredException error message appears. For
information about changing your support plan, see Amazon Web Services Support.

# Arguments
- `attachments`: One or more attachments to add to the set. You can add up to three
  attachments per set. The size limit is 5 MB per attachment. In the Attachment object, use
  the data parameter to specify the contents of the attachment file. In the previous request
  syntax, the value for data appear as blob, which is represented as a base64-encoded string.
  The value for fileName is the name of the attachment, such as troubleshoot-screenshot.png.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"attachmentSetId"`: The ID of the attachment set. If an attachmentSetId is not
  specified, a new attachment set is created, and the ID of the set is returned in the
  response. If an attachmentSetId is specified, the attachments are added to the specified
  set, if it exists.
"""
function add_attachments_to_set(
    attachments; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "AddAttachmentsToSet",
        Dict{String,Any}("attachments" => attachments);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function add_attachments_to_set(
    attachments,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "AddAttachmentsToSet",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("attachments" => attachments), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Adds additional customer communication to an Amazon Web Services Support case. Use the
caseId parameter to identify the case to which to add communication. You can list a set of
email addresses to copy on the communication by using the ccEmailAddresses parameter. The
communicationBody value contains the text of the communication.    You must have a
Business, Enterprise On-Ramp, or Enterprise Support plan to use the Amazon Web Services
Support API.    If you call the Amazon Web Services Support API from an account that does
not have a Business, Enterprise On-Ramp, or Enterprise Support plan, the
SubscriptionRequiredException error message appears. For information about changing your
support plan, see Amazon Web Services Support.

# Arguments
- `communication_body`: The body of an email communication to add to the support case.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"attachmentSetId"`: The ID of a set of one or more attachments for the communication to
  add to the case. Create the set by calling AddAttachmentsToSet
- `"caseId"`: The support case ID requested or returned in the call. The case ID is an
  alphanumeric string formatted as shown in this example:
  case-12345678910-2013-c4c1d2bf33c5cf47
- `"ccEmailAddresses"`: The email addresses in the CC line of an email to be added to the
  support case.
"""
function add_communication_to_case(
    communicationBody; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "AddCommunicationToCase",
        Dict{String,Any}("communicationBody" => communicationBody);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function add_communication_to_case(
    communicationBody,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "AddCommunicationToCase",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("communicationBody" => communicationBody), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_case(communication_body, subject)
    create_case(communication_body, subject, params::Dict{String,<:Any})

Creates a case in the Amazon Web Services Support Center. This operation is similar to how
you create a case in the Amazon Web Services Support Center Create Case page. The Amazon
Web Services Support API doesn't support requesting service limit increases. You can submit
a service limit increase in the following ways:    Submit a request from the Amazon Web
Services Support Center Create Case page.   Use the Service Quotas
RequestServiceQuotaIncrease operation.   A successful CreateCase request returns an Amazon
Web Services Support case number. You can use the DescribeCases operation and specify the
case number to get existing Amazon Web Services Support cases. After you create a case, use
the AddCommunicationToCase operation to add additional communication or attachments to an
existing case. The caseId is separate from the displayId that appears in the Amazon Web
Services Support Center. Use the DescribeCases operation to get the displayId.    You must
have a Business, Enterprise On-Ramp, or Enterprise Support plan to use the Amazon Web
Services Support API.    If you call the Amazon Web Services Support API from an account
that does not have a Business, Enterprise On-Ramp, or Enterprise Support plan, the
SubscriptionRequiredException error message appears. For information about changing your
support plan, see Amazon Web Services Support.

# Arguments
- `communication_body`: The communication body text that describes the issue. This text
  appears in the Description field on the Amazon Web Services Support Center Create Case page.
- `subject`: The title of the support case. The title appears in the Subject field on the
  Amazon Web Services Support Center Create Case page.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"attachmentSetId"`: The ID of a set of one or more attachments for the case. Create the
  set by using the AddAttachmentsToSet operation.
- `"categoryCode"`: The category of problem for the support case. You also use the
  DescribeServices operation to get the category code for a service. Each Amazon Web Services
  service defines its own set of category codes.
- `"ccEmailAddresses"`: A list of email addresses that Amazon Web Services Support copies
  on case correspondence. Amazon Web Services Support identifies the account that creates the
  case when you specify your Amazon Web Services credentials in an HTTP POST method or use
  the Amazon Web Services SDKs.
- `"issueType"`: The type of issue for the case. You can specify customer-service or
  technical. If you don't specify a value, the default is technical.
- `"language"`: The language in which Amazon Web Services Support handles the case. You
  must specify the ISO 639-1 code for the language parameter if you want support in that
  language. Currently, English (\"en\") and Japanese (\"ja\") are supported.
- `"serviceCode"`: The code for the Amazon Web Services service. You can use the
  DescribeServices operation to get the possible serviceCode values.
- `"severityCode"`: A value that indicates the urgency of the case. This value determines
  the response time according to your service level agreement with Amazon Web Services
  Support. You can use the DescribeSeverityLevels operation to get the possible values for
  severityCode.  For more information, see SeverityLevel and Choosing a Severity in the
  Amazon Web Services Support User Guide.  The availability of severity levels depends on the
  support plan for the Amazon Web Services account.
"""
function create_case(
    communicationBody, subject; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "CreateCase",
        Dict{String,Any}("communicationBody" => communicationBody, "subject" => subject);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_case(
    communicationBody,
    subject,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "CreateCase",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "communicationBody" => communicationBody, "subject" => subject
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the attachment that has the specified ID. Attachments can include screenshots,
error logs, or other files that describe your issue. Attachment IDs are generated by the
case management system when you add an attachment to a case or case communication.
Attachment IDs are returned in the AttachmentDetails objects that are returned by the
DescribeCommunications operation.    You must have a Business, Enterprise On-Ramp, or
Enterprise Support plan to use the Amazon Web Services Support API.    If you call the
Amazon Web Services Support API from an account that does not have a Business, Enterprise
On-Ramp, or Enterprise Support plan, the SubscriptionRequiredException error message
appears. For information about changing your support plan, see Amazon Web Services Support.

# Arguments
- `attachment_id`: The ID of the attachment to return. Attachment IDs are returned by the
  DescribeCommunications operation.

"""
function describe_attachment(
    attachmentId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeAttachment",
        Dict{String,Any}("attachmentId" => attachmentId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_attachment(
    attachmentId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "DescribeAttachment",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("attachmentId" => attachmentId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of cases that you specify by passing one or more case IDs. You can use the
afterTime and beforeTime parameters to filter the cases by date. You can set values for the
includeResolvedCases and includeCommunications parameters to specify how much information
to return. The response returns the following in JSON format:   One or more CaseDetails
data types.   One or more nextToken values, which specify where to paginate the returned
records represented by the CaseDetails objects.   Case data is available for 12 months
after creation. If a case was created more than 12 months ago, a request might return an
error.    You must have a Business, Enterprise On-Ramp, or Enterprise Support plan to use
the Amazon Web Services Support API.    If you call the Amazon Web Services Support API
from an account that does not have a Business, Enterprise On-Ramp, or Enterprise Support
plan, the SubscriptionRequiredException error message appears. For information about
changing your support plan, see Amazon Web Services Support.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"afterTime"`: The start date for a filtered date search on support case communications.
  Case communications are available for 12 months after creation.
- `"beforeTime"`: The end date for a filtered date search on support case communications.
  Case communications are available for 12 months after creation.
- `"caseIdList"`: A list of ID numbers of the support cases you want returned. The maximum
  number of cases is 100.
- `"displayId"`: The ID displayed for a case in the Amazon Web Services Support Center user
  interface.
- `"includeCommunications"`: Specifies whether to include communications in the
  DescribeCases response. By default, communications are included.
- `"includeResolvedCases"`: Specifies whether to include resolved support cases in the
  DescribeCases response. By default, resolved cases aren't included.
- `"language"`: The ISO 639-1 code for the language in which Amazon Web Services provides
  support. Amazon Web Services Support currently supports English (\"en\") and Japanese
  (\"ja\"). Language parameters must be passed explicitly for operations that take them.
- `"maxResults"`: The maximum number of results to return before paginating.
- `"nextToken"`: A resumption point for pagination.
"""
function describe_cases(; aws_config::AbstractAWSConfig=global_aws_config())
    return support("DescribeCases"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET)
end
function describe_cases(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeCases", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Returns communications and attachments for one or more support cases. Use the afterTime and
beforeTime parameters to filter by date. You can use the caseId parameter to restrict the
results to a specific case. Case data is available for 12 months after creation. If a case
was created more than 12 months ago, a request for data might cause an error. You can use
the maxResults and nextToken parameters to control the pagination of the results. Set
maxResults to the number of cases that you want to display on each page, and use nextToken
to specify the resumption of pagination.    You must have a Business, Enterprise On-Ramp,
or Enterprise Support plan to use the Amazon Web Services Support API.    If you call the
Amazon Web Services Support API from an account that does not have a Business, Enterprise
On-Ramp, or Enterprise Support plan, the SubscriptionRequiredException error message
appears. For information about changing your support plan, see Amazon Web Services Support.

# Arguments
- `case_id`: The support case ID requested or returned in the call. The case ID is an
  alphanumeric string formatted as shown in this example:
  case-12345678910-2013-c4c1d2bf33c5cf47

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"afterTime"`: The start date for a filtered date search on support case communications.
  Case communications are available for 12 months after creation.
- `"beforeTime"`: The end date for a filtered date search on support case communications.
  Case communications are available for 12 months after creation.
- `"maxResults"`: The maximum number of results to return before paginating.
- `"nextToken"`: A resumption point for pagination.
"""
function describe_communications(caseId; aws_config::AbstractAWSConfig=global_aws_config())
    return support(
        "DescribeCommunications",
        Dict{String,Any}("caseId" => caseId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_communications(
    caseId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeCommunications",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("caseId" => caseId), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the current list of Amazon Web Services services and a list of service categories
for each service. You then use service names and categories in your CreateCase requests.
Each Amazon Web Services service has its own set of categories. The service codes and
category codes correspond to the values that appear in the Service and Category lists on
the Amazon Web Services Support Center Create Case page. The values in those fields don't
necessarily match the service codes and categories returned by the DescribeServices
operation. Always use the service codes and categories that the DescribeServices operation
returns, so that you have the most recent set of service and category codes.    You must
have a Business, Enterprise On-Ramp, or Enterprise Support plan to use the Amazon Web
Services Support API.    If you call the Amazon Web Services Support API from an account
that does not have a Business, Enterprise On-Ramp, or Enterprise Support plan, the
SubscriptionRequiredException error message appears. For information about changing your
support plan, see Amazon Web Services Support.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"language"`: The ISO 639-1 code for the language in which Amazon Web Services provides
  support. Amazon Web Services Support currently supports English (\"en\") and Japanese
  (\"ja\"). Language parameters must be passed explicitly for operations that take them.
- `"serviceCodeList"`: A JSON-formatted list of service codes available for Amazon Web
  Services services.
"""
function describe_services(; aws_config::AbstractAWSConfig=global_aws_config())
    return support(
        "DescribeServices"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_services(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeServices", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Returns the list of severity levels that you can assign to a support case. The severity
level for a case is also a field in the CaseDetails data type that you include for a
CreateCase request.    You must have a Business, Enterprise On-Ramp, or Enterprise Support
plan to use the Amazon Web Services Support API.    If you call the Amazon Web Services
Support API from an account that does not have a Business, Enterprise On-Ramp, or
Enterprise Support plan, the SubscriptionRequiredException error message appears. For
information about changing your support plan, see Amazon Web Services Support.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"language"`: The ISO 639-1 code for the language in which Amazon Web Services provides
  support. Amazon Web Services Support currently supports English (\"en\") and Japanese
  (\"ja\"). Language parameters must be passed explicitly for operations that take them.
"""
function describe_severity_levels(; aws_config::AbstractAWSConfig=global_aws_config())
    return support(
        "DescribeSeverityLevels"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_severity_levels(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeSeverityLevels",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the refresh status of the Trusted Advisor checks that have the specified check IDs.
You can get the check IDs by calling the DescribeTrustedAdvisorChecks operation. Some
checks are refreshed automatically, and you can't return their refresh statuses by using
the DescribeTrustedAdvisorCheckRefreshStatuses operation. If you call this operation for
these checks, you might see an InvalidParameterValue error.    You must have a Business,
Enterprise On-Ramp, or Enterprise Support plan to use the Amazon Web Services Support API.
  If you call the Amazon Web Services Support API from an account that does not have a
Business, Enterprise On-Ramp, or Enterprise Support plan, the SubscriptionRequiredException
error message appears. For information about changing your support plan, see Amazon Web
Services Support.

# Arguments
- `check_ids`: The IDs of the Trusted Advisor checks to get the status.  If you specify the
  check ID of a check that is automatically refreshed, you might see an InvalidParameterValue
  error.

"""
function describe_trusted_advisor_check_refresh_statuses(
    checkIds; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeTrustedAdvisorCheckRefreshStatuses",
        Dict{String,Any}("checkIds" => checkIds);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_trusted_advisor_check_refresh_statuses(
    checkIds,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "DescribeTrustedAdvisorCheckRefreshStatuses",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("checkIds" => checkIds), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the results of the Trusted Advisor check that has the specified check ID. You can
get the check IDs by calling the DescribeTrustedAdvisorChecks operation. The response
contains a TrustedAdvisorCheckResult object, which contains these three objects:
TrustedAdvisorCategorySpecificSummary     TrustedAdvisorResourceDetail
TrustedAdvisorResourcesSummary    In addition, the response contains these fields:
status - The alert status of the check can be ok (green), warning (yellow), error (red), or
not_available.    timestamp - The time of the last refresh of the check.    checkId - The
unique identifier for the check.      You must have a Business, Enterprise On-Ramp, or
Enterprise Support plan to use the Amazon Web Services Support API.    If you call the
Amazon Web Services Support API from an account that does not have a Business, Enterprise
On-Ramp, or Enterprise Support plan, the SubscriptionRequiredException error message
appears. For information about changing your support plan, see Amazon Web Services Support.

# Arguments
- `check_id`: The unique identifier for the Trusted Advisor check.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"language"`: The ISO 639-1 code for the language in which Amazon Web Services provides
  support. Amazon Web Services Support currently supports English (\"en\") and Japanese
  (\"ja\"). Language parameters must be passed explicitly for operations that take them.
"""
function describe_trusted_advisor_check_result(
    checkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeTrustedAdvisorCheckResult",
        Dict{String,Any}("checkId" => checkId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_trusted_advisor_check_result(
    checkId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeTrustedAdvisorCheckResult",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("checkId" => checkId), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the results for the Trusted Advisor check summaries for the check IDs that you
specified. You can get the check IDs by calling the DescribeTrustedAdvisorChecks operation.
The response contains an array of TrustedAdvisorCheckSummary objects.    You must have a
Business, Enterprise On-Ramp, or Enterprise Support plan to use the Amazon Web Services
Support API.    If you call the Amazon Web Services Support API from an account that does
not have a Business, Enterprise On-Ramp, or Enterprise Support plan, the
SubscriptionRequiredException error message appears. For information about changing your
support plan, see Amazon Web Services Support.

# Arguments
- `check_ids`: The IDs of the Trusted Advisor checks.

"""
function describe_trusted_advisor_check_summaries(
    checkIds; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeTrustedAdvisorCheckSummaries",
        Dict{String,Any}("checkIds" => checkIds);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_trusted_advisor_check_summaries(
    checkIds,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "DescribeTrustedAdvisorCheckSummaries",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("checkIds" => checkIds), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns information about all available Trusted Advisor checks, including the name, ID,
category, description, and metadata. You must specify a language code. The Amazon Web
Services Support API currently supports English (\"en\") and Japanese (\"ja\"). The
response contains a TrustedAdvisorCheckDescription object for each check. You must set the
Amazon Web Services Region to us-east-1.    You must have a Business, Enterprise On-Ramp,
or Enterprise Support plan to use the Amazon Web Services Support API.    If you call the
Amazon Web Services Support API from an account that does not have a Business, Enterprise
On-Ramp, or Enterprise Support plan, the SubscriptionRequiredException error message
appears. For information about changing your support plan, see Amazon Web Services Support.
  The names and descriptions for Trusted Advisor checks are subject to change. We recommend
that you specify the check ID in your code to uniquely identify a check.

# Arguments
- `language`: The ISO 639-1 code for the language in which Amazon Web Services provides
  support. Amazon Web Services Support currently supports English (\"en\") and Japanese
  (\"ja\"). Language parameters must be passed explicitly for operations that take them.

"""
function describe_trusted_advisor_checks(
    language; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "DescribeTrustedAdvisorChecks",
        Dict{String,Any}("language" => language);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_trusted_advisor_checks(
    language,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return support(
        "DescribeTrustedAdvisorChecks",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("language" => language), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Refreshes the Trusted Advisor check that you specify using the check ID. You can get the
check IDs by calling the DescribeTrustedAdvisorChecks operation.  Some checks are refreshed
automatically. If you call the RefreshTrustedAdvisorCheck operation to refresh them, you
might see the InvalidParameterValue error.  The response contains a
TrustedAdvisorCheckRefreshStatus object.    You must have a Business, Enterprise On-Ramp,
or Enterprise Support plan to use the Amazon Web Services Support API.    If you call the
Amazon Web Services Support API from an account that does not have a Business, Enterprise
On-Ramp, or Enterprise Support plan, the SubscriptionRequiredException error message
appears. For information about changing your support plan, see Amazon Web Services Support.

# Arguments
- `check_id`: The unique identifier for the Trusted Advisor check to refresh.  Specifying
  the check ID of a check that is automatically refreshed causes an InvalidParameterValue
  error.

"""
function refresh_trusted_advisor_check(
    checkId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "RefreshTrustedAdvisorCheck",
        Dict{String,Any}("checkId" => checkId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function refresh_trusted_advisor_check(
    checkId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "RefreshTrustedAdvisorCheck",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("checkId" => checkId), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Resolves a support case. This operation takes a caseId and returns the initial and final
state of the case.    You must have a Business, Enterprise On-Ramp, or Enterprise Support
plan to use the Amazon Web Services Support API.    If you call the Amazon Web Services
Support API from an account that does not have a Business, Enterprise On-Ramp, or
Enterprise Support plan, the SubscriptionRequiredException error message appears. For
information about changing your support plan, see Amazon Web Services Support.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"caseId"`: The support case ID requested or returned in the call. The case ID is an
  alphanumeric string formatted as shown in this example:
  case-12345678910-2013-c4c1d2bf33c5cf47
"""
function resolve_case(; aws_config::AbstractAWSConfig=global_aws_config())
    return support("ResolveCase"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET)
end
function resolve_case(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return support(
        "ResolveCase", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end


text:
# LoadBalance top module

module LoadBalancers

export parallel_lb, WorkUnit

include("utils.jl")
include("worker_utils.jl")
include("random_poll.jl")
include("sched_based.jl")
include("async_rrobin.jl")

end

ext: jl

max_stars_repo_name: dillondaudert/jlance

max_issues_repo_name: dillondaudert/jlance

max_forks_repo_name: dillondaudert/jlance


text:
# Modified from the file automatically generated by ProtoBuf.jl
# See https://github.com/pszufe/OpenStreetMapX.jl/pull/52/
module OSMPBF
  const _ProtoBuf_Top_ = @static isdefined(parentmodule(@__MODULE__), :_ProtoBuf_Top_) ? (parentmodule(@__MODULE__))._ProtoBuf_Top_ : parentmodule(@__MODULE__)

  using ProtoBuf
  abstract type SimpleProtoType <: ProtoType end
  set_defaults!(::SimpleProtoType) = nothing
  function ProtoBuf.clear(obj::SimpleProtoType)
      # FIXME how does it play with GC ?
      Base.unsafe_securezero!(pointer_from_objref(obj), sizeof(typeof(obj)))
      set_defaults!(obj)
      return
  end
  Base.hasproperty(obj::SimpleProtoType, field::Symbol) = isdefined(obj, field)
  Base.setproperty!(obj::SimpleProtoType, field::Symbol, value) = setfield!(obj, field, value)
  ProtoBuf.setdefaultproperties!(::SimpleProtoType, ::ProtoMeta) = nothing

  include("fileformat_pb.jl")
  include("osmformat_pb.jl")
end


text:
using Infiltrator
@testset verbose = true "Test RL Loop" begin

    MAX_STEPS=10
    N_STARTING_PLAYERS = 2
    UPDATE_FREQ = 6
    WORLD_SIZE = (12, 12)
    clip = 0.1f0

    env = NatureEnv(num_starting_players=N_STARTING_PLAYERS,
                    world_size=WORLD_SIZE,
                    episode_len=UPDATE_FREQ+2,
                    num_frames=4,
                    window=3,
                    food_generators=[
                        FoodGen([4,4],[30,30], max_pos=WORLD_SIZE[1:2]),
                       ])
    RLBase.reset!(env)
    ns, na = size(state(env, 1)), length(action_space(env,1))

    policy = build_MultiPPOManager(env, N_STARTING_PLAYERS, UPDATE_FREQ, clip)

    policy(PRE_EXPERIMENT_STAGE, env)
    policy(PRE_EPISODE_STAGE, env)
    acts = []
    states = []
    rews = []

    function step()
        s = [state(env, p) for p in 1:length(env.players)]
        s = cat(s..., dims=ndims(s[1])+1)

        action = policy(env)
        policy(PRE_ACT_STAGE, env, action)
        env(action)
        policy(POST_ACT_STAGE, env)

        a = [action[p].action for p in 1:length(env.players)]
        a = cat(a..., dims=ndims(a[1])+1)

        r = [reward(env, p) for p in 1:length(env.players)]
        r = cat(r..., dims=ndims(r[1])+1)

        push!(states, s)
        push!(acts, a)
        push!(rews, r)
    end

    @testset "Sane MultiPPO states" begin
    for i in 1:UPDATE_FREQ
        step()
        @test states[i] == policy.agents[1].trajectory[:state][:,:,:,:,i]
    end
        @test states[1] == policy.agents[1].trajectory[:state][:,:,:,:,1]
        @test states[2] == policy.agents[1].trajectory[:state][:,:,:,:,2]
        @test states[3] == policy.agents[1].trajectory[:state][:,:,:,:,3]
        # State buffer is one size bigger, so size 3 will be size 4
        step()
        step()
        @test states[2] == policy.agents[1].trajectory[:state][:,:,:,:,1]
        @test states[3] == policy.agents[1].trajectory[:state][:,:,:,:,2]
        @test states[4] == policy.agents[1].trajectory[:state][:,:,:,:,3]
        @test is_terminated(env)
        # check you can create terminal state
        @test [state(env, p) for p in 1:length(env.players)] !== nothing
    end

    @testset "Frame tests" begin
        nfc = Int(env.obs_size[3] / env.num_frames) # num frame channels

        # Timestep 1
        # Test initial zero frames are equal
        @test all(0f0 .== states[1][:,:,1:3*nfc, :])
        @test states[1][:,:,1:nfc,:] == states[1][:,:,nfc+1:2nfc,:] == states[1][:,:,2nfc+1:3nfc,:]
        # Test initial zero frame is not equal to starting frame
        @test states[1][:,:,2nfc+1:3nfc,:] != states[1][:,:,3nfc+1:end,:]

        # Timestep 2
        @test states[2][:,:,1:nfc,:] == states[2][:,:,nfc+1:2nfc,:] != states[2][:,:,2nfc+1:3nfc,:]
        @test states[2][:,:,2nfc+1:3nfc,:] != states[2][:,:,3nfc+1:end,:]
        @test states[1][:,:,3nfc+1:end,:] == states[2][:,:,2nfc+1:3nfc,:]

        # Timestep 3
        @test all(0f0 .== states[3][:,:,1:nfc,:])
        @test states[3][:,:,1:nfc,:] != states[3][:,:,nfc+1:2nfc,:]
        @test states[3][:,:,1:nfc,:] != states[3][:,:,nfc+1:2nfc,:]
        @test states[3][:,:,2nfc+1:3nfc,:] != states[3][:,:,3nfc+1:end,:]
        @test states[2][:,:,3nfc+1:end,:] == states[3][:,:,2nfc+1:3nfc,:]

        # Timestep 4
        @test states[1][:,:,3nfc+1:end,:] == states[4][:,:,1:nfc,:]
        @test states[2][:,:,3nfc+1:end,:] == states[4][:,:,1nfc+1:2nfc,:]
        @test states[3][:,:,3nfc+1:end,:] == states[4][:,:,2nfc+1:3nfc,:]
        @test states[4][:,:,2nfc+1:3nfc,:] != states[4][:,:,3nfc+1:end,:]

        # Timestep 5
        @test states[1][:,:,3nfc+1:end,:] == states[4][:,:,1:nfc,:]
        @test states[2][:,:,3nfc+1:end,:] == states[4][:,:,1nfc+1:2nfc,:]
        @test states[3][:,:,3nfc+1:end,:] == states[4][:,:,2nfc+1:3nfc,:]
        @test states[4][:,:,2nfc+1:3nfc,:] != states[4][:,:,3nfc+1:end,:]
        # Last timestep
        @test states[5][:,:,3nfc+1:end,:] == states[8][:,:,1:nfc,:]
        @test states[6][:,:,3nfc+1:end,:] == states[8][:,:,1nfc+1:2nfc,:]
        @test states[7][:,:,3nfc+1:end,:] == states[8][:,:,2nfc+1:3nfc,:]
        @test states[8][:,:,2nfc+1:3nfc,:] != states[8][:,:,3nfc+1:end,:]
        step()
        @test env.step == env.episode_len == UPDATE_FREQ + 2
    end
end


text:
using EigenCpp
using Base.Test

m1 = EigenCpp.Matrix{Float64,4,2}()
jm1 = Matrix{Float64}(m1) # converted to Julia
EigenCpp.setConstant(m1, 10)
m1[1,2] = 5.
@test m1 == [10. 5.; 10. 10.; 10. 10.; 10. 10]

println("Printing 4x2 matrix:")
EigenCpp.print(m1)

@test jm1[1,2] == 5.
@test m1[1,2] == 5.

sumexpr = m1 + m1
@test startswith(string(typeof(sumexpr)), "Sum")
msum = EigenCpp.Matrix{Float64,4,2}(sumexpr)
@test Matrix{Float64}(msum) == [20. 10.; 20. 20.; 20. 20.; 20. 20]


text:
istraining() = false

ChainRulesCore.rrule(::typeof(istraining)) = true, _ -> (NoTangent(),)

_isactive(m) = isnothing(m.active) ? istraining() : m.active

_dropout_shape(s, ::Colon) = size(s)
_dropout_shape(s, dims) = tuple((i ∉ dims ? 1 : si for (i, si) ∈ enumerate(size(s)))...)

_dropout_kernel(y::T, p, q) where {T} = y > p ? T(1 / q) : T(0)

"""
    dropout([rng = rng_from_array(x)], x, p; dims=:, active=true)

The dropout function. If `active` is `true`,
for each input, either sets that input to `0` (with probability
`p`) or scales it by `1 / (1 - p)`. `dims` specifies the unbroadcasted dimensions,
e.g. `dims=1` applies dropout along columns and `dims=2` along rows.
This is used as a regularisation, i.e. it reduces overfitting during training.

If `active` is `false`, it just returns the input `x`.

Specify `rng` for custom RNGs instead of the default RNG.
Note that custom RNGs are only supported on the CPU.

Warning: when using this function, you have to manually manage the activation
state. Usually in fact, dropout is used while training
but is deactivated in the inference phase. This can be
automatically managed using the [`Dropout`](@ref) layer instead of the
`dropout` function.

The [`Dropout`](@ref) layer is what you should use in most scenarios.
"""
function dropout(rng, x, p; dims=:, active::Bool=true)
  active || return x
  y = dropout_mask(rng, x, p, dims=dims)
  return x .* y
end
dropout(x, p; kwargs...) = dropout(rng_from_array(x), x, p; kwargs...)

dropout_mask(rng::CUDA.RNG, x::CuArray, p; kwargs...) = _dropout_mask(rng, x, p; kwargs...)
dropout_mask(rng, x::CuArray, p; kwargs...) =
  throw(ArgumentError("x isa CuArray, but rng isa $(typeof(rng)). dropout_mask only support CUDA.RNG for CuArrays."))
dropout_mask(rng, x, p; kwargs...) = _dropout_mask(rng, x, p; kwargs...)
function _dropout_mask(rng, x, p; dims=:)
  realfptype = float(real(eltype(x)))
  y = rand!(rng, similar(x, realfptype, _dropout_shape(x, dims)))
  y .= _dropout_kernel.(y, p, 1 - p)
  return y
end

# TODO move this to NNlib
ChainRulesCore.@non_differentiable dropout_mask(::Any, ::Any, ::Any)

"""
    Dropout(p; dims=:, rng = rng_from_array())

Dropout layer. In the forward pass, apply the [`Flux.dropout`](@ref) function on the input.

To apply dropout along certain dimension(s), specify the `dims` keyword.
e.g. `Dropout(p; dims = 3)` will randomly zero out entire channels on WHCN input
(also called 2D dropout).

Specify `rng` to use a custom RNG instead of the default.
Custom RNGs are only supported on the CPU.

Does nothing to the input once [`Flux.testmode!`](@ref) is `true`.
"""
mutable struct Dropout{F,D,R<:AbstractRNG}
  p::F
  dims::D
  active::Union{Bool, Nothing}
  rng::R
end
Dropout(p, dims, active) = Dropout(p, dims, active, rng_from_array())

function Dropout(p; dims=:, rng = rng_from_array())
  @assert 0 ≤ p ≤ 1
  Dropout(p, dims, nothing, rng)
end

@functor Dropout
trainable(a::Dropout) = (;)

function (a::Dropout)(x)
  _isactive(a) || return x
  return dropout(a.rng, x, a.p; dims=a.dims, active=true)
end

testmode!(m::Dropout, mode=true) =
  (m.active = (isnothing(mode) || mode == :auto) ? nothing : !mode; m)

function Base.show(io::IO, d::Dropout)
  print(io, "Dropout(", d.p)
  d.dims != (:) && print(io, ", dims = $(repr(d.dims))")
  print(io, ")")
end

"""
    AlphaDropout(p; rng = rng_from_array())

A dropout layer. Used in
[Self-Normalizing Neural Networks](https://arxiv.org/abs/1706.02515).
The AlphaDropout layer ensures that mean and variance of activations
remain the same as before.

Does nothing to the input once [`testmode!`](@ref) is true.
"""
mutable struct AlphaDropout{F,R<:AbstractRNG}
  p::F
  active::Union{Bool, Nothing}
  rng::R
  function AlphaDropout(p, active, rng)
    @assert 0 ≤ p ≤ 1
    new{typeof(p), typeof(rng)}(p, active, rng)
  end
end
AlphaDropout(p, active) = AlphaDropout(p, active, rng_from_array())
AlphaDropout(p; rng = rng_from_array()) = AlphaDropout(p, nothing, rng)

@functor AlphaDropout
trainable(a::AlphaDropout) = (;)

function (a::AlphaDropout)(x::AbstractArray{T}) where T
  _isactive(a) || return x
  p = a.p
  iszero(p) && return x
  isone(p) && return sign.(x) .* T(0)

  α′ = T(-1.7580993408473766) # selu(-Inf) == -λα
  A = T(inv(sqrt((1 - p) * (1 + p * α′^2))))
  B = T(-A * α′ * p)

  noise = rand!(a.rng, similar(x))
  return A .* ifelse.(noise .> p, x, α′) .+ B
end

testmode!(m::AlphaDropout, mode=true) =
  (m.active = (isnothing(mode) || mode == :auto) ? nothing : !mode; m)

"""
    LayerNorm(size..., λ=identity; affine=true, ϵ=1fe-5)

A [normalisation layer](https://arxiv.org/abs/1607.06450) designed to be
used with recurrent hidden states.
The argument `sz` should be an integer or a tuple of integers.
In the forward pass, the layer normalises the mean and standard
deviation of the input, the applied the elementwise activation `λ`.
The input is normalised along the first `length(sz)` dimensions
for tuple `sz`, along the first dimension for integer `sz`.
The input  is expected to have first dimensions' size equal to `sz`.

If `affine=true` also applies a learnable shift and rescaling
using the [`Scale`](@ref) layer.

See also [`BatchNorm`](@ref), [`InstanceNorm`](@ref), [`GroupNorm`](@ref), and [`normalise`](@ref).
"""
struct LayerNorm{F,D,T,N}
  λ::F
  diag::D
  ϵ::T
  size::NTuple{N,Int}
  affine::Bool
end

function LayerNorm(size::Tuple{Vararg{Int}}, λ=identity; affine::Bool=true, ϵ::Real=1f-5)
  diag = affine ? Scale(size..., λ) : λ!=identity ? Base.Fix1(broadcast, λ) : identity
  return LayerNorm(λ, diag, ϵ, size, affine)
end
LayerNorm(size::Integer...; kw...) = LayerNorm(Int.(size); kw...)
LayerNorm(size_act...; kw...) = LayerNorm(Int.(size_act[1:end-1]), size_act[end]; kw...)

@functor LayerNorm

(a::LayerNorm)(x) = a.diag(normalise(x, dims=1:length(a.size), ϵ=a.ϵ))

function Base.show(io::IO, l::LayerNorm)
  print(io, "LayerNorm(", join(l.size, ", "))
  l.λ === identity || print(io, ", ", l.λ)
  hasaffine(l) || print(io, ", affine=false")
  print(io, ")")
end

# For InstanceNorm, GroupNorm, and BatchNorm.
# Compute the statistics on the slices specified by reduce_dims.
# reduce_dims=[1,...,N-2,N] for BatchNorm
# reduce_dims=[1,...,N-2] for InstanceNorm and GroupNorm
function _norm_layer_forward(
  l, x::AbstractArray{T, N}; reduce_dims, affine_shape,
) where {T, N}
  if !_isactive(l) && l.track_stats # testmode with tracked stats
    stats_shape = ntuple(i -> i == N-1 ? size(x, N-1) : 1, N)
    μ = reshape(l.μ, stats_shape)
    σ² = reshape(l.σ², stats_shape)
  else # trainmode or testmode without tracked stats
    μ = mean(x; dims=reduce_dims)
    σ² = mean((x .- μ).^2; dims=reduce_dims)
    if l.track_stats
      _track_stats!(l, x, μ, σ², reduce_dims) # update moving mean/std
    end
  end

  o = _norm_layer_forward(x, μ, σ², l.ϵ)
  hasaffine(l) || return l.λ.(o)

  γ = reshape(l.γ, affine_shape)
  β = reshape(l.β, affine_shape)
  return l.λ.(γ .* o .+ β)
end

@inline _norm_layer_forward(x, μ, σ², ϵ) = (x .- μ) ./ sqrt.(σ² .+ ϵ)

function _track_stats!(
  bn, x::AbstractArray{T, N}, μ, σ², reduce_dims,
) where {T, N}
  V = eltype(bn.σ²)
  mtm = bn.momentum
  res_mtm = one(V) - mtm
  m = prod(size(x, i) for i in reduce_dims)

  μnew = vec(N ∈ reduce_dims ? μ : mean(μ, dims=N))
  σ²new = vec(N ∈ reduce_dims ? σ² : mean(σ², dims=N))

  bn.μ = res_mtm .* bn.μ .+ mtm .* μnew
  bn.σ² = res_mtm .* bn.σ² .+ mtm .* (m / (m - one(V))) .* σ²new
  return nothing
end

ChainRulesCore.@non_differentiable _track_stats!(::Any...)

"""
    BatchNorm(channels::Integer, λ=identity;
              initβ=zeros32, initγ=ones32,
              affine = true, track_stats = true,
              ϵ=1f-5, momentum= 0.1f0)

[Batch Normalization](https://arxiv.org/abs/1502.03167) layer.
`channels` should be the size of the channel dimension in your data (see below).

Given an array with `N` dimensions, call the `N-1`th the channel dimension. For
a batch of feature vectors this is just the data dimension, for `WHCN` images
it's the usual channel dimension.

`BatchNorm` computes the mean and variance for each `D_1×...×D_{N-2}×1×D_N`
input slice and normalises the input accordingly.

If `affine=true`, it also applies  a shift and a rescale to the input
through to learnable per-channel bias β and scale γ parameters.

After normalisation, elementwise activation `λ` is applied.

If `track_stats=true`, accumulates mean and var statistics in training phase
that will be used to renormalize the input in test phase.

Use [`testmode!`](@ref) during inference.

# Examples
```julia
m = Chain(
  Dense(28^2 => 64),
  BatchNorm(64, relu),
  Dense(64 => 10),
  BatchNorm(10),
  softmax)
```
"""
mutable struct BatchNorm{F,V,N,W}
  λ::F  # activation function
  β::V  # bias
  γ::V  # scale
  μ::W     # moving mean
  σ²::W    # moving var
  ϵ::N
  momentum::N
  affine::Bool
  track_stats::Bool
  active::Union{Bool, Nothing}
  chs::Int # number of channels
end

function BatchNorm(chs::Int, λ=identity;
          initβ=zeros32, initγ=ones32,
          affine=true, track_stats=true,
          ϵ=1f-5, momentum=0.1f0)

  β = affine ? initβ(chs) : nothing
  γ = affine ? initγ(chs) : nothing
  μ = track_stats ? zeros32(chs) : nothing
  σ² = track_stats ? ones32(chs) : nothing

  return BatchNorm(λ, β, γ,
            μ, σ², ϵ, momentum,
            affine, track_stats,
            nothing, chs)
end

@functor BatchNorm
trainable(bn::BatchNorm) = hasaffine(bn) ? (β = bn.β, γ = bn.γ) : (;)

function (BN::BatchNorm)(x)
  @assert size(x, ndims(x)-1) == BN.chs
  N = ndims(x)
  reduce_dims = [1:N-2; N]
  affine_shape = ntuple(i -> i == N-1 ? size(x, N-1) : 1, N)
  return _norm_layer_forward(BN, x; reduce_dims, affine_shape)
end

testmode!(m::BatchNorm, mode=true) =
  (m.active = (isnothing(mode) || mode == :auto) ? nothing : !mode; m)

function Base.show(io::IO, l::BatchNorm)
  print(io, "BatchNorm($(l.chs)")
  (l.λ == identity) || print(io, ", $(l.λ)")
  hasaffine(l) || print(io,  ", affine=false")
  print(io, ")")
end

"""
    InstanceNorm(channels::Integer, λ=identity;
                 initβ=zeros32, initγ=ones32,
                 affine=false, track_stats=false,
                 ϵ=1f-5, momentum=0.1f0)

[Instance Normalization](https://arxiv.org/abs/1607.08022) layer.
`channels` should be the size of the channel dimension in your data (see below).

Given an array with `N > 2` dimensions, call the `N-1`th the channel dimension.
For `WHCN` images it's the usual channel dimension.

`InstanceNorm` computes the mean and variance for each `D_1×...×D_{N-2}×1×1`
input slice and normalises the input accordingly.

If `affine=true`, it also applies  a shift and a rescale to the input
through to learnable per-channel bias `β` and scale `γ` parameters.

If `track_stats=true`, accumulates mean and var statistics in training phase
that will be used to renormalize the input in test phase.

**Warning**: the defaults for `affine` and `track_stats` used to be `true`
in previous Flux versions (< v0.12).
"""
mutable struct InstanceNorm{F,V,N,W}
  λ::F  # activation function
  β::V  # bias
  γ::V  # scale
  μ::W  # moving mean
  σ²::W  # moving var
  ϵ::N
  momentum::N
  affine::Bool
  track_stats::Bool
  active::Union{Bool, Nothing}
  chs::Int # number of channels
end

function InstanceNorm(chs::Int, λ=identity;
                    initβ=zeros32, initγ=ones32,
                    affine=false, track_stats=false,
                    ϵ=1f-5, momentum=0.1f0)

  β = affine ? initβ(chs) : nothing
  γ = affine ? initγ(chs) : nothing
  μ = track_stats ? zeros32(chs) : nothing
  σ² = track_stats ? ones32(chs) : nothing

  return InstanceNorm(λ, β, γ,
            μ, σ², ϵ, momentum,
            affine, track_stats,
            nothing, chs)
end

@functor InstanceNorm
trainable(in::InstanceNorm) = hasaffine(in) ? (β = in.β, γ = in.γ) : (;)

function (l::InstanceNorm)(x)
  @assert ndims(x) > 2
  @assert size(x, ndims(x)-1) == l.chs
  N = ndims(x)
  reduce_dims = 1:N-2
  affine_shape = ntuple(i -> i == N-1 ? size(x, N-1) : 1, N)
  return _norm_layer_forward(l, x; reduce_dims, affine_shape)
end

testmode!(m::InstanceNorm, mode=true) =
  (m.active = (isnothing(mode) || mode == :auto) ? nothing : !mode; m)

function Base.show(io::IO, l::InstanceNorm)
  print(io, "InstanceNorm($(l.chs)")
  l.λ == identity || print(io, ", $(l.λ)")
  hasaffine(l) || print(io,  ", affine=false")
  print(io, ")")
end

"""
    GroupNorm(channels::Integer, G::Integer, λ=identity;
              initβ=zeros32, initγ=ones32,
              affine=true, track_stats=false,
              ϵ=1f-5, momentum=0.1f0)

[Group Normalization](https://arxiv.org/abs/1803.08494) layer.

`chs` is the number of channels, the channel dimension of your input.
For an array of N dimensions, the `N-1`th index is the channel dimension.

`G` is the number of groups along which the statistics are computed.
The number of channels must be an integer multiple of the number of groups.

`channels` should be the size of the channel dimension in your data (see below).

Given an array with `N > 2` dimensions, call the `N-1`th the channel dimension.
For `WHCN` images it's the usual channel dimension.

If `affine=true`, it also applies  a shift and a rescale to the input
through to learnable per-channel bias `β` and scale `γ` parameters.

If `track_stats=true`, accumulates mean and var statistics in training phase
that will be used to renormalize the input in test phase.
"""
mutable struct GroupNorm{F,V,N,W}
  G::Int  # number of groups
  λ::F  # activation function
  β::V  # bias
  γ::V  # scale
  μ::W     # moving mean
  σ²::W    # moving std
  ϵ::N
  momentum::N
  affine::Bool
  track_stats::Bool
  active::Union{Bool, Nothing}
  chs::Int # number of channels
end

@functor GroupNorm
trainable(gn::GroupNorm) = hasaffine(gn) ? (β = gn.β, γ = gn.γ) : (;)

function GroupNorm(chs::Int, G::Int, λ=identity;
              initβ=zeros32, initγ=ones32,
              affine=true, track_stats=false,
              ϵ=1f-5, momentum=0.1f0)

  chs % G == 0 || error("The number of groups ($(G)) must divide the number of channels ($chs)")

  β = affine ? initβ(chs) : nothing
  γ = affine ? initγ(chs) : nothing
  μ = track_stats ? zeros32(G) : nothing
  σ² = track_stats ? ones32(G) : nothing

  return GroupNorm(G, λ,
            β, γ,
            μ, σ²,
            ϵ, momentum,
            affine, track_stats,
            nothing, chs)
end

function (gn::GroupNorm)(x)
  @assert ndims(x) > 2
  @assert size(x, ndims(x)-1) == gn.chs
  N = ndims(x)
  sz = size(x)
  x = reshape(x, sz[1:N-2]..., sz[N-1]÷gn.G, gn.G, sz[N])
  N = ndims(x)
  reduce_dims = 1:N-2
  affine_shape = ntuple(i -> i ∈ (N-1, N-2) ? size(x, i) : 1, N)
  x = _norm_layer_forward(gn, x; reduce_dims, affine_shape)
  return reshape(x, sz)
end

testmode!(m::GroupNorm, mode = true) =
  (m.active = (isnothing(mode) || mode == :auto) ? nothing : !mode; m)

function Base.show(io::IO, l::GroupNorm)
  # print(io, "GroupNorm($(join(size(l.β), ", "))", ", ", l.G)
  print(io, "GroupNorm($(l.chs), $(l.G)")
  l.λ == identity || print(io, ", ", l.λ)
  hasaffine(l) || print(io,  ", affine=false")
  print(io, ")")
end

"""
  hasaffine(l)

Return `true` if a normalisation layer has trainable shift and
scale parameters, `false` otherwise.

See [`BatchNorm`](@ref), [`InstanceNorm`](@ref), [`GroupNorm`](@ref), and [`LayerNorm`](@ref).
"""
hasaffine(l::Union{BatchNorm, InstanceNorm, LayerNorm, GroupNorm}) = l.affine


text:
module FluxModels

using Reexport
include("./VisionModel/VisionModel.jl")
@reexport using .VisionModel

end


text:
struct Load
    bus_ids::AbstractVector{String}
    nodal_load::AbstractArray{Real}    # This can be a Vector or a Matrix, for multiple time steps
end
Base.show(io::IO, loads::Load) = print("Nodal load specified at $(loads.bus_ids)")

struct Generator
    bus::String
    id::String
    gtype::String
    price::Real
    startup::Real
    shutdown::Real
    ramp_up::Real
    ramp_down::Real
    upper_limit::Real
    lower_limit::Real
end
Generator(bus, price) = Generator(bus, bus, "default", price, 0.0, 0.0, Inf, Inf, Inf, 0.0)
Base.show(io::IO, g::Generator) = print("Generator $(g.id) of type $(g.gtype) at bus $(g.bus).")

abstract type Branch end

struct Line<:Branch
    branch_id::String
    from_to_bus::Tuple{String,String}
    thermal_limit::Real
end
Base.show(io::IO, l::Line) = print("Line $(l.branch_id), $(l.from_to_bus), with limit $(l.thermal_limit)")

struct Grid
    bus_ids::AbstractVector{String}
    branch_ids::AbstractVector{String}
    generators::AbstractVector{Generator}
    branches::AbstractVector{<:Branch}
    incidence::AxisArray
end
function Grid(bus_ids, branch_ids, generators, branches)
    all_branches = [b.branch_id for b in branches]
    if length(Set(bus_ids)) < length(bus_ids)
        error("Multiply defined bus ids")
    end
    if length(Set(branch_ids)) < length(branch_ids)
        error("Multiply defined branch ids")
    end
    if !isempty(symdiff(all_branches, branch_ids))
        error("Incorrect specification of the branches and branch_ids")
    end
    all_buses = Set()
    for b in branches
        for i in b.from_to_bus
            push!(all_buses, i)
        end
    end
    if !isempty(symdiff(bus_ids, all_buses))
        error("Incorrect specification of buses. The branches and buses contain different identifiers")
    end
    gen_buses = [g.bus for g in generators]
    if !isempty(setdiff(gen_buses, bus_ids))
        error("Some generators have been placed in non-existent buses")
    end

    n_buses = length(all_buses)
    n_branches = length(branch_ids)
    incidence = AxisArray(zeros(n_branches, n_buses), Axis{:branch_id}(branch_ids), Axis{:bus_id}(bus_ids))
    for b in branches
        branch_idx = find(branch_ids .== b.branch_id)[1] # find returns an array
        from_idx = find(bus_ids .== b.from_to_bus[1])[1]
        to_idx = find(bus_ids .== b.from_to_bus[2])[1]
        incidence[branch_idx, from_idx] = -1 # This is immaterial, at the moment.
        incidence[branch_idx, to_idx] = 1
    end

    return Grid(bus_ids, branch_ids, generators, branches, incidence)
end
Base.show(io::IO, g::Grid) = print("Grid with $(length(g.bus_ids)) buses and $(length(g.branch_ids)) branches")


text:
module effective_interaction_simple

using ..CollectiveSpins

const e_z = [0,0,1]
const gamma = 1

function effective_interactions(S::SpinCollection{T,P,G}) where {T,P,G}
    omega_eff::float(G) = 0.
    gamma_eff::float(G) = 0.
    origin = zeros(3)
    for spin = S.spins
        omega_eff += interaction.Omega(origin, spin.position, S.polarizations[1], S.polarizations[1], gamma, gamma)
        gamma_eff += interaction.Gamma(origin, spin.position, S.polarizations[1], S.polarizations[1], gamma, gamma)
    end
    return omega_eff, gamma_eff
end

# Finite symmetric systems

function triangle_orthogonal(a::T) where T<:Real
    positions = Vector{float(T)}[[a,0,0], [a/2, sqrt(3. /4)*a,0]]
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function square_orthogonal(a::T) where T<:Real
    positions = Vector{float(T)}[[a,0,0], [0,a,0], [a,a,0]]
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function polygon_orthogonal(N::Int, a::T) where T<:Real
    @assert N>2
    dα = 2*pi/N
    R = a/(2*sin(dα/2))
    positions = Vector{float(T)}[]
    for i=1:(N-1)
        x = R*cos(i*dα)
        y = R*sin(i*dα)
        push!(positions, float(T)[x-R,y,0])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function rectangle_orthogonal(a::U, b::T) where {U<:Real,T<:Real}
    positions = Vector{float(T)}[[a,0,0], [0,b,0], [a,b,0]]
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function cube_orthogonal(a::T) where T<:Real
    positions = Vector{float(T)}[]
    for ix=0:1, iy=0:1, iz=0:1
        if ix==0 && iy==0 && iz==0
            continue
        end
        push!(positions, [ix*a, iy*a, iz*a])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function box_orthogonal(a::T, b::U, c::V) where {T<:Real,U<:Real,V<:Real}
   positions = Vector{float(T)}[]
    for ix=0:1, iy=0:1, iz=0:1
        if ix==0 && iy==0 && iz==0
            continue
        end
        push!(positions, [ix*a, iy*b, iz*c])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

# Infinite 1D symmetric systems

function chain(a::T, Θ, N::Int) where T<:Real
    positions = Vector{float(T)}[]
    for ix=-N:N
        if ix==0
            continue
        end
        push!(positions, [ix*a, 0., 0.])
    end
    S = SpinCollection(positions, Float64[cos(Θ), 0., sin(Θ)]; gammas=gamma)
    return effective_interactions(S)
end

function chain_orthogonal(a::T, N::Int) where T<:Real
    positions = Vector{float(T)}[]
    for ix=-N:N
        if ix==0
            continue
        end
        push!(positions, [ix*a, 0., 0.])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

# Infinite 2D symmetric systems

function squarelattice_orthogonal(a::T, N::Int) where {T<:Real}
    positions = Vector{float(T)}[]
    for ix=-N:N, iy=-N:N
        if ix==0 && iy==0
            continue
        end
        push!(positions, [ix*a, iy*a, 0.])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function hexagonallattice_orthogonal(a::T, N::Int) where {T<:Real}
    positions = Vector{float(T)}[]
    ax = sqrt(3.0/4)*a
    for iy=1:N
        push!(positions, [0, iy*a, 0])
        push!(positions, [0, -iy*a, 0])
    end
    for ix=[-1:-2:-N; 1:2:N]
        Ny = div(2*N+1-abs(ix),2)
        for iy=0:Ny-1
            push!(positions, [ax*ix, (0.5+iy)*a, 0])
            push!(positions, [ax*ix, -(0.5+iy)*a, 0])
        end
    end
    for ix=[-2:-2:-N; 2:2:N]
        Ny = div(2*N-abs(ix),2)
        push!(positions, [ax*ix, 0, 0])
        for iy=1:Ny
            push!(positions, [ax*ix, iy*a, 0])
            push!(positions, [ax*ix, -iy*a, 0])
        end
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

# Infinite 3D symmetric systems

function cubiclattice_orthogonal(a::T, N::Int) where {T<:Real}
    positions = Vector{float(T)}[]
    for ix=-N:N, iy=-N:N, iz=-N:N
        if ix==0 && iy==0 && iz==0
            continue
        end
        push!(positions, [ix*a, iy*a, iz*a])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function tetragonallattice_orthogonal(a::T, b::U, N::Int) where {T<:Real,U<:Real}
    positions = Vector{float(T)}[]
    for ix=-N:N, iy=-N:N, iz=-N:N
        if ix==0 && iy==0 && iz==0
            continue
        end
        push!(positions, [ix*a, iy*a, iz*b])
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

function hexagonallattice3d_orthogonal(a::T, b::U, N::Int) where {T<:Real,U<:Real}
    positions = Vector{float(T)}[]
    ax = sqrt(3.0/4)*a
    for iz=-N:N
        for iy=1:N
            push!(positions, [0, iy*a, iz*b])
            push!(positions, [0, -iy*a, iz*b])
        end
        for ix=[-1:-2:-N; 1:2:N]
            Ny = div(2*N+1-abs(ix),2)
            for iy=0:Ny-1
                push!(positions, [ax*ix, (0.5+iy)*a, iz*b])
                push!(positions, [ax*ix, -(0.5+iy)*a, iz*b])
            end
        end
        for ix=[-2:-2:-N; 2:2:N]
            Ny = div(2*N-abs(ix),2)
            push!(positions, [ax*ix, 0, iz*b])
            for iy=1:Ny
                push!(positions, [ax*ix, iy*a, iz*b])
                push!(positions, [ax*ix, -iy*a, iz*b])
            end
        end
    end
    S = SpinCollection(positions, e_z; gammas=gamma)
    return effective_interactions(S)
end

end # module


text:
X(j,n) = sqrt((j+n)*(j-n+1))

function Jy_matrix_zbasis!(j, A::AbstractArray{T}) where {T<:Complex}

	N = twojp1(j)
	length(A) >= N^2 || throw(ArgumentError("array needs to have at least $(N^2) elements"))

	Av = reshape(@view(A[1:N^2]),N,N)
	fill!(Av,zero(T))
	h = Hermitian(Av)

    @inbounds for i in 1:N-1
	    Av[i,i+1] = T(0, X(j,-j+i)/2)
	end

	return h
end

function Jy_eigen_nomatch!(j, A)
	Jy = Jy_matrix_zbasis!(j, A)
	_,v = eigen!(Jy, sortby = λ -> round(real(λ)))
	permutedims(v)
end

function eigenvecsJy!(j, A = Matrix{ComplexF64}(undef, twojp1(j), twojp1(j)))
	get!(JyEigenDict, HalfInt(j)) do
		Jy_eigen_nomatch!(j, A)
	end
end

function wignerdmatrixelement(::Type{T}, j::HalfInt, m, n, β::Real, v) where {T<:Real}
	mind = floor(Int, m + j) + 1
	nind = floor(Int, n + j) + 1

	# Compute the sum in the higher accuracy out of T and the individual terms
	Tel = promote_type(eltype(v), Complex{promote_type(HalfInt, typeof(β))})
	Tdcomplex = promote_type(Complex{T},Tel)
	TR_high = typeofreal(Tdcomplex)
	dmn = zero(Tdcomplex)

	λ = HalfInt(-j):HalfInt(j)

	@inbounds for μ in eachindex(λ)
		dmn += cis(-TR_high(λ[μ]*β)) * v[μ,mind] * conj(v[μ,nind])
	end

	T(real(dmn))
end

nonzerocheck(::Union{ZeroRadians, TwoNPiRadians}, m, n, j) = m == n
nonzerocheck(::TwoNPlusOnePiRadians, m, n, j) = m == -n
function nonzerocheck(::Piby2Radians, m, n, j)
	!(isodd(floor(Int, j+m)) && iszero(n)) && !(isodd(floor(Int, j+n)) && iszero(m))
end
# Generally this isn't known from the type, assume all are non-zero
nonzerocheck(::Real, m, n, j) = true

nonzerofirstindex(::Union{ZeroRadians, TwoNPiRadians}, n) = n
nonzerofirstindex(::Union{TwoNPlusOnePiRadians}, n) = -n

function wignerdmatrixelement(::Type{T}, j::HalfInt, m, n, β::ZeroRadians, v = nothing) where {T<:Real}
	nonzerocheck(β, m, n, j) ? one(T) : zero(T)
end

function wignerdmatrixelement(::Type{T}, j::HalfInt, m, n, β::Irrational{:π}, v = nothing) where {T<:Real}
	wignerdmatrixelement(T, j, m, n, Pi, v)
end

function wignerdmatrixelement(::Type{T}, j::HalfInt, m, n, x::TwoNPiRadians, v = nothing) where {T<:Real}
	nonzerocheck(x, m, n, j) ? (iseven(twice(j)*x.n) ? one(T) : -one(T)) : zero(T)
end

function wignerdmatrixelement(::Type{T}, j::HalfInt, m, n, x::TwoNPlusOnePiRadians, v = nothing) where {T<:Real}
	nonzerocheck(x, m, n, j) ? (iseven(twice(j)*x.n + floor(Int, j+m)) ? one(T) : -one(T)) : zero(T)
end

function wignerdmatrixelement(::Type{T}, j::HalfInt, m, n, β::Piby2Radians, v) where {T<:Real}
	mind = floor(Int, m + j) + 1
	nind = floor(Int, n + j) + 1

	λ = HalfInt(-j):HalfInt(j)

	# Compute the sum in the higher accuracy out of T and the individual terms
	Tel = promote_type(eltype(v), Complex{promote_type(HalfInt, typeof(β))})
	Tdcomplex = promote_type(Complex{T},Tel)
	TR_high = typeofreal(Tdcomplex)
	dmn = zero(Tdcomplex)

	if nonzerocheck(β, m, n, j)
		@inbounds for μ in eachindex(λ)
			dmn += cis_special(TR_high,-λ[μ], β) * v[μ,mind] * conj(v[μ,nind])
		end
	end

	T(real(dmn))
end

typeofreal(::Type{Complex{T}}) where {T} = T

# promote different depending on whether v is specified.
# the result will be real if v is not specified
function wignerdmatrixelement(j::HalfInt, m, n, β::Real, v::AbstractMatrix)
	T = promote_type(eltype(v), Complex{promote_type(HalfInt, typeof(β))})
	wignerdmatrixelement(typeofreal(T), j, m, n, β, v)
end

function wignerdmatrixelement(j::HalfInt, m, n, β::Real, v = nothing)
	T = promote_type(HalfInt, typeof(β))
	wignerdmatrixelement(T, j, m, n, β, v)
end

"""
	wignerdmatrixelement(j::Real, m, n, β::Real, [v = WignerDMatrices.eigenvecsJy!(j)])

Compute the element ``d^j_{m,n}(β)`` of the Wigner d-matrix in the z-y-z convention.
Optionally the matrix `v` that contains the transposed eigenvectors of the angular momentum
operator `Jy` corresponding to `j` may be provided.

The angular momentum `j` must be either an `Integer` or a half-integer.

# Examples

```jldoctest
julia> dmn = WignerDMatrices.wignerdmatrixelement(1/2, 1/2, 1/2, WignerDMatrices.TwoPi)
-1.0

julia> d = WignerdMatrix(1/2, WignerDMatrices.TwoPi)
2×2 WignerdMatrix{Float64} for j = 1/2 and beta = 2π with indices -1/2:1/2×-1/2:1/2:
 -1.0    ⋅
   ⋅   -1.0

julia> d[1/2,1/2] == dmn
true
```
"""
wignerdmatrixelement(j::Real, m, n, β::Real, v = eigenvecsJy!(j)) = wignerdmatrixelement(HalfInt(j), m, n, β, v)

"""
	wignerDmatrixelement(j::Real, m, n, (α, β, γ)::NTuple{3,Real}, [v = WignerDMatrices.eigenvecsJy!(j)])

Compute the element ``D^j_{m,n}(α, β, γ)`` of the Wigner D-matrix in the z-y-z convention.
Optionally the matrix `v` that contains the transposed eigenvectors of the angular momentum
operator `Jy` corresponding to `j` may be provided.

The angular momentum `j` must be either an `Integer` or a half-integer.

# Examples

```jldoctest
julia> Dmn = WignerDMatrices.wignerDmatrixelement(1/2, 1/2, 1/2, (0, WignerDMatrices.TwoPi, 0))
-1.0 + 0.0im

julia> D = WignerDMatrix(1/2, 0, WignerDMatrices.TwoPi, 0)
2×2 WignerDMatrix{Complex{Float64}} for j = 1/2, alpha = 0, beta = 2π and gamma = 0 with indices -1/2:1/2×-1/2:1/2:
 -1.0-0.0im       ⋅
      ⋅      -1.0-0.0im

julia> D[1/2,1/2] == Dmn
true
```
"""
function wignerDmatrixelement(j::Real, m, n, (α, β, γ)::NTuple{3,Real}, v = eigenvecsJy!(j))
	wignerdmatrixelement(j, m, n, β, v) * WignerDphase(m, α) * WignerDphase(n, γ)
end

function djmatrix_fill!(d::SpinMatrix{T,<:WignerdMatrixContainer}, j, β, v) where {T}
	# optimizations
	if iszero(β)
		djmatrix_fill!(d, j, Zero, v)
	elseif isinteger(β/π)
		n = floor(Int, β/π)
		# type-unstable, but doesn't really matter
		if isodd(n)
			βnew = TwoNPlusOnePiRadians(n >> 1)
		else
			βnew = TwoNPiRadians(n >> 1)
		end
		djmatrix_fill!(d, j, βnew, v)
	else
		@inbounds for n = -j:zero(j), m = n:-n
			# skip if the element is known to be zero
			nonzerocheck(β, m, n, j) || continue
			d[m,n] = wignerdmatrixelement(T, j, m, n, β, v)
		end
	end

	return d
end

function djmatrix_fill!(d::SpinMatrix{<:Any,<:WignerdMatrixContainer}, j, β::Irrational{:π}, v)
	djmatrix_fill!(d, j, Pi, v)
end

function djmatrix_fill!(d::SpinMatrix{T,<:WignerdMatrixContainer}, j, β::ScaledPi, v) where {T}
	@inbounds for n = -j:zero(j)
		m = nonzerofirstindex(β, n)
		d[m,n] = wignerdmatrixelement(T, j, m, n, β, v)
	end

	return d
end

djmatrix_fill!(d::WignerdMatrix, args...) = djmatrix_fill!(d.dj, args...)
djmatrix_fill!(d::WignerDMatrix, args...) = djmatrix_fill!(d.dj, args...)

function WignerdMatrix!(d::SpinMatrix, β::Real, A)

	fill!(parent(d), zero(eltype(parent(d))))

	j = d.j
	v = eigenvecsJy!(j, A)
	djmatrix_fill!(d, HalfInt(j), β, v)

	return d
end

function WignerdMatrix!(D::WignerDMatrix{T}, β::Real, args...) where {T}
	d = WignerdMatrix!(D.dj, β, args...)
	WignerDMatrix{T}(D.alpha, d, D.gamma)
end

"""
	WignerdMatrix{T}(j, β::Real) where {T<:Real}

Construct the Wigner d-matrix in the z-y-z convention for the angular momentum `j` and the angle `β`, where
the elements of the matrix will be of the type `T`.

The matrix elements are expanded in the basis of the eigenfunctions of
the angular momentum operator `Jy`.
Specifying the type of the matrix elements does not increase the accuracy
of the numerical evaluation of eigenvectors,
as this is set by the LAPACK implementation used.
An increased precision, however, might help in avoiding round-off errors in the sum.

# Examples

```jldoctest
julia> d = WignerdMatrix{BigFloat}(1, 0.4)
3×3 WignerdMatrix{BigFloat} for j = 1 and beta = 0.4 with indices -1:1×-1:1:
  0.96053     0.27536   0.0394695
 -0.27536     0.921061  0.27536
  0.0394695  -0.27536   0.96053

julia> d[1,1]
0.9605304970014426312106516543295635109444272950721034465148443857827477926800066

julia> WignerdMatrix{Float64}(1, 0.4)[1,1] # compare with Float64
0.9605304970014427
```
"""
function WignerdMatrix{T}(j, β::Real) where {T<:Real}
	jh = HalfInt(j)
	w = WignerdMatrixContainer{T}(undef, jh)
	s = SpinMatrix(w, jh)
	d = WignerdMatrix(β, s)

	A = Matrix{ComplexF64}(undef, size(d)...)

	WignerdMatrix!(s, β, A)
	return d
end
WignerdMatrix(j, β::T) where {T<:Real} = WignerdMatrix{promote_type(T,Float64)}(j, β)
WignerdMatrix(j, β::SpecialAngle{T}) where {T} = WignerdMatrix{promote_type(T,Float64)}(j, β)

"""
	WignerDMatrix{T}(j, α::Real, β::Real, γ::Real) where {T<:Complex}

Construct the Wigner D-matrix in the z-y-z convention for the angular momentum `j` and the Euler angles
`(α, β, γ)`, where the matrix elements are of the type `T`.

# Examples

```jldoctest
julia> WignerDMatrix{Complex{BigFloat}}(0.5, 0, 0, 0)
2×2 WignerDMatrix{Complex{BigFloat}} for j = 1/2, alpha = 0, beta = 0 and gamma = 0 with indices -1/2:1/2×-1/2:1/2:
 1.0+0.0im  0.0-0.0im
 0.0+0.0im  1.0+0.0im
```
"""
function WignerDMatrix{Complex{T}}(j, α::Real, β::Real, γ::Real) where {T}
	Td = promote_type(T, Float64)
	d = WignerdMatrix{Td}(j, β)
	WignerDMatrix{Complex{T}}(α, d, γ)
end

function WignerDMatrix(j, α::Real, β::Real, γ::Real)
	d = WignerdMatrix(j, β)
	WignerDMatrix(α, d, γ)
end


text:
"""
    abstract type Polytope{D} <: GridapType

Abstract type representing a polytope (i.e., a polyhedron in arbitrary dimensions).
`D` is the environment dimension (typically, 0, 1, 2, or 3).
This type parameter is needed since there are functions in the
`Polytope` interface that return containers with `Point{D}` objects.
We adopt the [usual nomenclature](https://en.wikipedia.org/wiki/Polytope) for polytope-related objects.
All objects in a polytope (from vertices to the polytope itself) are called *n-faces* or simply *faces*.
The notation *n-faces* is used only when it is needed to refer to the object dimension n. Otherwise we simply
use *face*. In addition, we say

- vertex (pl. vertices): for 0-faces
- edge: for 1-faces
- facet: for (`D-1`)-faces

The `Polytope` interface is defined by overloading the following functions

- [`get_faces(p::Polytope)`](@ref)
- [`get_dimranges(p::Polytope)`](@ref)
- [`Polytope{N}(p::Polytope,faceid::Integer) where N`](@ref)
- [`get_vertex_coordinates(p::Polytope)`](@ref)
- [`(==)(a::Polytope{D},b::Polytope{D}) where D`](@ref)

And optionally these ones:

- [`get_edge_tangent(p::Polytope)`](@ref)
- [`get_facet_normal(p::Polytope)`](@ref)
- [`get_facet_orientations(p::Polytope)`](@ref)
- [`get_vertex_permutations(p::Polytope)`](@ref)
- [`is_n_cube(p::Polytope)`](@ref)
- [`is_simplex(p::Polytope)`](@ref)
- [`simplexify(p::Polytope)`](@ref)

The interface can be tested with the function

- [`test_polytope`](@ref)

"""
abstract type Polytope{D} <: GridapType end

# Mandatory

"""
    get_faces(p::Polytope) -> Vector{Vector{Int}}

Given a polytope `p` the function returns a vector of vectors
defining the *incidence* relation of the faces in the polytope.

Each face in the polytope receives a unique integer id. The id 1 is assigned
to the first 0-face. Consecutive increasing ids are assigned to the other
0-faces, then to 1-faces, and so on. The polytope itself receives the largest id
which coincides with `num_faces(p)`. For a face id `iface`, `get_faces(p)[iface]`
is a vector of face ids, corresponding to the faces that are *incident* with the face
labeled with `iface`. That is, faces that are either on its boundary or the face itself.
In this vector of incident face ids, faces are ordered by dimension, starting with 0-faces.
Within each dimension, the labels are ordered in a consistent way with the polyope object
for the face `iface` itself.

# Examples

```jldoctest
using Gridap.ReferenceFEs

faces = get_faces(SEGMENT)
println(faces)

# output
Array{Int,1}[[1], [2], [1, 2, 3]]
```

The constant [`SEGMENT`](@ref) is bound to a predefined instance of polytope
that represents a segment.
The face labels associated with a segment are `[1,2,3]`, being `1` and `2` for the vertices and
`3` for the segment itself. In this case, this function returns the vector of vectors
`[[1],[2],[1,2,3]]` meaning that vertex `1` is incident with vertex `1` (idem for vertex 2), and that
the segment (id `3`) is incident with the vertices `1` and `2` and the segment itself.

"""
function get_faces(p::Polytope)
  @abstractmethod
end

"""
    get_dimranges(p::Polytope) -> Vector{UnitRange{Int}}

Given a polytope `p` it returns a vector of ranges. The entry `d+1` in this vector
contains the range of face ids for the faces of dimension `d`.

# Examples

```jldoctest
using Gridap.ReferenceFEs

ranges = get_dimranges(SEGMENT)
println(ranges)

# output
UnitRange{Int}[1:2, 3:3]
```
Face ids for the vertices in the segment range from 1 to 2 (2 vertices),
the face ids for edges in the segment range from 3 to 3 (only one edge with id 3).

"""
function get_dimranges(p::Polytope)
  @abstractmethod
end

"""
    get_dimrange(p::Polytope,d::Integer)

Equivalent to

    get_dimranges(p)[d+1]
"""
function get_dimrange(p::Polytope,d::Integer)
  get_dimranges(p)[d+1]
end

"""
    Polytope{N}(p::Polytope,faceid::Integer) where N

Returns a `Polytope{N}` object representing the "reference" polytope of the `N`-face with id `faceid`.
The value `faceid` refers to the numeration restricted to the dimension `N`
(it starts with 1 for the first `N`-face).
"""
function Polytope{D}(p::Polytope,Dfaceid::Integer) where D
  @abstractmethod
end

"""
    get_vertex_coordinates(p::Polytope) -> Vector{Point{D,Float64}}

Given a polytope `p` return a vector of points
representing containing the coordinates of the vertices.
"""
function get_vertex_coordinates(p::Polytope)
  @abstractmethod
end

"""
    (==)(a::Polytope{D},b::Polytope{D}) where D

Returns `true` if the polytopes `a` and `b` are equivalent. Otherwise, it
returns `false`.
Note that the operator `==` returns `false` by default for polytopes
of different dimensions. Thus, this function has to be overloaded only
for the case of polytopes `a` and `b` of same dimension.
"""
function (==)(a::Polytope{D},b::Polytope{D}) where D
  @abstractmethod
end

function (==)(a::Polytope,b::Polytope)
  false
end

# Optional

"""
    get_edge_tangent(p::Polytope) -> Vector{VectorValue{D,Float64}}

Given a polytope `p`, returns a vector of `VectorValue` objects
representing the unit tangent vectors to the polytope edges.
"""
function get_edge_tangent(p::Polytope)
  @abstractmethod
end

"""
    get_facet_normal(p::Polytope) -> Vector{VectorValue{D,Float64}}

Given a polytope `p`, returns a vector of `VectorValue` objects
representing the unit outward normal vectors to the polytope facets.
"""
function get_facet_normal(p::Polytope)
  @abstractmethod
end

"""
    get_facet_orientations(p::Polytope) -> Vector{Int}

Given a polytope `p` returns a vector of integers of length `num_facets(p)`.
Facets, whose vertices are ordered consistently with the
outwards normal vector, receive value `1` in this vector. Otherwise, facets
receive value `-1`.
"""
function get_facet_orientations(p::Polytope)
  @abstractmethod
end

"""
    get_vertex_permutations(p::Polytope) -> Vector{Vector{Int}}

Given a polytope `p`, returns a vector of vectors containing all admissible permutations
of the polytope vertices. An admissible permutation is one such that, if the vertices of the polytope
are re-labeled according to this permutation, the resulting polytope preserves the shape of the
original one.

# Examples

```jldoctest
using Gridap.ReferenceFEs

perms = get_vertex_permutations(SEGMENT)
println(perms)

# output
Array{Int,1}[[1, 2], [2, 1]]

```
The first admissible permutation for a segment is `[1,2]`,i.e., the identity.
The second one is `[2,1]`, i.e., the first vertex is relabeled as `2` and the
second vertex is relabeled as `1`.

"""
function get_vertex_permutations(p::Polytope)
  @abstractmethod
end

"""
    is_simplex(p::Polytope) -> Bool
"""
function is_simplex(p::Polytope)
  @abstractmethod
end

"""
    is_n_cube(p::Polytope) -> Bool
"""
function is_n_cube(p::Polytope)
  @abstractmethod
end

"""
    simplexify(p::Polytope) -> Tuple{Vector{Vector{Int}},Polytope}
"""
function simplexify(p::Polytope)
  @abstractmethod
end

# Some generic API

num_dims(::Type{<:Polytope{D}}) where D = D

num_cell_dims(::Type{<:Polytope{D}}) where D = D

num_point_dims(::Type{<:Polytope{D}}) where D = D

"""
    num_dims(::Type{<:Polytope{D}}) where D
    num_dims(p::Polytope{D}) where D

Returns `D`.
"""
num_dims(p::Polytope) = num_dims(typeof(p))

num_cell_dims(p::Polytope) = num_dims(p)

num_point_dims(p::Polytope) = num_dims(p)

"""
    num_faces(p::Polytope)

Returns the total number of faces in polytope `p` (from vertices to the polytope itself).
"""
function num_faces(p::Polytope)
  length(get_faces(p))
end

"""
    num_faces(p::Polytope,dim::Integer)

Returns the number of faces of dimension `dim` in polytope `p`.
"""
function num_faces(p::Polytope,dim::Integer)
  _num_faces(p,dim)
end

@inline function _num_faces(p,dim)
  length(get_dimranges(p)[dim+1])
end

"""
    num_facets(p::Polytope)

Returns the number of facets in the polytope `p`.
"""
function num_facets(p::Polytope)
  _num_facets(p)
end

@inline function _num_facets(p)
  D = num_dims(p)
  if D > 0
    num_faces(p,D-1)
  else
    0
  end
end

"""
    num_edges(p::Polytope)

Returns the number of edges in the polytope `p`.
"""
function num_edges(p::Polytope)
  _num_edges(p)
end

@inline function _num_edges(p)
  D = num_dims(p)
  if D > 0
    num_faces(p,1)
  else
    0
  end
end

"""
    num_vertices(p::Polytope)

Returns the number of vertices in the polytope `p`.
"""
function num_vertices(p::Polytope)
  _num_vertices(p)
end

@inline function _num_vertices(p)
  num_faces(p,0)
end

"""
    get_facedims(p::Polytope) -> Vector{Int}

Given a polytope `p`, returns a vector indicating
the dimension of each face in the polytope

# Examples

```jldoctest
using Gridap.ReferenceFEs

dims = get_facedims(SEGMENT)
println(dims)

# output
[0, 0, 1]

```

The first two faces in the segment (the two vertices) have dimension 0 and the
third face (the segment itself) has dimension 1

"""
function get_facedims(p::Polytope)
  _get_facedims(Int,p)
end

@inline function _get_facedims(::Type{T},p) where T
  n = num_faces(p)
  facedims = zeros(T,n)
  dimrange = get_dimranges(p)
  for (i,r) in enumerate(dimrange)
    d = i-1
    facedims[r] .= d
  end
  facedims
end

"""
    get_offsets(p::Polytope) -> Vector{Int}

Given a polytope `p`, it returns a vector of integers. The position in
the `d+1` entry in this vector is the offset that transforms a face id in
the global numeration in the polytope to the numeration restricted to faces
to dimension `d`.

# Examples

```jldoctest
using Gridap.ReferenceFEs

offsets = get_offsets(SEGMENT)
println(offsets)

# output
[0, 2]

```
"""
function get_offsets(p::Polytope)
  _get_offsets(p)
end

@inline function _get_offsets(p)
  D = num_dims(p)
  dimrange = get_dimranges(p)
  offsets = zeros(Int,D+1)
  k = 0
  for i in 0:D
    d = i+1
    offsets[d] = k
    r = dimrange[d]
    k += length(r)
  end
  offsets
end

"""
    get_offset(p::Polytope,d::Integer)

Equivalent to `get_offsets(p)[d+1]`.
"""
function get_offset(p::Polytope,d::Integer)
  _get_offset(p,d)
end

@inline function _get_offset(p,d)
  get_offsets(p)[d+1]
end

"""
    get_faces(p::Polytope,dimfrom::Integer,dimto::Integer) -> Vector{Vector{Int}}

For `dimfrom >= dimto` returns a vector that for each face of
dimension `dimfrom` stores a vector of the ids of faces of
dimension `dimto` on its boundary.

For `dimfrom < dimto` returns a vector that for each face of `dimfrom`
stores a vector of the face ids of faces of dimension `dimto` that touch it.

The numerations used in this funcitons are the ones restricted to each dimension.

```jldoctest
using Gridap.ReferenceFEs

edge_to_vertices = get_faces(QUAD,1,0)
println(edge_to_vertices)

vertex_to_edges_around = get_faces(QUAD,0,1)
println(vertex_to_edges_around)

# output
Array{Int,1}[[1, 2], [3, 4], [1, 3], [2, 4]]
Array{Int,1}[[1, 3], [1, 4], [2, 3], [2, 4]]
```
"""
function get_faces(p::Polytope,dimfrom::Integer,dimto::Integer)
  if dimfrom >= dimto
    _get_faces_primal(p,dimfrom,dimto)
  else
    _get_faces_dual(p,dimfrom,dimto)
  end
end

function _get_faces_primal(p,dimfrom,dimto)
  dimrange = get_dimranges(p)
  r = dimrange[dimfrom+1]
  faces = get_faces(p)
  faces_dimfrom = faces[r]
  n = length(faces_dimfrom)
  faces_dimfrom_dimto = Vector{Vector{Int}}(undef,n)
  offset = get_offset(p,dimto)
  facefrom_dimranges = get_face_dimranges(p,dimfrom)
  for i in 1:n
    rto = facefrom_dimranges[i][dimto+1]
    faces_dimfrom_dimto[i] = faces_dimfrom[i][rto].-offset
  end
  faces_dimfrom_dimto
end

function _get_faces_dual(p,dimfrom,dimto)
  tface_to_ffaces = get_faces(p,dimto,dimfrom)
  nffaces = num_faces(p,dimfrom)
  fface_to_tfaces = [Int[] for in in 1:nffaces]
  for (tface,ffaces) in enumerate(tface_to_ffaces)
    for fface in ffaces
      push!(fface_to_tfaces[fface],tface)
    end
  end
  fface_to_tfaces
end

"""
    get_face_dimranges(p::Polytope,d::Integer)
"""
function get_face_dimranges(p::Polytope,d::Integer)
  n = num_faces(p,d)
  rs = Vector{UnitRange{Int}}[]
  for i in 1:n
    f = Polytope{d}(p,i)
    r = get_dimranges(f)
    push!(rs,r)
  end
  rs
end

function get_face_dimranges(p::Polytope)
  rs = Vector{UnitRange{Int}}[]
  D = num_dims(p)
  for b in 0:D
    rs = vcat(rs,get_face_dimranges(p,d))
  end
  rs
end

"""
    get_face_vertices(p::Polytope) -> Vector{Vector{Int}}
    get_face_vertices(p::Polytope,dim::Integer) -> Vector{Vector{Int}}
"""
function get_face_vertices(p::Polytope,dim::Integer)
  get_faces(p,dim,0)
end

function get_face_vertices(p::Polytope)
  face_vertices = Vector{Int}[]
  for d in 0:num_dims(p)
    dface_to_vertices = get_faces(p,d,0)
    for vertices in dface_to_vertices
      push!(face_vertices,vertices)
    end
  end
  face_vertices
end

"""
    get_reffaces(::Type{Polytope{d}},p::Polytope) where d -> Vector{Polytope{d}}

Get a vector of the unique polytopes for the faces of dimension `d`.

# Examples

Get the unique polytopes for the facets of a wedge.

```jldoctest
using Gridap.ReferenceFEs

reffaces = get_reffaces(Polytope{2},WEDGE)

println(reffaces)

# output
Gridap.ReferenceFEs.ExtrusionPolytope{2}[TRI, QUAD]

```

"""
function get_reffaces(::Type{Polytope{d}},p::Polytope) where d
  ftype_to_refface, = _compute_reffaces_and_face_types(p,Val{d}())
  collect(ftype_to_refface)
end

"""
    get_face_type(p::Polytope,d::Integer) -> Vector{Int}

Return a vector of integers denoting, for each face of dimension `d`, an index to the
vector `get_reffaces(Polytope{d},p)`

# Examples

Get the unique polytopes for the facets of a wedge and identify of which
type each face is.

```jldoctest
using Gridap.ReferenceFEs

reffaces = get_reffaces(Polytope{2},WEDGE)

face_types = get_face_type(WEDGE,2)

println(reffaces)
println(face_types)

# output
Gridap.ReferenceFEs.ExtrusionPolytope{2}[TRI, QUAD]
[1, 1, 2, 2, 2]

```

The three first facets are of type `1`, i.e, `QUAD`, and the last ones of type `2`, i.e., `TRI`.

"""
function get_face_type(p::Polytope,d::Integer)
  _, iface_to_ftype = _compute_reffaces_and_face_types(p,Val{d}())
  iface_to_ftype
end

function _compute_reffaces_and_face_types(p::Polytope,::Val{d}) where d
  iface_to_refface = [ Polytope{d}(p,iface) for iface in 1:num_faces(p,d) ]
  _find_unique_with_indices(iface_to_refface)
end

function _find_unique_with_indices(a_to_b)
  T = eltype(a_to_b)
  u_to_b = T[]
  _find_unique!(u_to_b,a_to_b)
  a_to_u = zeros(Int,length(a_to_b))
  _find_indexin!(a_to_u,a_to_b,u_to_b)
  (u_to_b, a_to_u)
end

function _find_unique!(f::Vector,itr,pred::Function=(==))
  for i in itr
    found = false
    for fi in f
      if pred(i,fi)
        found = true
      end
    end
    if !found
      push!(f,i)
    end
  end
  f
end

function _find_indexin!(a_to_index, a_to_b, index_to_b,pred::Function=(==))
  for (a,b) in enumerate(a_to_b)
    for (index,_b) in enumerate(index_to_b)
      if pred(b,_b)
        a_to_index[a] = index
        break
      end
    end
  end
  a_to_index
end

"""
    get_bounding_box(p::Polytope{D}) where D
"""
function get_bounding_box(p::Polytope{D}) where D
  vertex_to_coords = get_vertex_coordinates(p)
  P = eltype(vertex_to_coords)
  T = eltype(P)
  pmin = Point(tfill(T(Inf),Val{D}()))
  pmax = Point(tfill(T(-Inf),Val{D}()))
  for coord in vertex_to_coords
    if coord < pmin
      pmin = coord
    end
    if coord > pmax
      pmax = coord
    end
  end
  (pmin,pmax)
end

"""
    get_face_vertex_permutations(p::Polytope)
    get_face_vertex_permutations(p::Polytope,d::Integer)
"""
function get_face_vertex_permutations(p::Polytope,d::Integer)
  reffaces = [ Polytope{d}(p, iface) for iface in 1:num_faces(p,d)]
  map(get_vertex_permutations,reffaces)
end

function get_face_vertex_permutations(p::Polytope)
  D = num_cell_dims(p)
  p = [ get_face_vertex_permutations(p,d) for d in 0:D ]
  vcat(p...)
end

"""
    get_face_coordinates(p::Polytope,d::Integer)
"""
function get_face_coordinates(p::Polytope,d::Integer)
  vert_to_coord = get_vertex_coordinates(p)
  face_to_vertices = get_faces(p,d,0)
  collect(lazy_map(Broadcasting(Reindex(vert_to_coord)),face_to_vertices))
  # collect(LocalToGlobalArray(face_to_vertices,vert_to_coord))
end

# Testers

"""
    test_polytope(p::Polytope{D}; optional::Bool=false) where D

Function that stresses out the functions in the `Polytope` interface.
It tests whether the function in the polytope interface are defined
for the given object, and whether they return objects of the expected type.
With `optional=false` (the default), only the mandatory functions are checked.
With `optional=true`, the optional functions are also tested.
"""
function test_polytope(p::Polytope{D};optional::Bool=false) where D
  @test D == num_dims(p)
  faces = get_faces(p)
  @test isa(faces,Vector{Vector{Int}})
  @test num_faces(p) == length(faces)
  offsets = get_offsets(p)
  @test isa(offsets,Vector{Int})
  @test length(offsets) == D+1
  dimrange = get_dimranges(p)
  @test isa(dimrange,Vector{UnitRange{Int}})
  @test length(dimrange) == D+1
  @test p == p
  for d in 0:D
    for id in 1:num_faces(p,d)
      pd = Polytope{d}(p,id)
      @test isa(pd,Polytope{d})
    end
  end
  for dimfrom in 0:D
    for dimto in 0:D
      fs = get_faces(p,dimfrom,dimto)
      @test isa(fs,Vector{Vector{Int}})
    end
  end
  x = get_vertex_coordinates(p)
  @test isa(x,Vector{Point{D,Float64}})
  @test length(x) == num_faces(p,0)
  if optional
    fn = get_facet_normal(p)
    @test isa(fn,Vector{VectorValue{D,Float64}})
    @test length(fn) == num_facets(p)
    or = get_facet_orientations(p)
    @test isa(or,Vector{Int})
    @test length(or) == num_facets(p)
    et = get_edge_tangent(p)
    @test isa(et,Vector{VectorValue{D,Float64}})
    @test length(et) == num_edges(p)
    @test isa(is_simplex(p),Bool)
    @test isa(is_n_cube(p),Bool)
  end
end


text:
module Node

mutable struct node
    mass
    radius
    position
    velocity
end

export node
end

max_stars_repo_name: PseudoDevelopers/Physics-Simulations

max_issues_repo_name: PseudoDevelopers/Physics-Simulations

max_forks_repo_name: PseudoDevelopers/Physics-Simulations


text:
#####################
# WebhookEvent Type #
#####################

mutable struct WebhookEvent
    kind::String
    payload::Dict
    repository::Repo
    sender::Owner
end

function event_from_payload!(kind, data::Dict)
    if haskey(data, "repository")
        repository = Repo(data["repository"])
    elseif kind == "membership" ||
           kind == "integration_installation" ||
           kind == "installation" ||
           kind == "installation_repositories" ||
           kind == "integration_installation_repositories"
        repository = Repo("")
    else
        error("event payload is missing repository field")
    end

    if haskey(data, "sender")
        sender = Owner(data["sender"])
    else
        error("event payload is missing sender")
    end

    return WebhookEvent(kind, data, repository, sender)
end

########################
# Validation Functions #
########################

has_event_header(request::HTTP.Request) = HTTP.hasheader(request, "X-Github-Event")
event_header(request::HTTP.Request) = HTTP.header(request, "X-Github-Event")

has_sig_header(request::HTTP.Request) = HTTP.hasheader(request, "X-Hub-Signature")
sig_header(request::HTTP.Request) = HTTP.header(request, "X-Hub-Signature")

function has_valid_secret(request::HTTP.Request, secret)
    if has_sig_header(request)
        secret_sha = "sha1="*bytes2hex(MbedTLS.digest(MbedTLS.MD_SHA1, HTTP.payload(request), secret))
        return sig_header(request) == secret_sha
    end
    return false
end

function is_valid_event(request::HTTP.Request, events)
    return (has_event_header(request) && in(event_header(request), events))
end

function from_valid_repo(event, repos)
    return (name(event.repository) == "" || in(name(event.repository), repos))
end

#################
# EventListener #
#################

struct EventListener
    handle_request
    repos
    events
    function EventListener(handle; auth::Authorization = AnonymousAuth(),
                           secret = nothing, events = nothing,
                           repos = nothing, forwards = nothing)
        if !(isa(forwards, Nothing))
            forwards = map(HTTP.URI, forwards)
        end

        if !(isa(repos, Nothing))
            repos = map(name, repos)
        end

        handle_request = request::HTTP.Request ->
            handle_event_request(request, handle; auth = auth,
                                 secret = secret, events = events,
                                 repos = repos, forwards = forwards)

        return new(handle_request, repos, events)
    end
end

function handle_event_request(request, handle;
                              auth::Authorization = AnonymousAuth(),
                              secret = nothing, events = nothing,
                              repos = nothing, forwards = nothing)
    if !(isa(secret, Nothing)) && !(has_valid_secret(request, secret))
        return HTTP.Response(400, "invalid signature")
    end

    if !(isa(events, Nothing)) && !(is_valid_event(request, events))
        return HTTP.Response(204, "event ignored")
    end

    event = event_from_payload!(event_header(request), JSON.parse(IOBuffer(HTTP.payload(request))))

    if !(isa(repos, Nothing)) && !(from_valid_repo(event, repos))
        return HTTP.Response(400, "invalid repo")
    end

    if !(isa(forwards, Nothing))
        for address in forwards
            HTTP.post(address, request)
        end
    end

    return handle(event)
end

function Base.run(listener, args...; host = nothing, port = nothing, kwargs...)
    if host != nothing || port != nothing
        Base.depwarn("The `host` and `port` keywords are deprecated, use `run(listener, host, port, args...; kwargs...)`", :run)
    end
    run(listener, host, port, args...; kwargs...)
end

function Base.run(listener::EventListener, host::HTTP.IPAddr, port::Int, args...; kwargs...)
    println("Listening for GitHub events sent to $port;")
    println("Whitelisted events: $(isa(listener.events, Nothing) ? "All" : listener.events)")
    println("Whitelisted repos: $(isa(listener.repos, Nothing) ? "All" : listener.repos)")
    HTTP.listen(listener.handle_request, host, port; kwargs...)
end

###################
# CommentListener #
###################

const COMMENT_EVENTS = ["commit_comment",
                        "pull_request",
                        "pull_request_review_comment",
                        "issues",
                        "issue_comment"]

struct CommentListener
    listener::EventListener
    function CommentListener(handle, trigger::Regex;
                             auth::Authorization = AnonymousAuth(),
                             check_collab::Bool = true,
                             secret = nothing,
                             repos = nothing,
                             forwards = nothing)
        listener = EventListener(auth=auth, secret=secret,
                                 events=COMMENT_EVENTS, repos=repos,
                                 forwards=forwards) do event
            return handle_comment(handle, event, auth, trigger, check_collab)
        end
        return new(listener)
    end
end

function Base.run(listener::CommentListener, args...; kwargs...)
    return run(listener.listener, args...; kwargs...)
end

function handle_comment(handle, event::WebhookEvent, auth::Authorization,
                        trigger::Regex, check_collab::Bool)
    kind, payload = event.kind, event.payload

    if (kind == "pull_request" || kind == "issues") && payload["action"] == "opened"
        body_container = kind == "issues" ? payload["issue"] : payload["pull_request"]
    elseif haskey(payload, "comment")
        body_container = payload["comment"]
    else
        return HTTP.Response(204, "payload does not contain comment")
    end

    if check_collab
        repo = event.repository
        user = body_container["user"]["login"]
        if !(iscollaborator(repo, user; auth = auth))
            return HTTP.Response(204, "commenter is not collaborator")
        end
    end

    trigger_match = match(trigger, body_container["body"])

    if trigger_match == nothing
        return HTTP.Response(204, "trigger match not found")
    end

    return handle(event, trigger_match)
end


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

include("../perfutil.jl")

include("axpy.jl")
include("sum_reduce.jl")
include("inner.jl")
include("seismic_fdtd.jl")

max_stars_repo_name: caodi/julia

max_issues_repo_name: caodi/julia

max_forks_repo_name: caodi/julia


text:
using ConservationLawsParticles
using RecursiveArrayTools, DifferentialEquations, Plots

# model
V(t, x) = t < 5 ? one(x) : -one(x)
Wprime(t, x) = sign(x) / (abs(x) + 1)
W(t, x) = log(abs(x) + 1)
mob(ρ) = max(1 - ρ, 0)
smodel = SampledModel((V,), ((Wprime,),), (mob,))
imodel = IntegratedModel((V,), ((W,),), (mob,))

# initial condition
x0 = ArrayPartition(vcat(range(0, 1, length=80)))

# time span
tspan = (0., 10.)

# ODE system for the particles
sprob = ODEProblem(velocities!, x0, tspan, smodel)
iprob = ODEProblem(velocities!, x0, tspan, imodel)

abstol = reltol = 1e-7;
@time ssol = solve(sprob, BS5(); abstol=abstol, reltol=reltol);
@time isol = solve(iprob, BS5(); abstol=abstol, reltol=reltol);

# plot the particle trajectories
plot(title="Trajectories", xlabel="time", ylabel="position", legend=false)
plot!(ssol, vars=1:2:80; color=:blue)
plot!(isol, vars=1:2:80; color=:red)
savefig("simple.png")

# plot an animation of the density
anim = @animate for t in range(tspan...; step=1/48)
    p = plot(title="Density", xlabel="position", ylabel="density",
        legend=false, xlims=(-2,4), ylims=(0,1))
    plot_density!(p, ssol(t); color=:blue)
    plot_density!(p, isol(t); color=:red)
    p
end
gif(anim, "simple.gif")


text:
import Pkg

for pkg in ("Transformations", "ObjectiveFunctions", "StochasticOptimization", "MLMetrics", "PenaltyFunctions", "LearningStrategies")
    org = pkg == "CatViews" ? "ahwillia" : "JuliaML"
	try
		avail = Pkg.available(pkg)
		if !isempty(avail)
			# this is a registered package!
			warn("$pkg is registered in METADATA... it shouldn't be in $(@__FILE__)")
		end
	catch
		# install it
		Pkg.clone("https://github.com/$org/$pkg.jl")
		Pkg.build(pkg)
	end
end


text:
using PkgBenchmark
using DataFrames
using CSV
using Statistics: median
using SymbolicRegression: SymbolicRegression
config = BenchmarkConfig(; juliacmd=`julia -O3`, env=Dict("JULIA_NUM_THREADS" => 4))
_results = benchmarkpkg(SymbolicRegression, config; script="benchmark/.benchmarks.jl")
results = vcat(
    [
        [["evaluation_" * k, median(v.times)] for (k, v) in bigV] for
        (bigK, bigV) in _results.benchmarkgroup.data
    ]...,
)
df = DataFrame(;
    commit=_results.commit,
    name=[results[i][1] for i in 1:length(results)],
    time=[results[i][2] for i in 1:length(results)],
)

CSV.write("output.csv", df; append=true)


text:
using JudiLing
using Test

@testset "make learn sequence" begin
    freq = [1, 2, 3, 4]
    learn_seq = JudiLing.make_learn_seq(freq)

    @test learn_seq == [2, 4, 2, 4, 4, 3, 1, 4, 3, 3]
end

@testset "wh tests" begin
    C = [1 1 1 1 0 0 0 0; 1 0 1 0 1 0 1 0; 0 0 0 0 1 1 1 1]
    S = [1 0 1 0; 1 1 0 0; 0 0 1 1]

    F = JudiLing.wh_learn(
        C,
        S,
        eta = 0.001,
        n_epochs = 10000,
        )
    Shat = C * F
    @test -0.05 < sum(Shat-S) < 0.05

    G = JudiLing.wh_learn(
        S,
        C,
        eta = 0.001,
        n_epochs = 10000,
        )
    Chat = S * G
    @test -0.05 < sum(Chat-C) < 0.05
end


text:
using Distributed, StatsBase, PyPlot

addprocs(4; exeflags="--project")
@everywhere begin

    using TransitionalMCMC, Distributions

    # Prior Bounds
    lb, ub  = -5, 5

    # Prior Density and sampler
    logprior(x) = logpdf(Uniform(lb, ub), x[1]) + logpdf(Uniform(lb, ub), x[2])
    priorRnd(Nsamples) = rand(Uniform(lb, ub), Nsamples, 2)

    # Log Likelihood
    function logLik(x)
        sleep(0.005)
        return -1 * ((x[1]^2 + x[2] - 11)^2 + (x[1] + x[2]^2 - 7).^2)
    end

end

Nsamples = 200

samps, acc = tmcmc(logLik, logprior, priorRnd, Nsamples, 5, 2)

plt.scatter(samps[:,1], samps[:,2])

rmprocs(workers())