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

"""
    cancel_job(job_id, reason)
    cancel_job(job_id, reason, params::Dict{String,<:Any})

Cancels a job in an AWS Batch job queue. Jobs that are in the SUBMITTED, PENDING, or
RUNNABLE state are canceled. Jobs that have progressed to STARTING or RUNNING are not
canceled (but the API operation still succeeds, even if no job is canceled); these jobs
must be terminated with the TerminateJob operation.

# Arguments
- `job_id`: The AWS Batch job ID of the job to cancel.
- `reason`: A message to attach to the job that explains the reason for canceling it. This
  message is returned by future DescribeJobs operations on the job. This message is also
  recorded in the AWS Batch activity logs.

"""
cancel_job(jobId, reason; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/canceljob", Dict{String, Any}("jobId"=>jobId, "reason"=>reason); aws_config=aws_config)
cancel_job(jobId, reason, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/canceljob", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobId"=>jobId, "reason"=>reason), params)); aws_config=aws_config)

"""
    create_compute_environment(compute_environment_name, service_role, type)
    create_compute_environment(compute_environment_name, service_role, type, params::Dict{String,<:Any})

Creates an AWS Batch compute environment. You can create MANAGED or UNMANAGED compute
environments. MANAGED compute environments can use Amazon EC2 or AWS Fargate resources.
UNMANAGED compute environments can only use EC2 resources. In a managed compute
environment, AWS Batch manages the capacity and instance types of the compute resources
within the environment. This is based on the compute resource specification that you define
or the launch template that you specify when you create the compute environment. You can
choose either to use EC2 On-Demand Instances and EC2 Spot Instances, or to use Fargate and
Fargate Spot capacity in your managed compute environment. You can optionally set a maximum
price so that Spot Instances only launch when the Spot Instance price is less than a
specified percentage of the On-Demand price.  Multi-node parallel jobs are not supported on
Spot Instances.  In an unmanaged compute environment, you can manage your own EC2 compute
resources and have a lot of flexibility with how you configure your compute resources. For
example, you can use custom AMI. However, you need to verify that your AMI meets the Amazon
ECS container instance AMI specification. For more information, see container instance AMIs
in the Amazon Elastic Container Service Developer Guide. After you have created your
unmanaged compute environment, you can use the DescribeComputeEnvironments operation to
find the Amazon ECS cluster that's associated with it. Then, manually launch your container
instances into that Amazon ECS cluster. For more information, see Launching an Amazon ECS
container instance in the Amazon Elastic Container Service Developer Guide.  AWS Batch
doesn't upgrade the AMIs in a compute environment after it's created. For example, it
doesn't update the AMIs when a newer version of the Amazon ECS-optimized AMI is available.
Therefore, you're responsible for the management of the guest operating system (including
updates and security patches) and any additional application software or utilities that you
install on the compute resources. To use a new AMI for your AWS Batch jobs, complete these
steps:   Create a new compute environment with the new AMI.   Add the compute environment
to an existing job queue.   Remove the earlier compute environment from your job queue.
Delete the earlier compute environment.

# Arguments
- `compute_environment_name`: The name for your compute environment. Up to 128 letters
  (uppercase and lowercase), numbers, hyphens, and underscores are allowed.
- `service_role`: The full Amazon Resource Name (ARN) of the IAM role that allows AWS Batch
  to make calls to other AWS services on your behalf. For more information, see AWS Batch
  service IAM role in the AWS Batch User Guide. If your specified role has a path other than
  /, then you must either specify the full role ARN (this is recommended) or prefix the role
  name with the path.  Depending on how you created your AWS Batch service role, its ARN
  might contain the service-role path prefix. When you only specify the name of the service
  role, AWS Batch assumes that your ARN doesn't use the service-role path prefix. Because of
  this, we recommend that you specify the full ARN of your service role when you create
  compute environments.
- `type`: The type of the compute environment: MANAGED or UNMANAGED. For more information,
  see Compute Environments in the AWS Batch User Guide.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"computeResources"`: Details about the compute resources managed by the compute
  environment. This parameter is required for managed compute environments. For more
  information, see Compute Environments in the AWS Batch User Guide.
- `"state"`: The state of the compute environment. If the state is ENABLED, then the
  compute environment accepts jobs from a queue and can scale out automatically based on
  queues. If the state is ENABLED, then the AWS Batch scheduler can attempt to place jobs
  from an associated job queue on the compute resources within the environment. If the
  compute environment is managed, then it can scale its instances out or in automatically,
  based on the job queue demand. If the state is DISABLED, then the AWS Batch scheduler
  doesn't attempt to place jobs within the environment. Jobs in a STARTING or RUNNING state
  continue to progress normally. Managed compute environments in the DISABLED state don't
  scale out. However, they scale in to minvCpus value after instances become idle.
- `"tags"`: The tags that you apply to the compute environment to help you categorize and
  organize your resources. Each tag consists of a key and an optional value. For more
  information, see Tagging AWS Resources in AWS General Reference. These tags can be updated
  or removed using the TagResource and UntagResource API operations. These tags don't
  propagate to the underlying compute resources.
"""
create_compute_environment(computeEnvironmentName, serviceRole, type; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/createcomputeenvironment", Dict{String, Any}("computeEnvironmentName"=>computeEnvironmentName, "serviceRole"=>serviceRole, "type"=>type); aws_config=aws_config)
create_compute_environment(computeEnvironmentName, serviceRole, type, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/createcomputeenvironment", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("computeEnvironmentName"=>computeEnvironmentName, "serviceRole"=>serviceRole, "type"=>type), params)); aws_config=aws_config)

"""
    create_job_queue(compute_environment_order, job_queue_name, priority)
    create_job_queue(compute_environment_order, job_queue_name, priority, params::Dict{String,<:Any})

Creates an AWS Batch job queue. When you create a job queue, you associate one or more
compute environments to the queue and assign an order of preference for the compute
environments. You also set a priority to the job queue that determines the order in which
the AWS Batch scheduler places jobs onto its associated compute environments. For example,
if a compute environment is associated with more than one job queue, the job queue with a
higher priority is given preference for scheduling jobs to that compute environment.

# Arguments
- `compute_environment_order`: The set of compute environments mapped to a job queue and
  their order relative to each other. The job scheduler uses this parameter to determine
  which compute environment should run a specific job. Compute environments must be in the
  VALID state before you can associate them with a job queue. You can associate up to three
  compute environments with a job queue. All of the compute environments must be either EC2
  (EC2 or SPOT) or Fargate (FARGATE or FARGATE_SPOT); EC2 and Fargate compute environments
  can't be mixed.  All compute environments that are associated with a job queue must share
  the same architecture. AWS Batch doesn't support mixing compute environment architecture
  types in a single job queue.
- `job_queue_name`: The name of the job queue. Up to 128 letters (uppercase and lowercase),
  numbers, and underscores are allowed.
- `priority`: The priority of the job queue. Job queues with a higher priority (or a higher
  integer value for the priority parameter) are evaluated first when associated with the same
  compute environment. Priority is determined in descending order. For example, a job queue
  with a priority value of 10 is given scheduling preference over a job queue with a priority
  value of 1. All of the compute environments must be either EC2 (EC2 or SPOT) or Fargate
  (FARGATE or FARGATE_SPOT); EC2 and Fargate compute environments cannot be mixed.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"state"`: The state of the job queue. If the job queue state is ENABLED, it is able to
  accept jobs. If the job queue state is DISABLED, new jobs can't be added to the queue, but
  jobs already in the queue can finish.
- `"tags"`: The tags that you apply to the job queue to help you categorize and organize
  your resources. Each tag consists of a key and an optional value. For more information, see
  Tagging your AWS Batch resources in AWS Batch User Guide.
"""
create_job_queue(computeEnvironmentOrder, jobQueueName, priority; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/createjobqueue", Dict{String, Any}("computeEnvironmentOrder"=>computeEnvironmentOrder, "jobQueueName"=>jobQueueName, "priority"=>priority); aws_config=aws_config)
create_job_queue(computeEnvironmentOrder, jobQueueName, priority, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/createjobqueue", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("computeEnvironmentOrder"=>computeEnvironmentOrder, "jobQueueName"=>jobQueueName, "priority"=>priority), params)); aws_config=aws_config)

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

Deletes an AWS Batch compute environment. Before you can delete a compute environment, you
must set its state to DISABLED with the UpdateComputeEnvironment API operation and
disassociate it from any job queues with the UpdateJobQueue API operation. Compute
environments that use AWS Fargate resources must terminate all active jobs on that compute
environment before deleting the compute environment. If this isn't done, the compute
environment will end up in an invalid state.

# Arguments
- `compute_environment`: The name or Amazon Resource Name (ARN) of the compute environment
  to delete.

"""
delete_compute_environment(computeEnvironment; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/deletecomputeenvironment", Dict{String, Any}("computeEnvironment"=>computeEnvironment); aws_config=aws_config)
delete_compute_environment(computeEnvironment, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/deletecomputeenvironment", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("computeEnvironment"=>computeEnvironment), params)); aws_config=aws_config)

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

Deletes the specified job queue. You must first disable submissions for a queue with the
UpdateJobQueue operation. All jobs in the queue are eventually terminated when you delete a
job queue. The jobs are terminated at a rate of about 16 jobs each second. It's not
necessary to disassociate compute environments from a queue before submitting a
DeleteJobQueue request.

# Arguments
- `job_queue`: The short name or full Amazon Resource Name (ARN) of the queue to delete.

"""
delete_job_queue(jobQueue; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/deletejobqueue", Dict{String, Any}("jobQueue"=>jobQueue); aws_config=aws_config)
delete_job_queue(jobQueue, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/deletejobqueue", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobQueue"=>jobQueue), params)); aws_config=aws_config)

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

Deregisters an AWS Batch job definition. Job definitions are permanently deleted after 180
days.

# Arguments
- `job_definition`: The name and revision (name:revision) or full Amazon Resource Name
  (ARN) of the job definition to deregister.

"""
deregister_job_definition(jobDefinition; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/deregisterjobdefinition", Dict{String, Any}("jobDefinition"=>jobDefinition); aws_config=aws_config)
deregister_job_definition(jobDefinition, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/deregisterjobdefinition", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobDefinition"=>jobDefinition), params)); aws_config=aws_config)

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

Describes one or more of your compute environments. If you're using an unmanaged compute
environment, you can use the DescribeComputeEnvironment operation to determine the
ecsClusterArn that you should launch your Amazon ECS container instances into.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"computeEnvironments"`: A list of up to 100 compute environment names or full Amazon
  Resource Name (ARN) entries.
- `"maxResults"`: The maximum number of cluster results returned by
  DescribeComputeEnvironments in paginated output. When this parameter is used,
  DescribeComputeEnvironments only returns maxResults results in a single page along with a
  nextToken response element. The remaining results of the initial request can be seen by
  sending another DescribeComputeEnvironments request with the returned nextToken value. This
  value can be between 1 and 100. If this parameter isn't used, then
  DescribeComputeEnvironments returns up to 100 results and a nextToken value if applicable.
- `"nextToken"`: The nextToken value returned from a previous paginated
  DescribeComputeEnvironments request where maxResults was used and the results exceeded the
  value of that parameter. Pagination continues from the end of the previous results that
  returned the nextToken value. This value is null when there are no more results to return.
  This token should be treated as an opaque identifier that's only used to retrieve the next
  items in a list and not for other programmatic purposes.
"""
describe_compute_environments(; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describecomputeenvironments"; aws_config=aws_config)
describe_compute_environments(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describecomputeenvironments", params; aws_config=aws_config)

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

Describes a list of job definitions. You can specify a status (such as ACTIVE) to only
return job definitions that match that status.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"jobDefinitionName"`: The name of the job definition to describe.
- `"jobDefinitions"`: A list of up to 100 job definition names or full Amazon Resource Name
  (ARN) entries.
- `"maxResults"`: The maximum number of results returned by DescribeJobDefinitions in
  paginated output. When this parameter is used, DescribeJobDefinitions only returns
  maxResults results in a single page along with a nextToken response element. The remaining
  results of the initial request can be seen by sending another DescribeJobDefinitions
  request with the returned nextToken value. This value can be between 1 and 100. If this
  parameter isn't used, then DescribeJobDefinitions returns up to 100 results and a nextToken
  value if applicable.
- `"nextToken"`: The nextToken value returned from a previous paginated
  DescribeJobDefinitions request where maxResults was used and the results exceeded the value
  of that parameter. Pagination continues from the end of the previous results that returned
  the nextToken value. This value is null when there are no more results to return.  This
  token should be treated as an opaque identifier that's only used to retrieve the next items
  in a list and not for other programmatic purposes.
- `"status"`: The status used to filter job definitions.
"""
describe_job_definitions(; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describejobdefinitions"; aws_config=aws_config)
describe_job_definitions(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describejobdefinitions", params; aws_config=aws_config)

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

Describes one or more of your job queues.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"jobQueues"`: A list of up to 100 queue names or full queue Amazon Resource Name (ARN)
  entries.
- `"maxResults"`: The maximum number of results returned by DescribeJobQueues in paginated
  output. When this parameter is used, DescribeJobQueues only returns maxResults results in a
  single page along with a nextToken response element. The remaining results of the initial
  request can be seen by sending another DescribeJobQueues request with the returned
  nextToken value. This value can be between 1 and 100. If this parameter isn't used, then
  DescribeJobQueues returns up to 100 results and a nextToken value if applicable.
- `"nextToken"`: The nextToken value returned from a previous paginated DescribeJobQueues
  request where maxResults was used and the results exceeded the value of that parameter.
  Pagination continues from the end of the previous results that returned the nextToken
  value. This value is null when there are no more results to return.  This token should be
  treated as an opaque identifier that's only used to retrieve the next items in a list and
  not for other programmatic purposes.
"""
describe_job_queues(; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describejobqueues"; aws_config=aws_config)
describe_job_queues(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describejobqueues", params; aws_config=aws_config)

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

Describes a list of AWS Batch jobs.

# Arguments
- `jobs`: A list of up to 100 job IDs.

"""
describe_jobs(jobs; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describejobs", Dict{String, Any}("jobs"=>jobs); aws_config=aws_config)
describe_jobs(jobs, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/describejobs", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobs"=>jobs), params)); aws_config=aws_config)

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

Returns a list of AWS Batch jobs. You must specify only one of the following items:   A job
queue ID to return a list of jobs in that job queue   A multi-node parallel job ID to
return a list of that job's nodes   An array job ID to return a list of that job's children
  You can filter the results by job status with the jobStatus parameter. If you don't
specify a status, only RUNNING jobs are returned.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"arrayJobId"`: The job ID for an array job. Specifying an array job ID with this
  parameter lists all child jobs from within the specified array.
- `"jobQueue"`: The name or full Amazon Resource Name (ARN) of the job queue used to list
  jobs.
- `"jobStatus"`: The job status used to filter jobs in the specified queue. If you don't
  specify a status, only RUNNING jobs are returned.
- `"maxResults"`: The maximum number of results returned by ListJobs in paginated output.
  When this parameter is used, ListJobs only returns maxResults results in a single page
  along with a nextToken response element. The remaining results of the initial request can
  be seen by sending another ListJobs request with the returned nextToken value. This value
  can be between 1 and 100. If this parameter isn't used, then ListJobs returns up to 100
  results and a nextToken value if applicable.
- `"multiNodeJobId"`: The job ID for a multi-node parallel job. Specifying a multi-node
  parallel job ID with this parameter lists all nodes that are associated with the specified
  job.
- `"nextToken"`: The nextToken value returned from a previous paginated ListJobs request
  where maxResults was used and the results exceeded the value of that parameter. Pagination
  continues from the end of the previous results that returned the nextToken value. This
  value is null when there are no more results to return.  This token should be treated as an
  opaque identifier that's only used to retrieve the next items in a list and not for other
  programmatic purposes.
"""
list_jobs(; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/listjobs"; aws_config=aws_config)
list_jobs(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/listjobs", params; aws_config=aws_config)

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

Lists the tags for an AWS Batch resource. AWS Batch resources that support tags are compute
environments, jobs, job definitions, and job queues. ARNs for child jobs of array and
multi-node parallel (MNP) jobs are not supported.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) that identifies the resource that tags are
  listed for. AWS Batch resources that support tags are compute environments, jobs, job
  definitions, and job queues. ARNs for child jobs of array and multi-node parallel (MNP)
  jobs are not supported.

"""
list_tags_for_resource(resourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = batch("GET", "/v1/tags/$(resourceArn)"; aws_config=aws_config)
list_tags_for_resource(resourceArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("GET", "/v1/tags/$(resourceArn)", params; aws_config=aws_config)

"""
    register_job_definition(job_definition_name, type)
    register_job_definition(job_definition_name, type, params::Dict{String,<:Any})

Registers an AWS Batch job definition.

# Arguments
- `job_definition_name`: The name of the job definition to register. Up to 128 letters
  (uppercase and lowercase), numbers, hyphens, and underscores are allowed.
- `type`: The type of job definition. For more information about multi-node parallel jobs,
  see Creating a multi-node parallel job definition in the AWS Batch User Guide.  If the job
  is run on Fargate resources, then multinode isn't supported.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"containerProperties"`: An object with various properties specific to single-node
  container-based jobs. If the job definition's type parameter is container, then you must
  specify either containerProperties or nodeProperties.  If the job runs on Fargate
  resources, then you must not specify nodeProperties; use only containerProperties.
- `"nodeProperties"`: An object with various properties specific to multi-node parallel
  jobs. If you specify node properties for a job, it becomes a multi-node parallel job. For
  more information, see Multi-node Parallel Jobs in the AWS Batch User Guide. If the job
  definition's type parameter is container, then you must specify either containerProperties
  or nodeProperties.  If the job runs on Fargate resources, then you must not specify
  nodeProperties; use containerProperties instead.
- `"parameters"`: Default parameter substitution placeholders to set in the job definition.
  Parameters are specified as a key-value pair mapping. Parameters in a SubmitJob request
  override any corresponding parameter defaults from the job definition.
- `"platformCapabilities"`: The platform capabilities required by the job definition. If no
  value is specified, it defaults to EC2. To run the job on Fargate resources, specify
  FARGATE.
- `"propagateTags"`: Specifies whether to propagate the tags from the job or job definition
  to the corresponding Amazon ECS task. If no value is specified, the tags are not
  propagated. Tags can only be propagated to the tasks during task creation. For tags with
  the same name, job tags are given priority over job definitions tags. If the total number
  of combined tags from the job and job definition is over 50, the job is moved to the FAILED
  state.
- `"retryStrategy"`: The retry strategy to use for failed jobs that are submitted with this
  job definition. Any retry strategy that's specified during a SubmitJob operation overrides
  the retry strategy defined here. If a job is terminated due to a timeout, it isn't retried.
- `"tags"`: The tags that you apply to the job definition to help you categorize and
  organize your resources. Each tag consists of a key and an optional value. For more
  information, see Tagging AWS Resources in AWS Batch User Guide.
- `"timeout"`: The timeout configuration for jobs that are submitted with this job
  definition, after which AWS Batch terminates your jobs if they have not finished. If a job
  is terminated due to a timeout, it isn't retried. The minimum value for the timeout is 60
  seconds. Any timeout configuration that's specified during a SubmitJob operation overrides
  the timeout configuration defined here. For more information, see Job Timeouts in the AWS
  Batch User Guide.
"""
register_job_definition(jobDefinitionName, type; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/registerjobdefinition", Dict{String, Any}("jobDefinitionName"=>jobDefinitionName, "type"=>type); aws_config=aws_config)
register_job_definition(jobDefinitionName, type, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/registerjobdefinition", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobDefinitionName"=>jobDefinitionName, "type"=>type), params)); aws_config=aws_config)

"""
    submit_job(job_definition, job_name, job_queue)
    submit_job(job_definition, job_name, job_queue, params::Dict{String,<:Any})

Submits an AWS Batch job from a job definition. Parameters specified during SubmitJob
override parameters defined in the job definition.  Jobs run on Fargate resources don't run
for more than 14 days. After 14 days, the Fargate resources might no longer be available
and the job is terminated.

# Arguments
- `job_definition`: The job definition used by this job. This value can be one of name,
  name:revision, or the Amazon Resource Name (ARN) for the job definition. If name is
  specified without a revision then the latest active revision is used.
- `job_name`: The name of the job. The first character must be alphanumeric, and up to 128
  letters (uppercase and lowercase), numbers, hyphens, and underscores are allowed.
- `job_queue`: The job queue into which the job is submitted. You can specify either the
  name or the Amazon Resource Name (ARN) of the queue.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"arrayProperties"`: The array properties for the submitted job, such as the size of the
  array. The array size can be between 2 and 10,000. If you specify array properties for a
  job, it becomes an array job. For more information, see Array Jobs in the AWS Batch User
  Guide.
- `"containerOverrides"`: A list of container overrides in JSON format that specify the
  name of a container in the specified job definition and the overrides it should receive.
  You can override the default command for a container (that's specified in the job
  definition or the Docker image) with a command override. You can also override existing
  environment variables (that are specified in the job definition or Docker image) on a
  container or add new environment variables to it with an environment override.
- `"dependsOn"`: A list of dependencies for the job. A job can depend upon a maximum of 20
  jobs. You can specify a SEQUENTIAL type dependency without specifying a job ID for array
  jobs so that each child array job completes sequentially, starting at index 0. You can also
  specify an N_TO_N type dependency with a job ID for array jobs. In that case, each index
  child of this job must wait for the corresponding index child of each dependency to
  complete before it can begin.
- `"nodeOverrides"`: A list of node overrides in JSON format that specify the node range to
  target and the container overrides for that node range.  This parameter isn't applicable to
  jobs running on Fargate resources; use containerOverrides instead.
- `"parameters"`: Additional parameters passed to the job that replace parameter
  substitution placeholders that are set in the job definition. Parameters are specified as a
  key and value pair mapping. Parameters in a SubmitJob request override any corresponding
  parameter defaults from the job definition.
- `"propagateTags"`: Specifies whether to propagate the tags from the job or job definition
  to the corresponding Amazon ECS task. If no value is specified, the tags aren't propagated.
  Tags can only be propagated to the tasks during task creation. For tags with the same name,
  job tags are given priority over job definitions tags. If the total number of combined tags
  from the job and job definition is over 50, the job is moved to the FAILED state. When
  specified, this overrides the tag propagation setting in the job definition.
- `"retryStrategy"`: The retry strategy to use for failed jobs from this SubmitJob
  operation. When a retry strategy is specified here, it overrides the retry strategy defined
  in the job definition.
- `"tags"`: The tags that you apply to the job request to help you categorize and organize
  your resources. Each tag consists of a key and an optional value. For more information, see
  Tagging AWS Resources in AWS General Reference.
- `"timeout"`: The timeout configuration for this SubmitJob operation. You can specify a
  timeout duration after which AWS Batch terminates your jobs if they haven't finished. If a
  job is terminated due to a timeout, it isn't retried. The minimum value for the timeout is
  60 seconds. This configuration overrides any timeout configuration specified in the job
  definition. For array jobs, child jobs have the same timeout configuration as the parent
  job. For more information, see Job Timeouts in the Amazon Elastic Container Service
  Developer Guide.
"""
submit_job(jobDefinition, jobName, jobQueue; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/submitjob", Dict{String, Any}("jobDefinition"=>jobDefinition, "jobName"=>jobName, "jobQueue"=>jobQueue); aws_config=aws_config)
submit_job(jobDefinition, jobName, jobQueue, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/submitjob", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobDefinition"=>jobDefinition, "jobName"=>jobName, "jobQueue"=>jobQueue), params)); aws_config=aws_config)

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

Associates the specified tags to a resource with the specified resourceArn. If existing
tags on a resource aren't specified in the request parameters, they aren't changed. When a
resource is deleted, the tags associated with that resource are deleted as well. AWS Batch
resources that support tags are compute environments, jobs, job definitions, and job
queues. ARNs for child jobs of array and multi-node parallel (MNP) jobs are not supported.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource that tags are added to.
  AWS Batch resources that support tags are compute environments, jobs, job definitions, and
  job queues. ARNs for child jobs of array and multi-node parallel (MNP) jobs are not
  supported.
- `tags`: The tags that you apply to the resource to help you categorize and organize your
  resources. Each tag consists of a key and an optional value. For more information, see
  Tagging AWS Resources in AWS General Reference.

"""
tag_resource(resourceArn, tags; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/tags/$(resourceArn)", Dict{String, Any}("tags"=>tags); aws_config=aws_config)
tag_resource(resourceArn, tags, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/tags/$(resourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tags"=>tags), params)); aws_config=aws_config)

"""
    terminate_job(job_id, reason)
    terminate_job(job_id, reason, params::Dict{String,<:Any})

Terminates a job in a job queue. Jobs that are in the STARTING or RUNNING state are
terminated, which causes them to transition to FAILED. Jobs that have not progressed to the
STARTING state are cancelled.

# Arguments
- `job_id`: The AWS Batch job ID of the job to terminate.
- `reason`: A message to attach to the job that explains the reason for canceling it. This
  message is returned by future DescribeJobs operations on the job. This message is also
  recorded in the AWS Batch activity logs.

"""
terminate_job(jobId, reason; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/terminatejob", Dict{String, Any}("jobId"=>jobId, "reason"=>reason); aws_config=aws_config)
terminate_job(jobId, reason, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/terminatejob", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobId"=>jobId, "reason"=>reason), params)); aws_config=aws_config)

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

Deletes specified tags from an AWS Batch resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the resource from which to delete tags.
  AWS Batch resources that support tags are compute environments, jobs, job definitions, and
  job queues. ARNs for child jobs of array and multi-node parallel (MNP) jobs are not
  supported.
- `tag_keys`: The keys of the tags to be removed.

"""
untag_resource(resourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()) = batch("DELETE", "/v1/tags/$(resourceArn)", Dict{String, Any}("tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(resourceArn, tagKeys, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("DELETE", "/v1/tags/$(resourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tagKeys"=>tagKeys), params)); aws_config=aws_config)

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

Updates an AWS Batch compute environment.

# Arguments
- `compute_environment`: The name or full Amazon Resource Name (ARN) of the compute
  environment to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"computeResources"`: Details of the compute resources managed by the compute
  environment. Required for a managed compute environment. For more information, see Compute
  Environments in the AWS Batch User Guide.
- `"serviceRole"`: The full Amazon Resource Name (ARN) of the IAM role that allows AWS
  Batch to make calls to other AWS services on your behalf. For more information, see AWS
  Batch service IAM role in the AWS Batch User Guide. If your specified role has a path other
  than /, then you must either specify the full role ARN (this is recommended) or prefix the
  role name with the path.  Depending on how you created your AWS Batch service role, its ARN
  might contain the service-role path prefix. When you only specify the name of the service
  role, AWS Batch assumes that your ARN does not use the service-role path prefix. Because of
  this, we recommend that you specify the full ARN of your service role when you create
  compute environments.
- `"state"`: The state of the compute environment. Compute environments in the ENABLED
  state can accept jobs from a queue and scale in or out automatically based on the workload
  demand of its associated queues. If the state is ENABLED, then the AWS Batch scheduler can
  attempt to place jobs from an associated job queue on the compute resources within the
  environment. If the compute environment is managed, then it can scale its instances out or
  in automatically, based on the job queue demand. If the state is DISABLED, then the AWS
  Batch scheduler doesn't attempt to place jobs within the environment. Jobs in a STARTING or
  RUNNING state continue to progress normally. Managed compute environments in the DISABLED
  state don't scale out. However, they scale in to minvCpus value after instances become idle.
"""
update_compute_environment(computeEnvironment; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/updatecomputeenvironment", Dict{String, Any}("computeEnvironment"=>computeEnvironment); aws_config=aws_config)
update_compute_environment(computeEnvironment, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/updatecomputeenvironment", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("computeEnvironment"=>computeEnvironment), params)); aws_config=aws_config)

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

Updates a job queue.

# Arguments
- `job_queue`: The name or the Amazon Resource Name (ARN) of the job queue.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"computeEnvironmentOrder"`: Details the set of compute environments mapped to a job
  queue and their order relative to each other. This is one of the parameters used by the job
  scheduler to determine which compute environment should run a given job. Compute
  environments must be in the VALID state before you can associate them with a job queue. All
  of the compute environments must be either EC2 (EC2 or SPOT) or Fargate (FARGATE or
  FARGATE_SPOT); EC2 and Fargate compute environments can't be mixed.  All compute
  environments that are associated with a job queue must share the same architecture. AWS
  Batch doesn't support mixing compute environment architecture types in a single job queue.
- `"priority"`: The priority of the job queue. Job queues with a higher priority (or a
  higher integer value for the priority parameter) are evaluated first when associated with
  the same compute environment. Priority is determined in descending order, for example, a
  job queue with a priority value of 10 is given scheduling preference over a job queue with
  a priority value of 1. All of the compute environments must be either EC2 (EC2 or SPOT) or
  Fargate (FARGATE or FARGATE_SPOT); EC2 and Fargate compute environments cannot be mixed.
- `"state"`: Describes the queue's ability to accept new jobs. If the job queue state is
  ENABLED, it is able to accept jobs. If the job queue state is DISABLED, new jobs cannot be
  added to the queue, but jobs already in the queue can finish.
"""
update_job_queue(jobQueue; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/updatejobqueue", Dict{String, Any}("jobQueue"=>jobQueue); aws_config=aws_config)
update_job_queue(jobQueue, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = batch("POST", "/v1/updatejobqueue", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("jobQueue"=>jobQueue), params)); aws_config=aws_config)


text:
function _one(params::Vector{Float64}, pop::Pathogen.Population, i::Int64)
  return 1.0
end

function _zero(params::Vector{Float64}, pop::Pathogen.Population, i::Int64)
  return 0.0
end

function _constant(params::Vector{Float64}, pop::Pathogen.Population, i::Int64)
  return params[1]
end

function _coefficient(params::Vector{Float64}, pop::Pathogen.Population, i::Int64)
  return params[1] * pop.risks[i, :riskfactor1]
end

function _linear(params::Vector{Float64}, pop::Pathogen.Population, i::Int64)
  return params[1] + params[2] * pop.risks[i, :riskfactor1]
end

function _powerlaw(params::Vector{Float64}, pop::Pathogen.Population, i::Int64, k::Int64)
  α = params[1]
  β = params[2]
  d = pop.distances[k, i]
  return α * (d^(-β))
end

function _powerlaw_w_intercept(params::Vector{Float64}, pop::Pathogen.Population, i::Int64, k::Int64)
  α = params[1]
  β = params[2]
  γ = params[3]
  d = pop.distances[k, i]
  return (α * (d^(-β))) + γ
end

ext: jl


text:
# Testing whether SpectralGaussianSimulation does what I think it does
using GeoStats
using SpectralGaussianSimulation
using DirectGaussianSimulation
using Distances
include("CovFuncs.jl")
include("Utils.jl")

n = 100
N = n^2
NSamples = 2000

l = 0.5
# covfunc!(r) = exponential!(r, l)
covfunc!(r) = matern32!(r, l)

# simProblem = SimulationProblem(RegularGrid((-2.0, -2.0.0, 0.0), (1.0, 1.0), dims=(n, n)), :z => Float64, NSamples)

# simSolver = SpecGaussSim(:z => (variogram=MaternVariogram(range=l, order=1.5),))

# simSolution = solve(simProblem, simSolver)
# x = coordinates(domain(simSolution))
# Y = reduce(hcat, simSolution.realizations[:z])

x, Y = specSampleGrid2D(n, NSamples, 3, MaternVariogram(range=l, order=1.5))

Θ = covfunc!(Distances.pairwise(Euclidean(), x[:, inds], dims=2))
YExact = rand(MvNormal(Θ), NSamples)

ΘEmp = cov(YExact, dims=2)
ΘEmpApp = cov(Y[inds, :], dims=2)

@show norm(Θ - ΘEmp) / norm(Θ)
@show norm(Θ - ΘEmpApp) / norm(Θ)


text:
function mpi_init()
    !MPI.Initialized() && MPI.Init()
    comm = MPI.COMM_WORLD
    rank = MPI.Comm_rank(comm)
    size = MPI.Comm_size(comm)
    return comm, rank, size
end

const process_functions = Dict(
    "save_basic_features_instances" => save_basic_features_instances!,
    "save_single_features_instances" => save_single_features_instances!,
    "save_features_instances" => save_features_instances!,
    "serialize_instances" => serialize_instances!,
    "generate_decompositions" => generate_decompositions!,
    "merge_decompositions" => merge_decompositions!,
    "combine_decompositions" => combine_decompositions!,
    "delete_duplicates" => delete_duplicates!,
    "export_matpowerm_instances" => export_matpowerm_instances!,
    "solve_decompositions" => solve_decompositions!,
    "load_matctr_instances" => load_matctr_instances!
)
const table_to_process = Dict(
    "save_basic_features_instances" => "instances",
    "save_single_features_instances" => "instances",
    "save_features_instances" => "instances",
    "serialize_instances" => "instances",
    "generate_decompositions" => "instances",
    "merge_decompositions" => "decompositions",
    "combine_decompositions" => "instances",
    "delete_duplicates" => "instances",
    "export_matpowerm_instances" => "instances",
    "solve_decompositions" => "decompositions",
    "load_matctr_instances" => "instances"
)

function execute_process_mpi(db::SQLite.DB, process_type, log_dir; kwargs...)
    comm, rank, size = mpi_init()
    nb_process = size - 1
    log_dir = joinpath(log_dir, process_type)
    !isdir(log_dir) && mkpath(log_dir)
    redirect_stdio(stdout=joinpath(log_dir, "$rank.txt"), stderr=joinpath(log_dir, "$rank.txt")) do
        println("Rank: $rank / $size")
        if rank == 0
            table = table_to_process[process_type]
            ids = get_table_ids(db, table)
            println("Number of ids: $(length(ids))")
            if isempty(ids)
                println("Nothing to process.")
                for i in 1:nb_process
                    println("Sending to $(i - 1): [-1]")
                    MPI.send([-1], i, 0, comm)
                end
            else
                nb_ids_per_chunk = Int(floor(length(ids) / nb_process))
                for i in 1:nb_process
                    start = (i - 1) * nb_ids_per_chunk + 1
                    stop = start + nb_ids_per_chunk
                    stop = stop > length(ids) ? length(ids) : stop
                    if i == nb_process && stop != length(ids)
                        stop = length(ids)
                    end
                    chunk = ids[start:stop]
                    println("Sending [$start -> $stop] to $(i):\n$chunk\n")
                    if isempty(chunk)
                        chunck = [-1]
                    end
                    MPI.send(chunk, i, 0, comm)
                end
            end
            process_done = zeros(Bool, nb_process)
            while !all(process_done)
                for i in 1:nb_process
                    has_recieved, status = MPI.Iprobe(i, 0, comm)
                    if has_recieved
                        query, status = MPI.recv(i, 0, comm)
                        query = String(query)
                        println("Recieved query: $query")
                        if query == "over"
                            println("Process $i over.")
                            process_done[i] = true
                        else
                            try
                                DBInterface.execute(db, query)
                            catch e
                                @warn query
                                rethrow()
                            end
                        end
                    end
                end
            end
            MPI.Barrier(comm)
            MPI.Finalize()
        else
            indexes, status = MPI.recv(0, 0, comm)
            if indexes == [-1]
                println("Nothing to process. Exiting.")
                MPI.Finalize()
                return
            end
            println("Recieved: $indexes")
            process_functions[process_type](db; subset=indexes, kwargs...)
            println("Process done.")
            MPI.send(['o', 'v', 'e', 'r'], 0, 0, comm)
            println("Finalize.")
            MPI.Barrier(comm)
            MPI.Finalize()
        end
    end
end

max_stars_repo_name: charlyalizadeh/PowerFlowNetworks

max_issues_repo_name: charlyalizadeh/PowerFlowNetworks

max_forks_repo_name: charlyalizadeh/PowerFlowNetworks


text:
include("solver_preamble.jl")
import SDPA
factory = with_optimizer(SDPA.Optimizer)
config = MOI.Test.TestConfig(atol=1e-5, rtol=1e-5, query=false)
@testset "Linear" begin
    Tests.linear_test(factory, config, [
        # cholesky miss condition :: not positive definite :: info = 7 :: line 785 in sdpa_linear.cpp
        # There are some possibilities. :: line 786 in sdpa_linear.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 787 in sdpa_linear.cpp
        # 2. The input problem may not have (any) interior-points :: line 788 in sdpa_linear.cpp
        # 3. Input matrices are linearly dependent :: line 789 in sdpa_linear.cpp
        # dsos_cheby_bivariate_quadratic: Test Failed at /home/blegat/.julia/dev/SumOfSquares/test/Tests/quadratic.jl:38
        #   Expression: termination_status(model) == MOI.OPTIMAL
        #    Evaluated: MathOptInterface.SLOW_PROGRESS == MathOptInterface.OPTIMAL
        "dsos_cheby_bivariate_quadratic", "dsos_cheby_univariate_quadratic", "dsos_scaled_bivariate_quadratic", "dsos_scaled_univariate_quadratic",
        # cholesky miss condition :: not positive definite :: info = 13 :: line 785 in sdpa_linear.cpp
        # There are some possibilities. :: line 786 in sdpa_linear.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 787 in sdpa_linear.cpp
        # 2. The input problem may not have (any) interior-points :: line 788 in sdpa_linear.cpp
        # 3. Input matrices are linearly dependent :: line 789 in sdpa_linear.cpp
        # dsos_univariate_sum:
        #   Expression: termination_status(model) == MOI.OPTIMAL
        #    Evaluated: MathOptInterface.ITERATION_LIMIT == MathOptInterface.OPTIMAL
        "dsos_univariate_sum",
        # The termination status is
        # `INFEASIBLE_OR_UNBOUNDED` instead of `INFEASIBLE`.
        "dsos_horn",
        # cholesky miss condition :: not positive definite :: info = 32 :: line 785 in sdpa_linear.cpp
        # There are some possibilities. :: line 786 in sdpa_linear.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 787 in sdpa_linear.cpp
        # 2. The input problem may not have (any) interior-points :: line 788 in sdpa_linear.cpp
        # 3. Input matrices are linearly dependent :: line 789 in sdpa_linear.cpp
        "dsos_concave_then_convex_cubic",
        # The termination status is
        # `SLOW_PROGRESS` instead of `INFEASIBLE`.
        # cholesky miss condition :: not positive definite :: info = 7 :: line 785 in sdpa_linear.cpp
        # There are some possibilities. :: line 786 in sdpa_linear.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 787 in sdpa_linear.cpp
        # 2. The input problem may not have (any) interior-points :: line 788 in sdpa_linear.cpp
        # 3. Input matrices are linearly dependent :: line 789 in sdpa_linear.cpp
        "dsos_bivariate_quadratic", "dsos_univariate_quadratic",
        # Cholesky failed by NUMERICAL ERROR :: line 558 in sdpa_chordal.cpp
        # There are some possibilities. :: line 559 in sdpa_chordal.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 560 in sdpa_chordal.cpp
        # 2. The input problem may not have (any) interior-points :: line 561 in sdpa_chordal.cpp
        # 3. Input matrices are linearly dependent :: line 562 in sdpa_chordal.cpp
        "dsos_options_pricing"])
end
@testset "SDP" begin
    # With `sos_concave_then_convex_cubic`, we have the same error than with
    # `dsos_concave_then_convex_cubic`.
    Tests.sd_test(factory, config, [
        "sos_concave_then_convex_cubic",
        "quartic_infeasible_lyapunov_switched_system", "quartic_infeasible_scaled_lyapunov_switched_system",
        # Strange behavior : primal < dual :: line 144 in sdpa_solve.cpp
        # pdINF criteria :: line 1192 in sdpa_parts.cpp
        "maxcut",
        # Strange behavior : primal < dual :: line 144 in sdpa_solve.cpp
        # dUNBD criteria :: line 1220 in sdpa_parts.cpp
        # dUNBD criteria :: line 1220 in sdpa_parts.cpp
        # dUNBD criteria :: line 1220 in sdpa_parts.cpp
        # Step length is too small.  :: line 198 in sdpa_dataset.cpp
        # cannot move: step length is too short :: line 176 in sdpa_solve.cpp
        "sos_horn",
        # Strange behavior : primal < dual :: line 144 in sdpa_solve.cpp
        # Strange behavior : primal < dual :: line 144 in sdpa_solve.cpp
        # cholesky miss condition :: not positive definite :: info = 326 :: line 785 in sdpa_linear.cpp
        # There are some possibilities. :: line 786 in sdpa_linear.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 787 in sdpa_linear.cpp
        # 2. The input problem may not have (any) interior-points :: line 788 in sdpa_linear.cpp
        # 3. Input matrices are linearly dependent :: line 789 in sdpa_linear.cpp
        # dUNBD criteria :: line 1220 in sdpa_parts.cpp
        # dUNBD criteria :: line 1220 in sdpa_parts.cpp
        # dUNBD criteria :: line 1220 in sdpa_parts.cpp
        # cholesky miss condition :: not positive definite :: info = 13 :: line 785 in sdpa_linear.cpp
        # There are some possibilities. :: line 786 in sdpa_linear.cpp
        # 1. SDPA terminates due to inaccuracy of numerical error :: line 787 in sdpa_linear.cpp
        # 2. The input problem may not have (any) interior-points :: line 788 in sdpa_linear.cpp
        # 3. Input matrices are linearly dependent :: line 789 in sdpa_linear.cpp
        "sos_options_pricing"
    ])
end


text:
"""
Utils module containing helper functions and macros for, e.g., block
decompositions and visualization.
"""
module Utils

using LazySets, MathematicalSystems, HybridSystems

include("../compat.jl")

using Reachability: Options

# Visualization
export print_sparsity,
       plot_sparsity,
       print_last_interval,
       range_last_x_percent,
       add_plot_labels

# Block Structure
export @block_id,
       add_dimension,
       convert_partition

# Usability
export @filename_to_png,
       @relpath

# internal conversion
export template_direction_symbols,
       matrix_conversion,
       matrix_conversion_lazy_explicit

# internal normalization
export normalize,
       distribute_initial_set

# Extension of MathematicalSystems for use inside Reachability.jl
include("systems.jl")

# normalization
include("normalization.jl")

# coordinate transformations
include("transformations.jl")

# abstract solution type
include("AbstractSolution.jl")

"""
    @filename_to_png

Expands into the current filename and transforms the file extension to `png`.

### Examples

If this macro is executed from a script named my_model.jl, then:

```julia
julia> plot_name = @filename_to_png
my_model.png
```
"""
macro filename_to_png()
    return :(split(split(@__FILE__, "/")[end], ".")[1] * ".png")
end

"""
    @block_id v

Returns the block number associated to a given variable/dimension.

It is assumed that the block size is two and that blocks are ordered from top
(first two rows) to bottom (last two rows) in a matrix.

### Examples

```julia
julia> @block_id 4
2
```
"""
macro block_id(v::Int64)
    return :(div($v, 2) + mod($v, 2))
end

"""
    range_last_x_percent(N, first=0, step=1)

Returns a StepRange for the last x percent of the range 1:N.

### Input

- `N`     -- range length
- `first` -- relative starting index (in percent); a number from [0, 99]
                (default: 0)
- `step`  -- step size (default: 1)

EXAMPLE:

```julia
julia> range_last_x_percent(200, 20, 5)
160:5:200
```
"""
function range_last_x_percent(N::Int64, first::Int64=0, step::Int64=1)
    if first == 0
        start = 1
    elseif first > 0 && first < 100
        start = (100-first)*(floor(Int, N/100))
        start = max(1, start + ((N-start) % step)) # last index should be N
    else
        throw(DomainError("Start index must be in [0, 99]."))
    end
    return start : step : N
end

"""
    print_last_interval(RsetsProj)

Prints the max/min values for the last element in the array of two-dimensional
sets in the second dimension.

### Input

- `RsetsProj` -- (projected) reach set given as an array of two-dimensional sets
"""
function print_last_interval(RsetsProj::Vector{<:LazySet})
    last_polygon = RsetsProj[end]
    min_val = -ρ([0., -1.], last_polygon)
    max_val = ρ([0., 1.], last_polygon)

    println("max/min values for the last time step:")
    @printf "[ %.6e ; %.6e ]\n" max_val min_val
end

"""
    print_sparsity(ϕ, name)

Prints the sparsity of a matrix ϕ.

### Input

- `ϕ`    -- matrix
- `name` -- (optional, default: "") matrix name (for the output message)
"""
function print_sparsity(ϕ::AbstractMatrix{Float64}, name::String="")
    zero_blocks = 0
    b = (Int64)(size(ϕ, 1) / 2)
    @inline F(bi::Int64, bj::Int64) = ϕ[(2*bi-1):(2*bi), (2*bj-1):(2*bj)]
    for bj in 1:b
        for bi in 1:b
            if iszero(F(bi, bj))
                zero_blocks += 1
            end
        end
    end
    all_blocks = b^2

    print_sparsity_message(name, zero_blocks, all_blocks)
end

function print_sparsity(ϕ::SparseMatrixExp{Float64}, name::String="")
    zero_blocks = 0
    b = (Int64)(size(ϕ, 1) / 2)
    @inline F(bi::Int64) = get_rows(ϕ, (2*bi-1):2*bi)
    for bi in 1:b
        block_row_bi = F(bi)
        for bj in 1:b
            if iszero(block_row_bi[1:2, (2*bj-1):(2*bj)])
                zero_blocks += 1
            end
        end
    end
    all_blocks = b^2

    print_sparsity_message(name, zero_blocks, all_blocks)
end

@inline function print_sparsity_message(name::String, zero_blocks::Int, all_blocks::Int)
    sparsity = zero_blocks / all_blocks * 100.
    @printf "sparsity %s: %.3f %% (%d/%d zero blocks)\n" name sparsity zero_blocks all_blocks
end

"""
    plot_sparsity(ϕ, name, [plot_backend])

Plots the sparsity of a matrix ϕ.

### Input

- `ϕ`            -- dense or sparse matrix
- `name`         -- the name of the matrix (for the file name)
- `plot_backend` -- (optional, default: `''`): name of the plotting backend;
                      valid values are:

                 -  `'pyplot_savefig'` -- use PyPlot package, save to a file

                 -  `'pyplot_inline'` -- use PyPlot package, showing in external program

                 - `''` -- (empty string), no plotting
"""
function plot_sparsity(ϕ::Union{AbstractMatrix{Float64}, SparseMatrixExp{Float64}}, name::String, plot_backend::String="")
    if isempty(plot_backend)
        # plot nothing
    elseif plot_backend in ["pyplot_inline", "pyplot_savefig"]
        if !isdefined(:PyPlot)
              error("this backend requires that your script loads the PyPlot module")
        end
        eval(Expr(:using, :PyPlot))
        if plot_backend == "pyplot_savefig"
            PyPlot.ioff()  # turn off interactive plotting
            fig = PyPlot.figure()
        end
#       PyPlot.gray()
        if ϕ isa SparseMatrixExp
            explicit_matrix = get_rows(ϕ, 1:size(ϕ, 1))
            PyPlot.spy(explicit_matrix)
        else
            PyPlot.spy(ϕ)
        end
        if plot_backend == "pyplot_savefig"
            PyPlot.savefig(name * "_sparsity.png")
            PyPlot.close(fig)
        end
    else
        warn("plotting backend $plot_backend is not supported for sparsity plots")
    end
end

"""
    add_plot_labels(plot_vars, [project_output], [plot_labels], [use_subindices])

Creates the axis labels for plotting.

### Input

- `plot_vars`      -- variable indices for plotting (0 stands for time)
- `project_output` -- (optional, default: false) flag indicating if the y axis
                      is an output function
- `plot_labels`    -- (optional, default: empty strings) vector of plot labels,
                      empty strings are ignored
- `use_subindices` -- (optional, default: `true`) if `true`, use subindices
                      for the labels, e.g. `x1` is displayed as `x₁`

### Output

Axis labels with the specified properties.
"""
function add_plot_labels(plot_vars::Vector{Int64};
                         project_output::Bool=false,
                         plot_labels::Vector{String}=["", ""],
                         use_subindices::Bool=true)
    labels = copy(plot_labels)

    # convert integer to subindex, such that e.g. x1 is displayed as x₁
    if use_subindices
        tosubindex = i -> join(Char.(0x2080 .+ convert.(UInt16, digits(i)[end:-1:1])))
    else
        tosubindex = i -> string(i)
    end
    if isempty(labels[1])
        xaxis = plot_vars[1]
        labels[1] = (xaxis == 0) ? "t" : "x" * tosubindex(xaxis)
    end
    if isempty(labels[2])
        yaxis = plot_vars[2]
        labels[2] = (yaxis == 0) ? "t" : (project_output ? "y" : "x" * tosubindex(yaxis))
    end
    return labels
end

# Taken from an older Julia version, see Reachability #414 or
# https://github.com/JuliaLang/julia/blob/788ce677f6043b52caf97323f9de4d6975882561/base/loading.jl#L485
function source_path(default::Union{AbstractString, Nothing}="")
    t = current_task()
    while true
        s = t.storage
        if !isa(s, Nothing) && haskey(s, :SOURCE_PATH)
            return s[:SOURCE_PATH]
        end
        if isa(t, t.parent)
            return default
        end
        t = t.parent
    end
end

"""
   @relpath(name)

Returns the path of the current code file.

### Input

- `name` -- file name

### Output

A string.

### Notes

This macro is handy, e.g., when calling a function that uses a file relative to
its own location.

For example, suppose that the folder `/home/projects/P1` contains the script `s.jl`
which loads the data `x.dat` and performs some calculations with it:

```julia
# file /home/projects/P1/s.jl
d = open(@relpath "x.dat")
# do stuff with d
```
In this example, the macro `@relpath "s.jl"` evaluates to the string
`/home/projects/P1/x.dat`. The script `s.jl` can be evaluated from the command line
from any folder, e.g. suppose that the path working directory is `/home/projects`,
then one can do `julia -e 'include("P1/s.jl")'` without having to change `s.jl`.
"""
macro relpath(name::String)
    return quote
        f = source_path()
        if f == nothing
            pathdir = ""
        else
            pathdir = join(split(f, "/")[1:end-1], "/")
        end
        if !isempty(pathdir)
            pathdir = pathdir * "/"
        end
        pathdir * $name
    end
end

"""
    convert_partition(partition::AbstractVector{<:AbstractVector{Int}})::Union{
        Vector{Int}, Vector{UnitRange{Int}}, Vector{Union{UnitRange{Int}, Int}}}

Convert a partition data structure to a more efficient representation where a 1D
block is represented by the index and a higher-dimensional block is represented
by a `UnitRange{Int}`.

### Input

- `partition` -- partition data structure (a vector of integer vectors)

### Output

New partition representation.

### Notes

We cannot mix `Int` and `UnitRange{Int}`, so we only create `Int` partitions if
all blocks are 1D.
"""
function convert_partition(partition::AbstractVector{<:AbstractVector{Int}})::Union{
        Vector{Int},
        Vector{UnitRange{Int}}}
    # are there kD blocks for k > 1?
    has_kD_blocks = false
    for (i, block) in enumerate(partition)
        if length(block) > 1
            has_kD_blocks = true
            break
        end
    end

    # use optimal partition type
    if !has_kD_blocks
        # only 1D blocks
        partition_out = Vector{Int}(undef, length(partition))
        for (i, block) in enumerate(partition)
            partition_out[i] = block[1]
        end
    else
        # at least one kD block for k > 1
        partition_out = Vector{UnitRange{Int}}(undef, length(partition))
        for (i, block) in enumerate(partition)
            partition_out[i] = block[1]:block[end]
        end
    end

    return partition_out
end

# sparse/dense matrix conversion
function matrix_conversion(Δ, options; A_passed=nothing)
    if A_passed == nothing
        A = Δ.s.A
        create_new_system = false
    else
        A = A_passed
        create_new_system = true
    end

    if options[:assume_sparse]
        if A isa SparseMatrixExp
            # ignore this case
            A_new = A
        elseif !hasmethod(sparse, Tuple{typeof(A)})
            info("`assume_sparse` option cannot be applied to a matrix of " *
                 "type $(typeof(A)) and will be ignored")
            A_new = A
        elseif !(A isa AbstractSparseMatrix)
            # convert to sparse matrix
            A_new = sparse(A)
            create_new_system = true
        else
            # A is already sparse
            A_new = A
        end
    else
        A_new = A
    end
    if create_new_system
        if inputdim(Δ.s) > 0
            Δ = IVP(CLCDS(A_new, Δ.s.B, stateset(Δ.s), inputset(Δ)), Δ.x0)
        else
            Δ = IVP(CLDS(A_new, stateset(Δ.s)), Δ.x0)
        end
    end
    return Δ
end

end # module