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

"""
    create_alarm_model(alarm_model_name, alarm_rule, role_arn)
    create_alarm_model(alarm_model_name, alarm_rule, role_arn, params::Dict{String,<:Any})

Creates an alarm model to monitor an AWS IoT Events input attribute. You can use the alarm
to get notified when the value is outside a specified range. For more information, see
Create an alarm model in the AWS IoT Events Developer Guide.

# Arguments
- `alarm_model_name`: A unique name that helps you identify the alarm model. You can't
  change this name after you create the alarm model.
- `alarm_rule`: Defines when your alarm is invoked.
- `role_arn`: The ARN of the IAM role that allows the alarm to perform actions and access
  AWS resources. For more information, see Amazon Resource Names (ARNs) in the AWS General
  Reference.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"alarmCapabilities"`: Contains the configuration information of alarm state changes.
- `"alarmEventActions"`: Contains information about one or more alarm actions.
- `"alarmModelDescription"`: A description that tells you what the alarm model detects.
- `"alarmNotification"`: Contains information about one or more notification actions.
- `"key"`: An input attribute used as a key to create an alarm. AWS IoT Events routes
  inputs associated with this key to the alarm.
- `"severity"`: A non-negative integer that reflects the severity level of the alarm.
- `"tags"`: A list of key-value pairs that contain metadata for the alarm model. The tags
  help you manage the alarm model. For more information, see Tagging your AWS IoT Events
  resources in the AWS IoT Events Developer Guide. You can create up to 50 tags for one alarm
  model.
"""
function create_alarm_model(
    alarmModelName, alarmRule, roleArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "POST",
        "/alarm-models",
        Dict{String,Any}(
            "alarmModelName" => alarmModelName,
            "alarmRule" => alarmRule,
            "roleArn" => roleArn,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_alarm_model(
    alarmModelName,
    alarmRule,
    roleArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/alarm-models",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "alarmModelName" => alarmModelName,
                    "alarmRule" => alarmRule,
                    "roleArn" => roleArn,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_detector_model(detector_model_definition, detector_model_name, role_arn)
    create_detector_model(detector_model_definition, detector_model_name, role_arn, params::Dict{String,<:Any})

Creates a detector model.

# Arguments
- `detector_model_definition`: Information that defines how the detectors operate.
- `detector_model_name`: The name of the detector model.
- `role_arn`: The ARN of the role that grants permission to AWS IoT Events to perform its
  operations.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"detectorModelDescription"`: A brief description of the detector model.
- `"evaluationMethod"`: Information about the order in which events are evaluated and how
  actions are executed.
- `"key"`: The input attribute key used to identify a device or system to create a detector
  (an instance of the detector model) and then to route each input received to the
  appropriate detector (instance). This parameter uses a JSON-path expression in the message
  payload of each input to specify the attribute-value pair that is used to identify the
  device associated with the input.
- `"tags"`: Metadata that can be used to manage the detector model.
"""
function create_detector_model(
    detectorModelDefinition,
    detectorModelName,
    roleArn;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/detector-models",
        Dict{String,Any}(
            "detectorModelDefinition" => detectorModelDefinition,
            "detectorModelName" => detectorModelName,
            "roleArn" => roleArn,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_detector_model(
    detectorModelDefinition,
    detectorModelName,
    roleArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/detector-models",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "detectorModelDefinition" => detectorModelDefinition,
                    "detectorModelName" => detectorModelName,
                    "roleArn" => roleArn,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_input(input_definition, input_name)
    create_input(input_definition, input_name, params::Dict{String,<:Any})

Creates an input.

# Arguments
- `input_definition`: The definition of the input.
- `input_name`: The name you want to give to the input.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"inputDescription"`: A brief description of the input.
- `"tags"`: Metadata that can be used to manage the input.
"""
function create_input(
    inputDefinition, inputName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "POST",
        "/inputs",
        Dict{String,Any}("inputDefinition" => inputDefinition, "inputName" => inputName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_input(
    inputDefinition,
    inputName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/inputs",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "inputDefinition" => inputDefinition, "inputName" => inputName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an alarm model. Any alarm instances that were created based on this alarm model are
also deleted. This action can't be undone.

# Arguments
- `alarm_model_name`: The name of the alarm model.

"""
function delete_alarm_model(
    alarmModelName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "DELETE",
        "/alarm-models/$(alarmModelName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_alarm_model(
    alarmModelName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "DELETE",
        "/alarm-models/$(alarmModelName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes a detector model. Any active instances of the detector model are also deleted.

# Arguments
- `detector_model_name`: The name of the detector model to be deleted.

"""
function delete_detector_model(
    detectorModelName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "DELETE",
        "/detector-models/$(detectorModelName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_detector_model(
    detectorModelName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "DELETE",
        "/detector-models/$(detectorModelName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an input.

# Arguments
- `input_name`: The name of the input to delete.

"""
function delete_input(inputName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_events(
        "DELETE",
        "/inputs/$(inputName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_input(
    inputName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "DELETE",
        "/inputs/$(inputName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Retrieves information about an alarm model. If you don't specify a value for the
alarmModelVersion parameter, the latest version is returned.

# Arguments
- `alarm_model_name`: The name of the alarm model.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"version"`: The version of the alarm model.
"""
function describe_alarm_model(
    alarmModelName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/alarm-models/$(alarmModelName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_alarm_model(
    alarmModelName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/alarm-models/$(alarmModelName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Describes a detector model. If the version parameter is not specified, information about
the latest version is returned.

# Arguments
- `detector_model_name`: The name of the detector model.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"version"`: The version of the detector model.
"""
function describe_detector_model(
    detectorModelName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/detector-models/$(detectorModelName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_detector_model(
    detectorModelName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/detector-models/$(detectorModelName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Retrieves runtime information about a detector model analysis.  After AWS IoT Events starts
analyzing your detector model, you have up to 24 hours to retrieve the analysis results.

# Arguments
- `analysis_id`: The ID of the analysis result that you want to retrieve.

"""
function describe_detector_model_analysis(
    analysisId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/analysis/detector-models/$(analysisId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_detector_model_analysis(
    analysisId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/analysis/detector-models/$(analysisId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Describes an input.

# Arguments
- `input_name`: The name of the input.

"""
function describe_input(inputName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_events(
        "GET",
        "/inputs/$(inputName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_input(
    inputName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/inputs/$(inputName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Retrieves the current settings of the AWS IoT Events logging options.

"""
function describe_logging_options(; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_events(
        "GET", "/logging"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_logging_options(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET", "/logging", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Retrieves one or more analysis results of the detector model.  After AWS IoT Events starts
analyzing your detector model, you have up to 24 hours to retrieve the analysis results.

# Arguments
- `analysis_id`: The ID of the analysis result that you want to retrieve.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to be returned per request.
- `"nextToken"`: The token that you can use to return the next set of results.
"""
function get_detector_model_analysis_results(
    analysisId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/analysis/detector-models/$(analysisId)/results";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_detector_model_analysis_results(
    analysisId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/analysis/detector-models/$(analysisId)/results",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists all the versions of an alarm model. The operation returns only the metadata
associated with each alarm model version.

# Arguments
- `alarm_model_name`: The name of the alarm model.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to be returned per request.
- `"nextToken"`: The token that you can use to return the next set of results.
"""
function list_alarm_model_versions(
    alarmModelName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/alarm-models/$(alarmModelName)/versions";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_alarm_model_versions(
    alarmModelName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/alarm-models/$(alarmModelName)/versions",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the alarm models that you created. The operation returns only the metadata associated
with each alarm model.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to be returned per request.
- `"nextToken"`: The token that you can use to return the next set of results.
"""
function list_alarm_models(; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_events(
        "GET", "/alarm-models"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_alarm_models(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/alarm-models",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists all the versions of a detector model. Only the metadata associated with each detector
model version is returned.

# Arguments
- `detector_model_name`: The name of the detector model whose versions are returned.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to be returned per request.
- `"nextToken"`: The token that you can use to return the next set of results.
"""
function list_detector_model_versions(
    detectorModelName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/detector-models/$(detectorModelName)/versions";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_detector_model_versions(
    detectorModelName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/detector-models/$(detectorModelName)/versions",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the detector models you have created. Only the metadata associated with each detector
model is returned.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to be returned per request.
- `"nextToken"`: The token that you can use to return the next set of results.
"""
function list_detector_models(; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_events(
        "GET", "/detector-models"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_detector_models(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/detector-models",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Lists one or more input routings.

# Arguments
- `input_identifier`:  The identifer of the routed input.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`:  The maximum number of results to be returned per request.
- `"nextToken"`:  The token that you can use to return the next set of results.
"""
function list_input_routings(
    inputIdentifier; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "POST",
        "/input-routings",
        Dict{String,Any}("inputIdentifier" => inputIdentifier);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_input_routings(
    inputIdentifier,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/input-routings",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("inputIdentifier" => inputIdentifier), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the inputs you have created.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to be returned per request.
- `"nextToken"`: The token that you can use to return the next set of results.
"""
function list_inputs(; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_events(
        "GET", "/inputs"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_inputs(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET", "/inputs", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Lists the tags (metadata) you have assigned to the resource.

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

"""
function list_tags_for_resource(
    resourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "GET",
        "/tags",
        Dict{String,Any}("resourceArn" => resourceArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    resourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "GET",
        "/tags",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("resourceArn" => resourceArn), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Sets or updates the AWS IoT Events logging options. If you update the value of any
loggingOptions field, it takes up to one minute for the change to take effect. If you
change the policy attached to the role you specified in the roleArn field (for example, to
correct an invalid policy), it takes up to five minutes for that change to take effect.

# Arguments
- `logging_options`: The new values of the AWS IoT Events logging options.

"""
function put_logging_options(
    loggingOptions; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "PUT",
        "/logging",
        Dict{String,Any}("loggingOptions" => loggingOptions);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_logging_options(
    loggingOptions,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "PUT",
        "/logging",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("loggingOptions" => loggingOptions), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Performs an analysis of your detector model. For more information, see Troubleshooting a
detector model in the AWS IoT Events Developer Guide.

# Arguments
- `detector_model_definition`:

"""
function start_detector_model_analysis(
    detectorModelDefinition; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "POST",
        "/analysis/detector-models/",
        Dict{String,Any}("detectorModelDefinition" => detectorModelDefinition);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_detector_model_analysis(
    detectorModelDefinition,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/analysis/detector-models/",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("detectorModelDefinition" => detectorModelDefinition),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Adds to or modifies the tags of the given resource. Tags are metadata that can be used to
manage a resource.

# Arguments
- `resource_arn`: The ARN of the resource.
- `tags`: The new or modified tags for the resource.

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

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

Removes the given tags (metadata) from the resource.

# Arguments
- `resource_arn`: The ARN of the resource.
- `tag_keys`: A list of the keys of the tags to be removed from the resource.

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

"""
    update_alarm_model(alarm_model_name, alarm_rule, role_arn)
    update_alarm_model(alarm_model_name, alarm_rule, role_arn, params::Dict{String,<:Any})

Updates an alarm model. Any alarms that were created based on the previous version are
deleted and then created again as new data arrives.

# Arguments
- `alarm_model_name`: The name of the alarm model.
- `alarm_rule`: Defines when your alarm is invoked.
- `role_arn`: The ARN of the IAM role that allows the alarm to perform actions and access
  AWS resources. For more information, see Amazon Resource Names (ARNs) in the AWS General
  Reference.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"alarmCapabilities"`: Contains the configuration information of alarm state changes.
- `"alarmEventActions"`: Contains information about one or more alarm actions.
- `"alarmModelDescription"`: The description of the alarm model.
- `"alarmNotification"`: Contains information about one or more notification actions.
- `"severity"`: A non-negative integer that reflects the severity level of the alarm.
"""
function update_alarm_model(
    alarmModelName, alarmRule, roleArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "POST",
        "/alarm-models/$(alarmModelName)",
        Dict{String,Any}("alarmRule" => alarmRule, "roleArn" => roleArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_alarm_model(
    alarmModelName,
    alarmRule,
    roleArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/alarm-models/$(alarmModelName)",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("alarmRule" => alarmRule, "roleArn" => roleArn),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_detector_model(detector_model_definition, detector_model_name, role_arn)
    update_detector_model(detector_model_definition, detector_model_name, role_arn, params::Dict{String,<:Any})

Updates a detector model. Detectors (instances) spawned by the previous version are deleted
and then re-created as new inputs arrive.

# Arguments
- `detector_model_definition`: Information that defines how a detector operates.
- `detector_model_name`: The name of the detector model that is updated.
- `role_arn`: The ARN of the role that grants permission to AWS IoT Events to perform its
  operations.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"detectorModelDescription"`: A brief description of the detector model.
- `"evaluationMethod"`: Information about the order in which events are evaluated and how
  actions are executed.
"""
function update_detector_model(
    detectorModelDefinition,
    detectorModelName,
    roleArn;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/detector-models/$(detectorModelName)",
        Dict{String,Any}(
            "detectorModelDefinition" => detectorModelDefinition, "roleArn" => roleArn
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_detector_model(
    detectorModelDefinition,
    detectorModelName,
    roleArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "POST",
        "/detector-models/$(detectorModelName)",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "detectorModelDefinition" => detectorModelDefinition,
                    "roleArn" => roleArn,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_input(input_definition, input_name)
    update_input(input_definition, input_name, params::Dict{String,<:Any})

Updates an input.

# Arguments
- `input_definition`: The definition of the input.
- `input_name`: The name of the input you want to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"inputDescription"`: A brief description of the input.
"""
function update_input(
    inputDefinition, inputName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_events(
        "PUT",
        "/inputs/$(inputName)",
        Dict{String,Any}("inputDefinition" => inputDefinition);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_input(
    inputDefinition,
    inputName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_events(
        "PUT",
        "/inputs/$(inputName)",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("inputDefinition" => inputDefinition), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
# FDSN web service specification tests

using Test
import Dates
using Dates: DateTime
using SeisRequests

@testset "FDSNWS" begin
    @testset "FDSNEvent" begin
        # Subtyping
        @test FDSNEvent <: SeisRequests.FDSNRequest
        # Construction
        @test_throws ArgumentError FDSNEvent(starttime=Dates.now(),
                                             endtime=Dates.now()-Dates.Second(1))
        @test_throws ArgumentError FDSNEvent(maxradius=20, longitude=0)
        @test_throws ArgumentError FDSNEvent(limit=-1)
        @test_throws ArgumentError FDSNEvent(offset=-5.0)
        @test_throws ArgumentError FDSNEvent(minlatitude=100)
        @test_throws ArgumentError FDSNEvent(maxlatitude=-100)
        @test_throws ArgumentError FDSNEvent(minlongitude=200)
        @test_throws ArgumentError FDSNEvent(maxlongitude=-181)
        @test_throws ArgumentError FDSNEvent(latitude=100)
        @test_throws ArgumentError FDSNEvent(longitude=360)
        @test_throws ArgumentError FDSNEvent(longitude=0, latitude=0, minradius=-5)
        @test_throws ArgumentError FDSNEvent(longitude=0, latitude=0, maxradius=181)
        @test_throws ArgumentError FDSNEvent(format="weird_format_please")
        @test_throws ArgumentError FDSNEvent(nodata=101)
        # Conversion of strings to dates
        @testset "Date conversion: $f" for f in (:starttime, :endtime, :updatedafter)
            str = "2000-01-02T03:04:05.678"
            dat = DateTime(2000, 1, 2, 3, 4, 5, 678)
            @test FDSNEvent(; f=>str) == FDSNEvent(; f=>dat)
        end
        let req = FDSNEvent()
            for field in fieldnames(FDSNEvent)
                if field == :nodata
                    @test getfield(req, field) == 204
                else
                    @test getfield(req, field) === missing
                end
            end
        end
        # Methods
        let req = FDSNEvent(longitude=45, latitude=15, maxradius=5, minmagnitude=4,
                            starttime=Dates.DateTime(2000),
                            endtime=Dates.DateTime(2000)+Dates.Day(1))
            @test SeisRequests.protocol_string(req) == "fdsnws"
            @test SeisRequests.service_string(req) == "event"
            @test SeisRequests.request_uri(req) ==
                "http://service.iris.edu/fdsnws/event/1/query?" *
                "starttime=2000-01-01T00:00:00&endtime=2000-01-02T00:00:00&" *
                "latitude=15.0&longitude=45.0&maxradius=5.0&minmagnitude=4.0&" *
                "nodata=204"
            # Get request: will fail if IRISWS not working for some reason
            response = get_request(req, verbose=false)
            @test response.status in keys(SeisRequests.STATUS_CODES)
        end
    end

    @testset "FDSNDataSelect" begin
        # Subtyping
        @test FDSNDataSelect <: SeisRequests.FDSNRequest
        # Construction
        @test_throws ArgumentError FDSNDataSelect(starttime=Dates.now(),
                                                  endtime=Dates.now()-Dates.Second(1))
        for field in (:network, :station, :location, :channel)
            @test_throws ArgumentError FDSNDataSelect(; field=>"β is not ASCII")
        end
        @test_throws ArgumentError FDSNDataSelect(quality="A")
        @test_throws ArgumentError FDSNDataSelect(minimumlength=-2.0)
        @test_throws ArgumentError FDSNDataSelect(nodata=101)

        @testset "Code conversion" begin
            @test_throws ArgumentError FDSNDataSelect(code="A.B.C.D", network="A")
            @test_throws ArgumentError FDSNDataSelect(code="A.B.C.D", station="A")
            @test_throws ArgumentError FDSNDataSelect(code="A.B.C.D", location="A")
            @test_throws ArgumentError FDSNDataSelect(code="A.B.C.D", channel="A")
            @test FDSNDataSelect(code="A,B.C*..D") ==
                FDSNDataSelect(network="A,B", station="C*", location="", channel="D")
        end
        # Conversion of strings to dates
        @test FDSNDataSelect(starttime=Dates.DateTime(2000, 01, 01, 01, 02, 03, 456),
            endtime=Dates.DateTime(3000, 03, 04, 05, 06, 07, 890)) ==
            FDSNDataSelect(starttime="2000-01-01T01:02:03.456",
                endtime="3000-03-04T05:06:07.89")
        let req = FDSNDataSelect()
            for field in fieldnames(FDSNDataSelect)
                if field == :nodata
                    @test getfield(req, field) == 204
                else
                    @test getfield(req, field) === missing
                end
            end
        end
        let req = FDSNDataSelect(location="  ")
            @test req.location == "--"
        end
        # Methods
        let req = FDSNDataSelect(network="IU", station="ANMO", location="*",
                                 channel="BH?", starttime=Dates.DateTime(2000),
                                 endtime=Dates.DateTime(2000)+Dates.Second(1))
            @test SeisRequests.protocol_string(req) == "fdsnws"
            @test SeisRequests.service_string(req) == "dataselect"
            @test SeisRequests.request_uri(req) ==
                "http://service.iris.edu/fdsnws/dataselect/1/query?" *
                "starttime=2000-01-01T00:00:00&endtime=2000-01-01T00:00:01&" *
                "network=IU&station=ANMO&location=*&channel=BH?&nodata=204"
            # Get request: will fail if IRISWS not working for some reason
            response = get_request(req, verbose=false)
            @test response.status in keys(SeisRequests.STATUS_CODES)
        end
    end

    @testset "FDSNStation" begin
        # Subtyping
        @test FDSNStation <: SeisRequests.FDSNRequest
        # Construction
        @test_throws ArgumentError FDSNStation(starttime=Dates.now(),
                                               endtime=Dates.now()-Dates.Second(1))
        @test_throws ArgumentError FDSNStation(maxradius=20, longitude=0)
        @test_throws ArgumentError FDSNStation(minlatitude=100)
        @test_throws ArgumentError FDSNStation(maxlatitude=-100)
        @test_throws ArgumentError FDSNStation(minlongitude=200)
        @test_throws ArgumentError FDSNStation(maxlongitude=-181)
        @test_throws ArgumentError FDSNStation(latitude=100)
        @test_throws ArgumentError FDSNStation(longitude=360)
        @test_throws ArgumentError FDSNStation(longitude=0, latitude=0, minradius=-5)
        @test_throws ArgumentError FDSNStation(longitude=0, latitude=0, maxradius=181)
        @test_throws ArgumentError FDSNStation(level="weird_level_please")
        @test_throws ArgumentError FDSNStation(nodata=101)

        @testset "Code conversion" begin
            @test_throws ArgumentError FDSNStation(code="A.B.C.D", network="A")
            @test_throws ArgumentError FDSNStation(code="A.B.C.D", station="A")
            @test_throws ArgumentError FDSNStation(code="A.B.C.D", location="A")
            @test_throws ArgumentError FDSNStation(code="A.B.C.D", channel="A")
            @test FDSNStation(code="A,B.C*..D") ==
                FDSNStation(network="A,B", station="C*", location="", channel="D")
        end

        # Conversion of strings to dates
        @testset "Date conversion: $f" for f in (:starttime, :endtime, :startbefore,
                :endbefore, :startafter, :endafter, :updatedafter)
            str = "2000-01-02T03:04:05.678"
            dat = DateTime(2000, 1, 2, 3, 4, 5, 678)
            @test FDSNStation(; f=>str) == FDSNStation(; f=>dat)
        end

        for field in (:network, :station, :location, :channel)
            @test_throws ArgumentError FDSNStation(; field=>"β is not ASCII")
        end
        let req = FDSNStation()
            for field in fieldnames(FDSNStation)
                if field == :nodata
                    @test getfield(req, field) == 204
                else
                    @test getfield(req, field) === missing
                end
            end
        end
        @testset "Default level" begin
            @test FDSNStation(network="A", station="B").level === missing
            @test FDSNStation(channel="X").level == "channel"
        end
        # Methods
        let req = FDSNStation(network="GB", station="JSA", location="--", channel="?H?",
                              level="channel", format="text")
            @test SeisRequests.protocol_string(req) == "fdsnws"
            @test SeisRequests.service_string(req) == "station"
            @test SeisRequests.request_uri(req) ==
                "http://service.iris.edu/fdsnws/station/1/query?" *
                "network=GB&station=JSA&location=--&channel=?H?&level=channel&" *
                "format=text&nodata=204"
            # Get request: will fail if IRISWS not working for some reason
            response = get_request(req, verbose=false)
            @test response.status in keys(SeisRequests.STATUS_CODES)
        end
    end

    @testset "FDSN text formats" begin
        # Correct parsing, including potentially missing end date
        @testset "FDSNNetworkTextResponse" begin
            @test convert(SeisRequests.FDSNNetworkTextResponse,
                "II|Global Seismograph Network (GSN - IRIS/IDA)|1986-01-01T00:00:00|2500-12-12T23:59:59|50") ==
                SeisRequests.FDSNNetworkTextResponse("II",
                    "Global Seismograph Network (GSN - IRIS/IDA)",
                    DateTime(1986), DateTime(2500, 12, 12, 23, 59, 59), 50)
            @test convert(SeisRequests.FDSNNetworkTextResponse,
                "A|B|2000-01-01||0") ==
                SeisRequests.FDSNNetworkTextResponse("A", "B", DateTime(2000), missing, 0)
        end
        @testset "FDSNStationTextResponse" begin
            @test convert(SeisRequests.FDSNStationTextResponse,
                "IU|ANMO|34.9459|-106.4572|1850.0|Albuquerque, New Mexico, USA|1989-08-29T00:00:00|1995-07-14T00:00:00") ==
                SeisRequests.FDSNStationTextResponse("IU", "ANMO", 34.9459,
                    -106.4572, 1850.0, "Albuquerque, New Mexico, USA",
                    DateTime(1989, 08, 29), DateTime(1995, 07, 14))
            @test convert(SeisRequests.FDSNStationTextResponse,
                "A|B|1|2|3|X|2000-01-01|") ==
                SeisRequests.FDSNStationTextResponse("A", "B", 1, 2, 3, "X",
                    DateTime(2000), missing)

        end
        @testset "FDSNChannelTextResponse" begin
            @test convert(SeisRequests.FDSNChannelTextResponse,
                "IU|COLA|20|HNE|64.873599|-147.8616|200.0|0.0|90.0|0.0|Kinemetrics FBA-23 Low-GainSensor|53687.1|1.0|M/S**2|80.0|2005-09-28T22:00:00|2009-07-08T22:00:00") ==
                SeisRequests.FDSNChannelTextResponse("IU", "COLA", "20", "HNE",
                    64.873599, -147.8616, 200.0, 0.0, 90.0, 0.0,
                    "Kinemetrics FBA-23 Low-GainSensor", 53687.1,
                    1.0, "M/S**2", 80.0, DateTime(2005, 9, 28, 22),
                    DateTime(2009, 7, 8, 22))
            @test convert(SeisRequests.FDSNChannelTextResponse,
                "A|B|C|D|1|2|3|4|5|6|E|7|8|F|9|2000-01-01|") ==
                SeisRequests.FDSNChannelTextResponse("A", "B", "C", "D",
                    1, 2, 3, 4, 5, 6, "E", 7, 8, "F", 9, DateTime(2000), missing)
            @test convert(SeisRequests.FDSNChannelTextResponse,
                "XB|ELYSE|02|BDO|4.502384|135.623447|-2613.4|-0.1|90.0|0.0|PRESSURE||||20.0|2018-12-20T07:34:25|2022-12-31T23:59:59") ==
                SeisRequests.FDSNChannelTextResponse("XB", "ELYSE", "02", "BDO",
                    4.502384, 135.623447, -2613.4, -0.1, 90, 0, "PRESSURE", missing,
                    missing, missing, 20, DateTime(2018, 12, 20, 7, 34, 25),
                    DateTime(2022, 12, 31, 23, 59, 59))
        end
        @testset "FDSNEventTextResponse" begin
            @test convert(SeisRequests.FDSNEventTextResponse,
                "usp000jv5f|2012-11-07T16:35:46.930|13.988|-91.895|24|us|us|us|usp000jv5f|mww|7.4|us|offshore Guatemala|earthquake") ==
                SeisRequests.FDSNEventTextResponse("usp000jv5f",
                    DateTime(2012, 11, 7, 16, 35, 46, 930), 13.988, -91.895,
                    24.0, "us", "us", "us", "usp000jv5f", "mww", 7.4, "us",
                    "offshore Guatemala", "earthquake")
            @test convert(SeisRequests.FDSNEventTextResponse,
                "usp000jv5f|2012-11-07T16:35:46.930|13.988|-91.895|24|us|us|us|usp000jv5f|mww|7.4|us|offshore Guatemala") ==
                SeisRequests.FDSNEventTextResponse("usp000jv5f",
                    DateTime(2012, 11, 7, 16, 35, 46, 930), 13.988, -91.895,
                    24.0, "us", "us", "us", "usp000jv5f", "mww", 7.4, "us",
                    "offshore Guatemala", missing)
        end
        @testset "Error throwing" begin
            # Field counting
            @test SeisRequests.text_response_tokens_and_throw(Int, "1|2", 2) == ["1", "2"]
            # Multiple permissible number of fields
            @test SeisRequests.text_response_tokens_and_throw(Float32, "1|2|3", (3,4)) == ["1", "2", "3"]
            @test_throws ArgumentError SeisRequests.text_response_tokens_and_throw(Int, "1|2", 3)
            # Too few/many fields
            @test_throws ArgumentError convert(SeisRequests.FDSNNetworkTextResponse,
                "SY|Desc|2000-01-01T|2000-01-02T|10|__XXX__")
            @test_throws ArgumentError convert(SeisRequests.FDSNNetworkTextResponse,
                "SY|Desc|2000-01-01T|2000-01-02T")
            @test_throws ArgumentError convert(SeisRequests.FDSNStationTextResponse,
                "SY|STA|0.0|0.0|1000.0|Desc.|2000-01-01T|2001-01-01T|__XXX__")
            @test_throws ArgumentError convert(SeisRequests.FDSNStationTextResponse,
                "SY|STA|0.0|0.0|1000.0|Desc.|2000-01-01T")
            @test_throws ArgumentError convert(SeisRequests.FDSNChannelTextResponse,
                "SY|STA|00|LHZ|1|2|300|0|0|90|Seismometer|1234|2|M/S|-2|2000-01-01T")
            @test_throws ArgumentError convert(SeisRequests.FDSNChannelTextResponse,
                "SY|STA|00|LHZ|1|2|300|0|0|90|Seismometer|1234|2|M/S|-2|2000-01-01T|2001-01-01T|__XXX__")
            @test_throws ArgumentError convert(SeisRequests.FDSNEventTextResponse,
                "usp000jv5f|2012-11-07T16:35:46.930|13.988|-91.895|24|us|us|us|usp000jv5f|mww|7.4|us|offshoreGuatemala|earthquake|__XXX__")
            @test_throws ArgumentError convert(SeisRequests.FDSNEventTextResponse,
                "usp000jv5f|2012-11-07T16:35:46.930|13.988|-91.895|24|us|us|us|usp000jv5f|mww|7.4|us")
            # Wrong type of field
            @test_throws ArgumentError convert(SeisRequests.FDSNNetworkTextResponse,
                "SY|Desc|2000-01-01T|2000-01-02T|__XXX__")
            @test_throws ArgumentError convert(SeisRequests.FDSNStationTextResponse,
                "SY|STA|__XXX__|0.0|1000.0|Desc.|2000-01-01T|2001-01-01T")
            @test_throws ArgumentError convert(SeisRequests.FDSNChannelTextResponse,
                "SY|STA|00|LHZ|__XXX__|2|300|0|0|90|Seismometer|1234|2|M/S|-2|2000-01-01T|2001-01-01T")
            @test_throws ArgumentError convert(SeisRequests.FDSNEventTextResponse,
                "usp000jv5f|2012-11-07T16:35:46.930|__XXX__|-91.895|24|us|us|us|usp000jv5f|mww|7.4|us|earthquake")
        end
    end
end

ext: jl


text:
module JLLShim
    using Clang_jll
    using libclangex_jll
    using Libdl

    function __init__()
        if Clang_jll.libclang_cpp_handle == C_NULL
            global libclang_cpp_handle = Libdl.dlopen(Clang_jll.libclang_cpp_path, RTLD_LAZY | RTLD_DEEPBIND)
            global libclangex_handle = Libdl.dlopen(libclangex_jll.libclangex_path, RTLD_LAZY | RTLD_DEEPBIND)
        end
    end
end


text:
# Sectors giving rise to super vector spaces, which have at least a Z2 grading
#==============================================================================#
abstract type Fermion <: Sector end

Base.@pure braidingtype(::Type{<:Fermion}) = Fermionic

# FermionParity: only total fermion parity is conserved
struct FermionParity <: Fermion
    parity::Bool
end
FermionParity(p::Int) = FermionParity(isodd(p))
Base.one(::Type{FermionParity}) = FermionParity(false)
Base.conj(p::FermionParity) = p
⊗(p1::FermionParity, p2::FermionParity) = (FermionParity(p1.parity != p2.parity),)

Base.@pure fusiontype(::Type{FermionParity}) = Abelian

const fℤ₂ = FermionParity

Base.show(io::IO, ::Type{FermionParity}) = print(io, "fℤ₂")
Base.show(io::IO, p::FermionParity) = get(io, :compact, false) ? print(io, Int(p.parity)) : print(io, "fℤ₂(", Int(p.parity), ")")

# FermionNumber: total number of fermions is conserved -> U1 symmetry
struct FermionNumber <: Fermion
    num::Int
end
Base.one(::Type{FermionNumber}) = FermionNumber(0)
Base.conj(n::FermionNumber) = FermionNumber(n.num)
⊗(n1::FermionNumber, n2::FermionNumber) = (FermionNumber(n1.num+n2.num),)

Base.@pure fusiontype(::Type{FermionNumber}) = Abelian

const fU₁ = FermionNumber

Base.show(io::IO, ::Type{FermionNumber}) = print(io, "fU₁")
Base.show(io::IO, n::FermionNumber) = get(io, :compact, false) ? print(io, n.num) : print(io, "fU₁(", n.num, ")")

const AbelianFermion = Union{FermionParity,FermionNumber}
Nsymbol(a::G, b::G, c::G) where {G<:AbelianFermion} = c == first(a ⊗ b)
Fsymbol(a::G, b::G, c::G, d::G, e::G, f::G) where {G<:AbelianFermion} =
    Int(Nsymbol(a,b,e)*Nsymbol(e,c,d)*Nsymbol(b,c,f)*Nsymbol(a,f,d))
Rsymbol(a::G, b::G, c::G) where {G<:AbelianFermion} = fermionparity(a) && fermionparity(b) ? -Int(Nsymbol(a, b, c)) : Int(Nsymbol(a, b, c))

struct FermionSpin <: Fermion
    dim::Int
    FermionSpin(j::Int) = j >= 0 ? new(2*j+1) : throw(SU2IrrepException)
    function FermionSpin(j::Rational{Int})
        if j.den == 2
            new(j.num+1)
        elseif j.den == 1
            new(2*j.num+1)
        else
            throw(SU2IrrepException)
        end
    end
end
_getj(s::FermionSpin) = (s.dim-1)//2
Base.one(::Type{FermionSpin}) = FermionSpin(0)
Base.conj(s::FermionSpin) = s
⊗(s1::FermionSpin, s2::FermionSpin) = SectorSet{FermionSpin}( abs(_getj(s1)-_getj(s2)):(_getj(s1)+_getj(s2)) )

Base.@pure fusiontype(::Type{FermionSpin}) = SimpleNonAbelian

Nsymbol(s1::FermionSpin, s2::FermionSpin, s::FermionSpin) = (abs(s1.dim-s2.dim)+1) <= s.dim <= (s1.dim+s2.dim-1) && isodd(s1.dim+s2.dim - s.dim)

# TODO: Fsymbol -> 6j-symbols of SU(2), compute, cache, ... -> Recycle those of SU2Irrep

const fSU₂ = FermionSpin
Base.show(io::IO, ::Type{FermionSpin}) = print(io, "fSU₂")
Base.show(io::IO, s::FermionSpin) = get(io, :compact, false) ? print(io, _getj(s)) : print(io, "fSU₂(", _getj(s), ")")

"""
    function fermionparity(s::Fermion) -> Bool

Returns the fermion parity of a sector `s` that is a subtype of `Fermion`, as a
`Bool` being true if odd and false if even.
"""
fermionparity(p::FermionParity) = p.parity
fermionparity(n::FermionNumber) = isodd(n.num)
fermionparity(s::FermionSpin) = iseven(s.dim)


text:
using ClimateMachine.Orientations

import ClimateMachine.Orientations: vertical_unit_vector

function orientation_gradient(model::AbstractFluidModel, ::Orientation, state_auxiliary, grid, direction)
    auxiliary_field_gradient!(
        model,
        state_auxiliary,
        ("orientation.∇Φ",),
        state_auxiliary,
        ("orientation.Φ",),
        grid,
        direction,
    )

    return nothing
end

function orientation_gradient(::AbstractFluidModel, ::NoOrientation, _...)
    return nothing
end

function orientation_nodal_init_aux!(
    ::NoOrientation,
    # domain::AbstractDomain,
    aux::Vars,
    geom::LocalGeometry,
)
    return nothing
end

function orientation_nodal_init_aux!(
    ::FlatOrientation,
    # domain::AbstractDomain,
    aux::Vars,
    geom::LocalGeometry,
)
    @inbounds aux.orientation.Φ = geom.coord[3]

    return nothing
end

function orientation_nodal_init_aux!(
    ::SphericalOrientation,
    # domain::AbstractDomain,
    aux::Vars,
    geom::LocalGeometry,
)
    norm_R = norm(geom.coord)
    # @inbounds aux.orientation.Φ = norm_R - domain.radius
    @inbounds aux.orientation.Φ = norm_R

    return nothing
end

@inline vertical_unit_vector(::Orientation, aux) = aux.orientation.∇Φ
@inline vertical_unit_vector(::NoOrientation, aux) = @SVector [0, 0, 1]

max_stars_repo_name: CliMA/CouplerMachine

max_issues_repo_name: CliMA/CouplerMachine


text:
struct Figure7{T}
    sim::Vector{T}
    parameternames::Vector{String}
    truncation::Vector{Tuple{Float64,Float64}}
    truncate::Bool
    function Figure7(;truncate=false)
        @load "dat/simulation.jld"
        truncation = [
            (-0.7,1.3),
            (-0.1,0.1),
            (-1,2.7),
            (-2,3)
        ]
        new{eltype(sim)}(sim,makeparameternames(),truncation,truncate)
    end
end

@recipe function f(f::Figure7)
    count = 1
    goodindex = [s.DWflag .& s.DWuniflag .& s.LSmlmflag .& s.LSmemflag .& s.Mflag for s in f.sim]
    for (i,g) in enumerate(goodindex)
        if sum(g) != length(g)
            @warn "From scenario $i, some results failed. They were
                DW:     $(length(g)-sum(f.sim[i].DWflag))
                DW uni: $(length(g)-sum(f.sim[i].DWuniflag))
                LS mlm: $(length(g)-sum(f.sim[i].LSmlmflag))
                LS mem: $(length(g)-sum(f.sim[i].LSmemflag))
                moment: $(length(g)-sum(f.sim[i].Mflag))
            As a result, only $(sum(g)) results are used in the figure for Scenario $i.
            "
        end
    end
    color_palette := Main.Plots.palette([:royalblue, :green, :darkorange])
    layout := (4, 3)
    x = ["LS" "DW uni" "DW"]
    for i ∈ 1:4, k ∈ 1:3
        y = [getindex.(getfield(f.sim[k],t),i)[goodindex[k]] for t in [:LSmlm, :DWuni, :DW]]
        # @series begin
        #     seriestype := :violin
        #     subplot := count
        #     label := false
        #     x, y
        # end
        @series begin
            seriestype := :boxplot
            subplot := count
            label := false
            x, y
        end
        @series begin
            seriestype := :hline
            subplot := count
            seriescolor := :red
            linestyle := :dash
            label := false
            if k == 1
                yguide := L"%$(f.parameternames[i])"
                yguide_position := :left
            end
            if count ∈ 1:3
                xguide := "Scenario $k"
                xguide_position := :top
            end
            if f.truncate
                ylims := f.sim[k].θ_true[i] .+ f.truncation[i]
            end
            f.sim[k].θ_true[i:i]
        end
        count += 1
    end
end


text:
# Code example to use the polynomial chaos toolbox.
# Function approximation

include("./PolynomialChaos.jl");

# Example 1
Z = symVec("z",2);
PDF = 0.5^length(Z);
rangeZ = repeat([-1 1], length(Z));
Phi = getBasis("Legendre",Z,5);

@time M1_Phi = innerProduct(Phi*PDF,Z,rangeZ);
@time M2_Phi = innerProduct(Phi.*Phi*PDF,Z,rangeZ);
@time W = innerProduct(Phi*Phi'*PDF,Z,rangeZ);
println("Done")

# Example 2 -- More complicated functions
Z = symVec("z",1);
PDF = 0.5^length(Z);
rangeZ = repeat([-1 1], length(Z));
Phi = getBasis("Legendre",Z,3);
f = sin(Z[1]^3);
@time X = innerProduct(f*Phi*0.5,Z,rangeZ); # Really slow -- try non intrusive.
fhat = X'*Phi;
Xval = -1:0.01:1;
f1val = [subs(fhat,Z[1]=>x) for x in Xval];
fval =  [subs(f,Z[1]=>x) for x in Xval];

using PyPlot
plot(Xval,fval);
p = plot(Xval,f1val);
legend(["f","fhat"]);

max_stars_repo_name: uqLab/Polynomial-Chaos

max_issues_repo_name: uqLab/Polynomial-Chaos

max_forks_repo_name: uqLab/Polynomial-Chaos


text:
function check_imports(pkgdir;
        subs=["src", "scripts"]
    )

    ## ---------------------------------------------------
    file_imports = JLAssistant.find_imports(pkgdir; subs)
    proj_imports = vcat(collect.(values(file_imports))...)

    ## ---------------------------------------------------
    # Project.toml deps
    projfiles = joinpath.(pkgdir, Base.project_names)
    projis = findfirst(isfile, projfiles)
    isnothing(projis) && error("Project file not found")
    projfile = projfiles[projis]
    proj_toml = JLAssistant._load_project(projfile)
    proj_deps = get(proj_toml, "deps", String[""]) |> keys |> collect

    ## ---------------------------------------------------
    IGNORED = ["Base", "Core", get(proj_toml, "name", "")]

    ## ---------------------------------------------------
    # print

    ## ---------------------------------------------------
    println("\nChecking Imports")
    println("proj: ", pkgdir, "\n")
    pad_len = maximum(length.(proj_imports))
    pad_len = max(pad_len, maximum(length.(proj_deps)))
    pad_len = max(pad_len, length("in Project.toml"))
    pad_len += 3 #
    TAB = "  "

    ## ---------------------------------------------------
    missings_pool = Dict()
    for (file, imports) in file_imports
        imports = collect(imports)

        println(file, ":")
        isempty(imports) && (println(); continue)
        sort!(imports)
        println(TAB, "  ",
            rpad("to load", pad_len), rpad("in Project.toml", pad_len)
        )
        println(TAB, "  ",
            rpad("", pad_len, "-"), rpad("", pad_len, "-")
        )

        missings = String[]
        for imp in imports
            isignored = (imp in IGNORED)
            isdep = (imp in proj_deps)
            mark = (isignored || isdep) ? "  " : "* "
            label = isdep ? "present" :
                    isignored ? "ignored" : "missing"
            color = (isignored || isdep) ? :normal : :red
            printstyled(TAB, mark,
                rpad(imp, pad_len),
                rpad(label, pad_len);
                color, bold = (color == :red)
            )
            !isignored && !isdep && push!(missings, imp)
            println()
        end
        println()
        if !isempty(missings)
            println(TAB, "Missings: ", join(missings, ", "))
            println()
        end

        missings_pool[file] = missings
    end
    println()

    ## ---------------------------------------------------
    nonloaded = filter((dep)->!(dep in proj_imports), sort!(proj_deps))
    if !isempty(nonloaded)
        println("Fake deps: ")
        println(TAB, "  ",
            rpad("in Project.toml", pad_len),
            rpad("loaded", pad_len)
        )
        println(TAB, "  ",
            rpad("", pad_len, "-"), rpad("", pad_len, "-")
        )
        for dep in nonloaded
            printstyled(TAB, "* ",
                rpad(dep, pad_len),
                rpad("no", pad_len);
                color = :red, bold = true
            )
            println()
        end
        println()
        println(TAB, "All fakes: ", join(nonloaded, ", "))
    end

    ## ---------------------------------------------------
    # unregistered pkgs
    # check Manifest
    unreg_pkgs = _find_unregistered_pkgs(pkgdir)
    if !isempty(unreg_pkgs)
        println()
        println("In unregistered mode: ")
        printstyled(TAB, join(unreg_pkgs, ", "); color = :red, bold = true)
        println()
    end

    ## ---------------------------------------------------
    # fix command
    SINGLE_QUOTE = "\'"
    QUOTE = "\""
    BACK_SLASH = "\\"
    ENTER = "\n"
    COMMA = ","

    add_strs = []
    for (file, missings) in missings_pool
        isempty(missings) && continue
        add_str = string(
            "@info(", QUOTE, "Fixing ", file, QUOTE, ")", ENTER,
            "Pkg.add(",
                string("[", join(string.(QUOTE, missings, QUOTE), ", "), "]"),
            ")"

        )
        push!(add_strs, add_str)
    end
    add_str = join(add_strs, ENTER)

    rm_str = isempty(nonloaded) ? "" :
        string(
            "@info(", QUOTE, "Removing fake deps", QUOTE, ")", ENTER,
            "Pkg.rm(",
                string("[", join(string.(QUOTE, nonloaded, QUOTE), ", "), "]"),
            ")"
        )

    if !isempty(add_str) || !isempty(rm_str)
        fix_cmd = string(
            "julia", " --project=", pkgdir, " -e ", BACK_SLASH, ENTER,
            SINGLE_QUOTE,
            "import Pkg;", ENTER,
            add_str, ENTER,
            rm_str, ENTER,
            "Pkg.precompile()",
            SINGLE_QUOTE
        )
        println()
        println("Fix command:")
        println()
        println(fix_cmd)
    end

end

## ---------------------------------------------------------
function run_check_imports(argv=ARGS)

    ## ---------------------------------------------------------
    argset = ArgParse.ArgParseSettings()
    ArgParse.@add_arg_table! argset begin
        "pkgdir"
            help = "The package dir"
            arg_type = String
            default = pwd()
        "--subfolders", "-s"
            help = "The names (comma separated) of the subfolders to search"
            arg_type = String
            default = "src, scripts"
    end

    parsed_args = ArgParse.parse_args(argv, argset)
    pkgdir = parsed_args["pkgdir"]
    subs = _split_arglist(parsed_args["subfolders"])

    check_imports(pkgdir; subs)
end


text:
using Bridge, StaticArrays
using Test

n = 5000
T = (0.0, 10.0)
dt = (T[end]-T[1])/n
tt = collect(range(T[1], stop=T[2], length=n+1))
vx = [1.0,0.0,0.0]

const SV3 = SVector{3, Float64}

mutable struct VLorenz <: ContinuousTimeProcess{Vector{Float64}}
end

Bridge.b(t, u, ::VLorenz) = [10.0(u[2]-u[1]), u[1]*(28.0-u[3]) - u[2], u[1]*u[2] - (8/3)*u[3]]
function Bridge.b!(t, u, du, ::VLorenz)
    @inbounds du[1] = 10.0(u[2]-u[1])
    @inbounds du[2] = u[1]*(28.0-u[3]) - u[2]
    @inbounds du[3] = u[1]*u[2] - (8/3)*u[3]
end
Bridge.σ(t, u, ::VLorenz) = 3.0I
function Bridge.σ!(t, u, dw, dm, ::VLorenz)
    @inbounds dm[1] = 3*dw[1]
    @inbounds dm[2] = 3*dw[2]
    @inbounds dm[3] = 3*dw[3]
end

W = sample(tt, Wiener{SV3}())
VW = VSamplePath(tt, collect(Bridge.mat(W.yy)))
VX = VSamplePath(tt, copy(VW.yy))
VX2 = VSamplePath(tt, copy(VW.yy))
solve!(EulerMaruyama!(), VX, vx, VW, VLorenz())
solve!(EulerMaruyama(), VX2, vx, VW, VLorenz())

solve(EulerMaruyama!(), zeros(3), VW, Wiener())

######

mutable struct Lorenz <: ContinuousTimeProcess{SV3}
end

Bridge.b(s, u, ::Lorenz) = @SVector [10.0(u[2]-u[1]), u[1]*(28.0-u[3]) - u[2], u[1]*u[2] - (8/3)*u[3]           ]
Bridge.σ(t, u, ::Lorenz) = 3.0I

u = SVector{3}(vx)
X = solve(EulerMaruyama(), u, W, Lorenz())
X2 = copy(X)
X3 = copy(X)

solve!(Euler(), X, u, W, Lorenz())
solve!(EulerMaruyama(), X2, u, W, Lorenz())
invoke(solve!, Tuple{EulerMaruyama,Any,SVector{3,Float64},Bridge.AbstractPath,Lorenz},
EulerMaruyama(), X3, u, W, Lorenz())

@test maximum(abs.(VW.yy - Bridge.mat(W.yy))) < eps()

@test maximum(abs.(VX.yy - Bridge.mat(X.yy))) < eps()
@test maximum(abs.(VX.yy - VX2.yy)) < eps()
@test maximum(norm.(X.yy - X2.yy)) < eps()
@test maximum(norm.(X.yy - X3.yy)) < eps()


text:
function insideif(f, ctx::LintContext)
    ctx.ifdepth += 1
    f(ctx)
    ctx.ifdepth -= 1
end

function lintifexpr(ex::Expr, ctx::LintContext)
    if ex.args[1] == false
        msg(ctx, :W642, "true branch is unreachable")
        if length(ex.args) > 2
            insideif(x -> lintexpr(ex.args[3], x), ctx)
        end
    elseif ex.args[1] == true
        insideif(x -> lintexpr(ex.args[2], x), ctx)
        if length(ex.args) > 2
            msg(ctx, :W643, "false branch is unreachable")
        else
            msg(ctx, :W644, "redundant if-true statement")
        end
    else
        lintboolean(ex.args[1], ctx)
        # if the first "meaty" expression under the true branch is
        # a boolean expression (!=, ==, >=, <=, >, <, &&, ||),
        # generate a INFO, as it could have been a typo
        if isexpr(ex.args[2], :block) && length(ex.args[2].args) >= 2 &&
                (isexpr(ex.args[2].args[2], :comparison) ||
                isexpr(ex.args[2].args[2], :call) &&
                   ( ex.args[2].args[2].args[1] == :(!) || ex.args[2].args[2].args[1] in COMPARISON_OPS ) ||
                isexpr(ex.args[2].args[2], [:(&&), :(||)]) &&
                !isexpr(ex.args[2].args[2].args[end], [:call, :error, :throw, :return]))
            msg(ctx, :I571, "the 1st statement under the true-branch is a boolean expression")
        end
        (verconstraint1, verconstraint2) = versionconstraint(ex.args[1])
        if verconstraint1 != nothing
            tmpvtest = ctx.versionreachable
            ctx.versionreachable = x->(tmpvtest(x) && verconstraint1(x))
        end
        insideif(x -> lintexpr(ex.args[2], x), ctx)
        if verconstraint1 != nothing
            ctx.versionreachable = tmpvtest
        end
        if length(ex.args) > 2
            if verconstraint2 != nothing
                tmpvtest = ctx.versionreachable
                ctx.versionreachable = x->(tmpvtest(x) && verconstraint2(x))
            end
            insideif(x -> lintexpr(ex.args[3], x), ctx)
            if verconstraint2 != nothing
                ctx.versionreachable = tmpvtest
            end
        end
    end
end

# return a duplet of functions, the true branch version predicate and the false-branch version predicate
# if none exists, return (nothing, nothing)
function versionconstraint(ex)
    if isexpr(ex, :call) && ex.args[1] in COMPARISON_OPS
        if length(ex.args) == 3
            isversionfirst = :VERSION == ex.args[2]
            constraint = simplify_literal(
                isversionfirst ? ex.args[3] : ex.args[2])
            if isa(constraint, VersionNumber)
                pred = ver -> getfield(Base, ex.args[1])(
                    isversionfirst ? ver : constraint,
                    isversionfirst ? constraint : ver)
                return (pred, !pred)
            end
        end
        return (nothing, nothing)
    elseif isexpr(ex, :comparison)
        return versionconstraint(split_comparison(ex))
    elseif isexpr(ex, :(&&))
        vc1 = versionconstraint(ex.args[1])
        vc2 = versionconstraint(ex.args[2])
        arr = Any[]
        if vc1[1] != nothing
            if vc2[1] != nothing
                push!(arr, x->vc1[1](x) && vc2[1](x))
            else
                push!(arr, vc1[1])
            end
        elseif vc2[1] != nothing
            push!(arr, vc2[1])
        else
            push!(arr, nothing)
        end
        if vc1[2] != nothing && vc2[2] != nothing
            push!(arr, x->vc1[2](x) || vc2[2](x))
        else
            push!(arr, nothing)
        end
        return tuple(arr...)
    elseif isexpr(ex, :(||))
        vc1 = versionconstraint(ex.args[1])
        vc2 = versionconstraint(ex.args[2])
        arr = Any[]
        if vc1[1] != nothing && vc2[1] != nothing
                push!(arr, x->vc1[1](x) || vc2[1](x))
        else
            push!(arr, nothing)
        end

        if vc1[2] != nothing
            if vc2[2] != nothing
                push!(arr, x->vc1[2](x) && vc2[2](x))
            else
                push!(arr, vc1[2])
            end
        else
            if vc2[2] != nothing
                push!(arr, vc2[2])
            else
                push!(arr, nothing)
            end
        end
        return tuple(arr...)
    elseif isexpr(ex, :call) && ex.args[1] == :(!)
        (v1,v2) = versionconstraint(ex.args[2])
        return (v2, v1)
    end
    return (nothing, nothing)
end

# expect ex would compute into a boolean
function lintboolean(ex, ctx::LintContext)
    if typeof(ex) <: Expr
        if ex.head == :(=)
            msg(ctx, :I472, "assignment in the if-predicate clause")
        elseif ex.head == :call && ex.args[1] in [:(&), :(|), :($)]
            msg(ctx, :I475, ex.args[1], "bit-wise in a boolean " *
                "context. (&,|) do not have short-circuit behavior")
        elseif ex.head == :(&&) || ex.head == :(||)
            n = length(ex.args)
            for a in ex.args
                lintboolean(a, ctx)
            end
        elseif ex.head ==:call && ex.args[1] == :(!)
            for i in 2:length(ex.args)
                a = ex.args[i]
                lintboolean(a, ctx)
            end
        elseif ex.head == :call && ex.args[1] in COMPARISON_OPS
            #reuse lintcomparison by synthetically construct the expr
            lintcomparison( Expr( :comparison, ex.args[2], ex.args[1], ex.args[3] ), ctx )
        elseif ex.head == :comparison
            lintcomparison(ex, ctx)
        elseif ex.head == :call && ex.args[1] == :length
            msg(ctx, :E431, ex.args[1], "use of length() in a Boolean context, use isempty()")
        end
    elseif typeof(ex) == Symbol
        # can we figure of if that symbol is Bool?
        gt = guesstype(ex, ctx)
        if gt != Any && gt != Bool
            msg(ctx, :E511, ex, "apparent non-Bool type")
        end
    elseif typeof(ex) != Bool
        msg(ctx, :E512, ex, "Lint doesn't understand in a boolean context")
    end

    if typeof(ex) <: Expr || typeof(ex) == Symbol
        lintexpr(ex, ctx)
    end
end

function lintcomparison(ex::Expr, ctx::LintContext)
    if ctx.quoteLvl != 0
        return
    end
    pos = 0
    lefttype = Any
    righttype = Any
    for i in 2:2:length(ex.args)
        if ex.args[i] in COMPARISON_OPS
            if pos != i-1
                pos = i-1
                lefttype = guesstype(ex.args[i-1], ctx)
            end
            righttype = guesstype(ex.args[i+1], ctx)
            if lefttype != Any && righttype != Any
                problem = false
                if lefttype <: Number && righttype <: Number &&
                        !(lefttype <: Real && righttype <: Real) &&
                        !in(ex.args[i], [:(==), :(!=)]) # non-real comparison can only be == or !=
                    problem = true
                end
                if !problem && (!(lefttype <: Number) || !(righttype <: Number)) &&
                       !(lefttype <: righttype) && !(righttype <: lefttype)
                    problem = true
                end
                if problem && !pragmaexists("Ignore incompatible type comparison", ctx.current)
                    msg(ctx, :W542, "comparing apparently incompatible types " *
                        "(#$(i>>1)) LHS:$(lefttype) RHS:$(righttype)")
                end
            end
            lefttype = righttype
            pos += 2
        end
    end
end

function lintfor(ex::Expr, ctx::LintContext)
    withcontext(ctx, LocalContext(ctx.current)) do
        if isexpr(ex.args[1], :(=))
            lintassignment(ex.args[1], ctx; isForLoop=true)
        elseif isexpr(ex.args[1], :block)
            for a in ex.args[1].args
                if isexpr(a, :(=))
                    lintassignment(a, ctx; isForLoop=true)
                end
            end
        end
        lintexpr(ex.args[2], ctx)
    end
end

function lintwhile(ex::Expr, ctx::LintContext)
    if ex.args[1] == false
        msg(ctx, :W645, "while false block is unreachable")
    elseif isa(ex.args[1], Expr)
        lintboolean(ex.args[1], ctx)
    end
    withcontext(ctx, LocalContext(ctx.current)) do
        lintexpr(ex.args[2], ctx)
    end
end


text:
# ---
# title: 720. Longest Word in Dictionary
# id: problem720
# author: Tian Jun
# date: 2020-10-31
# difficulty: Easy
# categories: Hash Table, Trie
# link: <https://leetcode.com/problems/longest-word-in-dictionary/description/>
# hidden: true
# ---
#
# Given a list of strings `words` representing an English Dictionary, find the
# longest word in `words` that can be built one character at a time by other
# words in `words`. If there is more than one possible answer, return the
# longest word with the smallest lexicographical order.
#
# If there is no answer, return the empty string.
#
# **Example 1:**
#
#
#
#     Input:
#     words = ["w","wo","wor","worl", "world"]
#     Output: "world"
#     Explanation:
#     The word "world" can be built one character at a time by "w", "wo", "wor", and "worl".
#
#
# **Example 2:**
#
#
#
#     Input:
#     words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
#     Output: "apple"
#     Explanation:
#     Both "apply" and "apple" can be built from other words in the dictionary. However, "apple" is lexicographically smaller than "apply".
#
#
# **Note:**
#
# * All the strings in the input will only contain lowercase letters.
# * The length of `words` will be in the range `[1, 1000]`.
# * The length of `words[i]` will be in the range `[1, 30]`.
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
module Ref

using IdentityRanges: IdentityRange
using IntervalArithmetic: interval, mid
using IntervalRootFinding: Newton, roots
using LinearAlgebra: Diagonal, Hermitian, I, eigen, opnorm, norm, qr, svd, tr, /
using Logging: @debug
import PolynomialRoots
using Roots: find_zero
using Statistics: mean

# findmax from https://github.com/cmcaine/julia/blob/argmax-2-arg-harder/base/reduce.jl#L704-L705
# argmax from https://github.com/cmcaine/julia/blob/argmax-2-arg-harder/base/reduce.jl#L830
# part of pull request https://github.com/JuliaLang/julia/pull/35316
_findmax(f, domain) = mapfoldl(x -> (f(x), x), _rf_findmax, domain)
_rf_findmax((fm, m), (fx, x)) = isless(fm, fx) ? (fx, x) : (fm, m)
_argmax(f, domain) = _findmax(f, domain)[2]

# Homoscedastic init
function homppca(Y,k)
    Yflat = reduce(hcat,Y)
    d, n = size(Yflat)
    λh, Uh = eigen(Hermitian(Yflat*Yflat'/n),sortby=-)
    λb = mean(λh[k+1:d])
    return Uh[:,1:k], λh[1:k] .- λb, λb
end

# log-likelihood (todo: add constant)
function loglikelihood(U,λ,v,Y)
    d, k = size(U)
    n, L = size.(Y,2), length(Y)
    return 1/2*sum(
        -n[l]*sum(log.(λ .+ v[l])) - n[l]*(d-k)*log(v[l]) - norm(Y[l])^2/v[l]
        + norm(sqrt(Diagonal((λ./v[l])./(λ .+ v[l])))*U'Y[l])^2
        for l = 1:L
    )
end

# v updates
updatev_roots(U,λ,v,Y) = [updatevl_roots(U,λ,v[l],Y[l]) for l in 1:length(Y)]
function updatevl_roots(U,λ,vl,Yl)
    d, k = size(U)
    nl = size(Yl,2)

    # Compute coefficients and check edge case
    α = [j == 0 ? d-k : 1 for j in IdentityRange(0:k)]
    β = [j == 0 ? norm((I-U*U')*Yl)^2/nl : norm(Yl'U[:,j])^2/nl for j in IdentityRange(0:k)]
    γ = [j == 0 ? zero(eltype(λ)) : λ[j] for j in IdentityRange(0:k)]
    J0 = findall(iszero,γ)
    βtl = sum(β[j] for j in J0)
    iszero(βtl) && return zero(vl)

    # Find nonnegative critical points
    tol = 1e-8  # todo: choose tolerance adaptively
    vmin, vmax = extrema(β[j]/α[j]-γ[j] for j in 0:k if !(iszero(α[j]) && iszero(β[j])))
    vcritical = roots(interval(vmin,vmax) ∩ interval(zero(vl),Inf),Newton,tol) do v
        - sum(α[j]/(γ[j]+v) for j in 0:k if !iszero(α[j])) + sum(β[j]/(γ[j]+v)^2 for j in 0:k if !iszero(β[j]))
    end

    # Return maximizer
    return _argmax(mid.(interval.(vcritical))) do v
        -(sum(α[j]*log(γ[j]+v) for j in 0:k if !iszero(α[j])) + sum(β[j]/(γ[j]+v) for j in 0:k if !iszero(β[j])))
    end
end
function updatev_em(U,λ,v,Y)
    d, n, L = size(U,1), size.(Y,2), length(Y)
    Λ = Diagonal(λ)
    ρ = [norm((I-U*Λ*inv(Λ+v[l]*I)*U')*Y[l])^2/n[l] + v[l]*tr(U*Λ*inv(Λ+v[l]*I)*U') for l in 1:L]
    return ρ./d
end
updatev_doc(U,λ,v,Y) = [updatevl_doc(U,λ,v[l],Y[l]) for l in 1:length(Y)]
function updatevl_doc(U,λ,vl,Yl)
    d, k = size(U)
    nl = size(Yl,2)

    # Compute coefficients and check edge case
    α = [j == 0 ? d-k : 1 for j in IdentityRange(0:k)]
    β = [j == 0 ? norm((I-U*U')*Yl)^2/nl : norm(Yl'U[:,j])^2/nl for j in IdentityRange(0:k)]
    γ = [j == 0 ? zero(eltype(λ)) : λ[j] for j in IdentityRange(0:k)]
    affslope = -sum(α[j]/(γ[j]+vl) for j in 0:k if !iszero(α[j]))
    Ltp = v -> affslope + sum(β[j]/(γ[j]+v)^2 for j in 0:k if !iszero(β[j]))
    if affslope == -Inf || Ltp(zero(vl)) <= zero(vl)
        return zero(vl)
    end

    # Return nonnegative critical point
    tol = 1e-8  # todo: choose tolerance adaptively
    vmax = maximum(sqrt(β[j]/α[j]*(γ[j]+vl)) - γ[j] for j in 0:k if !(iszero(α[j]) && iszero(β[j])))
    return find_zero(Ltp, (zero(vmax),vmax))
end
updatev_mm_quad(U,λ,v,Y) = [updatevl_mm_quad(U,λ,v[l],Y[l]) for l in 1:length(Y)]
function updatevl_mm_quad(U,λ,vl,Yl)
    d, k = size(U)
    nl = size(Yl,2)

    # Compute coefficients
    α = [j == 0 ? d-k : 1 for j in IdentityRange(0:k)]
    β = [j == 0 ? norm((I-U*U')*Yl)^2/nl : norm(Yl'U[:,j])^2/nl for j in IdentityRange(0:k)]
    γ = [j == 0 ? zero(eltype(λ)) : λ[j] for j in IdentityRange(0:k)]
    J0 = findall(iszero,γ)
    αtl = sum(α[j] for j in J0)
    βtl = sum(β[j] for j in J0)
    ζtl = sum(α[j]/(γ[j]+vl) for j in 0:k if j ∉ J0)
    B = βtl + sum(β[j]*vl^2/(γ[j]+vl)^2 for j in 0:k if j ∉ J0)

    return (-αtl + sqrt(αtl^2 + 4*ζtl*B))/(2*ζtl)
end
updatev_mm_cubic(U,λ,v,Y) = [updatevl_mm_cubic(U,λ,v[l],Y[l]) for l in 1:length(Y)]
function updatevl_mm_cubic(U,λ,vl,Yl)
    d, k = size(U)
    nl = size(Yl,2)

    # Compute coefficients
    α = [j == 0 ? d-k : 1 for j in IdentityRange(0:k)]
    β = [j == 0 ? norm((I-U*U')*Yl)^2/nl : norm(Yl'U[:,j])^2/nl for j in IdentityRange(0:k)]
    γ = [j == 0 ? zero(eltype(λ)) : λ[j] for j in IdentityRange(0:k)]
    c = [-2*β[j]/γ[j]^3 for j in IdentityRange(0:k)]
    J0 = findall(iszero,γ)
    αtl = sum(α[j] for j in J0)
    βtl = sum(β[j] for j in J0)
    ζtl = sum(α[j]/(γ[j]+vl) for j in 0:k if j ∉ J0)
    γtl = -ζtl + sum(β[j]/(γ[j]+vl)^2 for j in 0:k if j ∉ J0)
    ctl = sum(c[j] for j in 0:k if j ∉ J0)

    complexroots = PolynomialRoots.solve_cubic_eq(complex.([βtl,-αtl,γtl-ctl*vl,ctl]))
    vcritical = [real(v) for v in complexroots if real(v) ≈ v && real(v) >= zero(vl)]

    isempty(vcritical) && return zero(vl)
    return _argmax(vcritical) do v
        -αtl*log(v) - βtl/v - ζtl*v + sum(β[j]/(γ[j]+vl)^2*v + (1/2)*c[j]*(v-vl)^2 for j in 0:k if j ∉ J0)
    end
end

# F updates
function updateF_em(F,v,Y)
    n, L = size.(Y,2), length(Y)
    U, θ, V = svd(F)
    λ = θ.^2

    Λ = Diagonal(λ)
    Γ = [inv(Λ + v[l]*I) for l in 1:L]
    Z = [Γ[l]*sqrt(Λ)*U'*Y[l] for l in 1:L]
    num = sum(Y[l]*Z[l]'/v[l] for l in 1:L)
    den = sum(Z[l]*Z[l]'/v[l] + n[l]*Γ[l] for l in 1:L)

    return (num / den) * V'
end

# U updates
function polar(A)
    F = svd(A)
    return F.U*F.Vt
end
gradF(U,λ,v,Y) = sum(Yl * Yl' * U * Diagonal(λ./vl./(λ.+vl)) for (Yl,vl) in zip(Y,v))
F(U,λ,v,Y) = 1/2*sum(norm(sqrt(Diagonal(λ./vl./(λ.+vl)))*U'*Yl)^2 for (Yl,vl) in zip(Y,v))
function LipBoundU1(λ,v,Y)
    L, λmax = length(v), maximum(λ)
    return sum(norm(Y[l])^2*λmax/v[l]/(λmax+v[l]) for l in 1:L)
end
function LipBoundU2(λ,v,Y)
    L = length(v)
    return sum(opnorm(Y[l]*Y[l]') * opnorm(Diagonal(λ./v[l]./(λ.+v[l]))) for l in 1:L)
end

updateU_mm(U,λ,v,Y) = polar(gradF(U,λ,v,Y))
updateU_pga(U,λ,v,Y,α) = α == Inf ? polar(gradF(U,λ,v,Y)) : polar(U + α*gradF(U,λ,v,Y))
function updateU_pga_armijo(U,λ,v,Y,maxsearches,stepsize,contraction,tol)
    dFdU = gradF(U,λ,v,Y)
    F0, FΔ = F(U,λ,v,Y), tol * norm(dFdU)^2
    for m in 0:maxsearches-1
        Δ = stepsize * contraction^m
        (F(polar(U + Δ*dFdU),λ,v,Y) >= F0 + Δ * FΔ) && return polar(U + Δ*dFdU)
    end
    @debug "Exceeded maximum line search iterations. Accuracy not guaranteed."
    Δ = stepsize * contraction^maxsearches
    return polar(U + Δ*dFdU)
end
function updateU_sga(U,λ,v,Y,maxsearches,stepsize,contraction,tol)
    dFdU = gradF(U,λ,v,Y)
    ∇F = dFdU - U*(dFdU'U)

    F0, FΔ = F(U,λ,v,Y), tol * norm(∇F)^2
    for m in 0:maxsearches-1
        Δ = stepsize * contraction^m
        (F(geodesic(U,∇F,Δ),λ,v,Y) >= F0 + Δ * FΔ) && return geodesic(U,∇F,Δ)
    end
    @debug "Exceeded maximum line search iterations. Accuracy not guaranteed."
    Δ = stepsize * contraction^maxsearches
    return geodesic(U,∇F,Δ)
end
skew(A) = (A-A')/2
function geodesic(U,X,t)
    k = size(U,2)

    A = skew(U'X)
    Q,R = qr(X - U*(U'X))

    MN = exp(t*[A -R'; R zeros(k,k)])[:,1:k]
    M, N = MN[1:k,:], MN[k+1:end,:]

    return U*M + Matrix(Q)*N
end

# λ updates
ispos(x) = x > zero(x)

updateλ_roots(U,v,Y) = [updateλj_roots(uj,v,Y) for uj in eachcol(U)]
function updateλj_roots(uj,v,Y)
    n, L = size.(Y,2), length(Y)

    # Compute coefficients and root bounds
    β = [norm(uj'Yl)^2 for Yl in Y]
    λmin, λmax = extrema(max.(zero.(β), β./n .- v))

    # Compute roots
    tol = 1e-8  # todo: choose tolerance adaptively
    λmax-λmin < tol && return (λmax+λmin)/2
    λcritical = roots(interval(λmin,λmax),Newton,tol) do λ
        sum(β[l]/(λ+v[l])^2 - n[l]/(λ+v[l]) for l in 1:L)
    end
    isempty(λcritical) && return λmin
    length(λcritical) == 1 && return mid(interval(first(λcritical)))

    # Return maximizer
    return _argmax(mid.(interval.(λcritical))) do λ
        -sum(n[l]*log(λ+v[l]) + β[l]/(λ+v[l]) for l in 1:L)
    end
end
updateλ_em(λ,U,v,Y) = [updateλj_em(λj,uj,v,Y) for (λj,uj) in zip(λ,eachcol(U))]
function updateλj_em(λj,uj,v,Y)
    n, L = size.(Y,2), length(Y)
    θj = sum(sqrt(λj)/v[l]/(λj+v[l])*norm(uj'Y[l])^2 for l = 1:L)/sum(λj/v[l]/(λj+v[l])^2*norm(uj'Y[l])^2 + n[l]/(λj+v[l]) for l = 1:L)
    θj^2
end
updateλ_mm(λ,U,v,Y) = [updateλj_mm(λj,uj,v,Y) for (λj,uj) in zip(λ,eachcol(U))]
function updateλj_mm(λj,uj,v,Y)
    all(ispos,v) || throw("Minorizer expects positive v. Got: $v")
    n, L = size.(Y,2), length(Y)

    ζ = [norm(Y[l]'uj)^2/v[l] * λj/(λj+v[l]) + n[l] for l in 1:L]
    num = sum(norm(Y[l]'uj)^2/v[l] * λj/(λj+v[l]) for l in 1:L) * sum(ζ[l]*v[l]/(λj+v[l]) for l in 1:L)
    den = sum(ζ[l]/(λj+v[l]) for l in 1:L)
    return (1/sum(n)) * num / den
end
updateλ_quad(λ,U,v,Y) = [updateλj_quad(λj,uj,v,Y) for (λj,uj) in zip(λ,eachcol(U))]
function updateλj_quad(λj,uj,v,Y)
    all(ispos,v) || throw("Minorizer expects positive v. Got: $v")
    n, L = size.(Y,2), length(Y)

    num = sum(1:L) do l
        ytlj = norm(Y[l]'uj)^2
        n[l]/(λj+v[l]) - ytlj/(λj+v[l])^2
    end
    den = sum(1:L) do l
        ytlj = norm(Y[l]'uj)^2
        ctlj = -2*ytlj/v[l]^3
        ctlj
    end

    return max(zero(λj),λj + num/den)
end
updateλ_doc(λ,U,v,Y) = [updateλj_doc(λj,uj,v,Y) for (λj,uj) in zip(λ,eachcol(U))]
function updateλj_doc(λj,uj,v,Y)
    n, L = size.(Y,2), length(Y)

    # Compute coefficients and check edge case
    ytj = [norm(Y[l]'uj)^2 for l in 1:L]
    affslope = -sum(n[l]/(λj+v[l]) for l in 1:L)
    Ltp = λ -> affslope + sum(ytj[l]/(λ+v[l])^2 for l in 1:L if !iszero(ytj[l]))
    if affslope == -Inf || Ltp(zero(λj)) <= zero(λj)
        return zero(λj)
    end

    # Return nonnegative critical point
    tol = 1e-13  # to get a bracketing interval
    λmax = maximum(sqrt(ytj[l]/n[l]*(λj+v[l])) - v[l] for l in 1:L) + tol
    return find_zero(Ltp,(zero(λmax),λmax))
end
updateλ_opt_quad(λ,U,v,Y) = [updateλj_opt_quad(λj,uj,v,Y) for (λj,uj) in zip(λ,eachcol(U))]
function updateλj_opt_quad(λj,uj,v,Y)
    all(ispos,v) || throw("Minorizer expects positive v. Got: $v")
    n, L = size.(Y,2), length(Y)

    num = sum(1:L) do l
        ytlj = norm(Y[l]'uj)^2
        n[l]/(λj+v[l]) - ytlj/(λj+v[l])^2
    end
    den = sum(1:L) do l
        ytlj = norm(Y[l]'uj)^2
        a, b = n[l], ytlj
        f = λ -> a*log(λ+v[l]) + b/(λ+v[l])
        fd = λ -> a/(λ+v[l]) - b/(λ+v[l])^2
        ctlj = (2b <= a*v[l]) ? zero(λj) : -2*(f(zero(λj)) - f(λj) + fd(λj)*λj)/λj^2
        ctlj
    end

    return max(zero(λj),λj + num/den)
end

end


text:
module TestConstraintVectorize

using Test

using MathOptInterface
const MOI = MathOptInterface

function runtests()
    for name in names(@__MODULE__; all = true)
        if startswith("$(name)", "test_")
            @testset "$(name)" begin
                getfield(@__MODULE__, name)()
            end
        end
    end
    return
end

include("../utilities.jl")

function test_ScalarFunctionConstantNotZero()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config()
    MOI.Test.test_model_ScalarFunctionConstantNotZero(bridged_mock, config)
    return
end

function test_basic()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config()
    MOI.Test.runtests(
        bridged_mock,
        config,
        include = [
            "test_basic_$(F)_$(S)" for
            F in ["VariableIndex", "ScalarAffineFunction"] for
            S in ["EqualTo", "GreaterThan", "LessThan"]
        ],
    )
    return
end

function test_linear_integration()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config(exclude = Any[MOI.ConstraintBasisStatus])
    MOI.Utilities.set_mock_optimize!(
        mock,
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            [1, 0],
            (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => [[-1]],
            (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) =>
                [[0], [1]],
        ),
    )
    MOI.Test.test_linear_integration_2(bridged_mock, config)
    return
end

function test_linear_LessThan_and_GreaterThan()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config()
    MOI.Utilities.set_mock_optimize!(
        mock,
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [0, 0]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [100, 0]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [100, -100]),
    )
    return MOI.Test.test_linear_LessThan_and_GreaterThan(bridged_mock, config)
end

function test_linear_integration_modification()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config()
    MOI.Utilities.set_mock_optimize!(
        mock,
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [4 / 3, 4 / 3]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [2, 0]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [4, 0]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [2]),
    )
    MOI.Test.test_linear_integration_modification(bridged_mock, config)
    return
end

function test_linear_modify_GreaterThan_and_LessThan_constraints()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config()
    MOI.Utilities.set_mock_optimize!(
        mock,
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [0, 0]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [100, 0]),
        (mock::MOI.Utilities.MockOptimizer) ->
            MOI.Utilities.mock_optimize!(mock, [100, -100]),
    )
    MOI.Test.test_linear_modify_GreaterThan_and_LessThan_constraints(
        bridged_mock,
        config,
    )
    return
end

function test_linear_integration_delete_variables()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config(exclude = Any[MOI.ConstraintBasisStatus])
    MOI.Utilities.set_mock_optimize!(
        mock,
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            [0, 1 / 2, 1],
            (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) =>
                [[-1], [-2]],
            (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) =>
                [[2], [0], [0]],
        ),
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            [1],
            (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => [[-1]],
            (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0]],
        ),
    )
    # test_linear_integration_delete_variables has double variable bounds for
    # the z variable
    mock.eval_variable_constraint_dual = false
    MOI.Test.test_linear_integration_delete_variables(bridged_mock, config)
    return
end

function test_conic_PositiveSemidefiniteConeTriangle_VectorOfVariables()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
    config = MOI.Test.Config()
    mock.optimize! =
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            ones(3),
            (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[2]],
        )
    MOI.Test.test_conic_PositiveSemidefiniteConeTriangle_VectorOfVariables(
        bridged_mock,
        config,
    )
    ci = first(
        MOI.get(
            bridged_mock,
            MOI.ListOfConstraintIndices{
                MOI.ScalarAffineFunction{Float64},
                MOI.EqualTo{Float64},
            }(),
        ),
    )
    for attr in [MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()]
        @test MOI.supports(bridged_mock, attr, typeof(ci))
        MOI.set(bridged_mock, attr, ci, 2.0)
        @test MOI.get(bridged_mock, attr, ci) == 2.0
    end
    _test_delete_bridge(
        bridged_mock,
        ci,
        3,
        ((MOI.VectorAffineFunction{Float64}, MOI.Zeros, 0),),
    )
    return
end

end  # module

TestConstraintVectorize.runtests()