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

"""
    batch_delete_recipe_version(recipe_versions, name)
    batch_delete_recipe_version(recipe_versions, name, params::Dict{String,<:Any})

Deletes one or more versions of a recipe at a time. The entire request will be rejected if:
  The recipe does not exist.   There is an invalid version identifier in the list of
versions.   The version list is empty.   The version list size exceeds 50.   The version
list contains duplicate entries.   The request will complete successfully, but with partial
failures, if:   A version does not exist.   A version is being used by a job.   You specify
LATEST_WORKING, but it's being used by a project.   The version fails to be deleted.   The
LATEST_WORKING version will only be deleted if the recipe has no other versions. If you try
to delete LATEST_WORKING while other versions exist (or if they can't be deleted), then
LATEST_WORKING will be listed as partial failure in the response.

# Arguments
- `recipe_versions`: An array of version identifiers, for the recipe versions to be
  deleted. You can specify numeric versions (X.Y) or LATEST_WORKING. LATEST_PUBLISHED is not
  supported.
- `name`: The name of the recipe whose versions are to be deleted.

"""
batch_delete_recipe_version(RecipeVersions, name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipes/$(name)/batchDeleteRecipeVersion", Dict{String, Any}("RecipeVersions"=>RecipeVersions); aws_config=aws_config)
batch_delete_recipe_version(RecipeVersions, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipes/$(name)/batchDeleteRecipeVersion", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("RecipeVersions"=>RecipeVersions), params)); aws_config=aws_config)

"""
    create_dataset(input, name)
    create_dataset(input, name, params::Dict{String,<:Any})

Creates a new DataBrew dataset.

# Arguments
- `input`:
- `name`: The name of the dataset to be created. Valid characters are alphanumeric (A-Z,
  a-z, 0-9), hyphen (-), period (.), and space.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Format"`: The file format of a dataset that is created from an S3 file or folder.
- `"FormatOptions"`:
- `"PathOptions"`: A set of options that defines how DataBrew interprets an S3 path of the
  dataset.
- `"Tags"`: Metadata tags to apply to this dataset.
"""
create_dataset(Input, Name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/datasets", Dict{String, Any}("Input"=>Input, "Name"=>Name); aws_config=aws_config)
create_dataset(Input, Name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/datasets", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Input"=>Input, "Name"=>Name), params)); aws_config=aws_config)

"""
    create_profile_job(dataset_name, name, output_location, role_arn)
    create_profile_job(dataset_name, name, output_location, role_arn, params::Dict{String,<:Any})

Creates a new job to analyze a dataset and create its data profile.

# Arguments
- `dataset_name`: The name of the dataset that this job is to act upon.
- `name`: The name of the job to be created. Valid characters are alphanumeric (A-Z, a-z,
  0-9), hyphen (-), period (.), and space.
- `output_location`:
- `role_arn`: The Amazon Resource Name (ARN) of the AWS Identity and Access Management
  (IAM) role to be assumed when DataBrew runs the job.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"EncryptionKeyArn"`: The Amazon Resource Name (ARN) of an encryption key that is used to
  protect the job.
- `"EncryptionMode"`: The encryption mode for the job, which can be one of the following:
   SSE-KMS - SSE-KMS - Server-side encryption with AWS KMS-managed keys.    SSE-S3 -
  Server-side encryption with keys managed by Amazon S3.
- `"JobSample"`: Sample configuration for profile jobs only. Determines the number of rows
  on which the profile job will be executed. If a JobSample value is not provided, the
  default value will be used. The default value is CUSTOM_ROWS for the mode parameter and
  20000 for the size parameter.
- `"LogSubscription"`: Enables or disables Amazon CloudWatch logging for the job. If
  logging is enabled, CloudWatch writes one log stream for each job run.
- `"MaxCapacity"`: The maximum number of nodes that DataBrew can use when the job processes
  data.
- `"MaxRetries"`: The maximum number of times to retry the job after a job run fails.
- `"Tags"`: Metadata tags to apply to this job.
- `"Timeout"`: The job's timeout in minutes. A job that attempts to run longer than this
  timeout period ends with a status of TIMEOUT.
"""
create_profile_job(DatasetName, Name, OutputLocation, RoleArn; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/profileJobs", Dict{String, Any}("DatasetName"=>DatasetName, "Name"=>Name, "OutputLocation"=>OutputLocation, "RoleArn"=>RoleArn); aws_config=aws_config)
create_profile_job(DatasetName, Name, OutputLocation, RoleArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/profileJobs", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DatasetName"=>DatasetName, "Name"=>Name, "OutputLocation"=>OutputLocation, "RoleArn"=>RoleArn), params)); aws_config=aws_config)

"""
    create_project(dataset_name, name, recipe_name, role_arn)
    create_project(dataset_name, name, recipe_name, role_arn, params::Dict{String,<:Any})

Creates a new DataBrew project.

# Arguments
- `dataset_name`: The name of an existing dataset to associate this project with.
- `name`: A unique name for the new project. Valid characters are alphanumeric (A-Z, a-z,
  0-9), hyphen (-), period (.), and space.
- `recipe_name`: The name of an existing recipe to associate with the project.
- `role_arn`: The Amazon Resource Name (ARN) of the AWS Identity and Access Management
  (IAM) role to be assumed for this request.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Sample"`:
- `"Tags"`: Metadata tags to apply to this project.
"""
create_project(DatasetName, Name, RecipeName, RoleArn; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/projects", Dict{String, Any}("DatasetName"=>DatasetName, "Name"=>Name, "RecipeName"=>RecipeName, "RoleArn"=>RoleArn); aws_config=aws_config)
create_project(DatasetName, Name, RecipeName, RoleArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/projects", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DatasetName"=>DatasetName, "Name"=>Name, "RecipeName"=>RecipeName, "RoleArn"=>RoleArn), params)); aws_config=aws_config)

"""
    create_recipe(name, steps)
    create_recipe(name, steps, params::Dict{String,<:Any})

Creates a new DataBrew recipe.

# Arguments
- `name`: A unique name for the recipe. Valid characters are alphanumeric (A-Z, a-z, 0-9),
  hyphen (-), period (.), and space.
- `steps`: An array containing the steps to be performed by the recipe. Each recipe step
  consists of one recipe action and (optionally) an array of condition expressions.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description for the recipe.
- `"Tags"`: Metadata tags to apply to this recipe.
"""
create_recipe(Name, Steps; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipes", Dict{String, Any}("Name"=>Name, "Steps"=>Steps); aws_config=aws_config)
create_recipe(Name, Steps, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipes", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "Steps"=>Steps), params)); aws_config=aws_config)

"""
    create_recipe_job(name, outputs, role_arn)
    create_recipe_job(name, outputs, role_arn, params::Dict{String,<:Any})

Creates a new job to transform input data, using steps defined in an existing AWS Glue
DataBrew recipe

# Arguments
- `name`: A unique name for the job. Valid characters are alphanumeric (A-Z, a-z, 0-9),
  hyphen (-), period (.), and space.
- `outputs`: One or more artifacts that represent the output from running the job.
- `role_arn`: The Amazon Resource Name (ARN) of the AWS Identity and Access Management
  (IAM) role to be assumed when DataBrew runs the job.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DatasetName"`: The name of the dataset that this job processes.
- `"EncryptionKeyArn"`: The Amazon Resource Name (ARN) of an encryption key that is used to
  protect the job.
- `"EncryptionMode"`: The encryption mode for the job, which can be one of the following:
   SSE-KMS - Server-side encryption with keys managed by AWS KMS.    SSE-S3 - Server-side
  encryption with keys managed by Amazon S3.
- `"LogSubscription"`: Enables or disables Amazon CloudWatch logging for the job. If
  logging is enabled, CloudWatch writes one log stream for each job run.
- `"MaxCapacity"`: The maximum number of nodes that DataBrew can consume when the job
  processes data.
- `"MaxRetries"`: The maximum number of times to retry the job after a job run fails.
- `"ProjectName"`: Either the name of an existing project, or a combination of a recipe and
  a dataset to associate with the recipe.
- `"RecipeReference"`:
- `"Tags"`: Metadata tags to apply to this job.
- `"Timeout"`: The job's timeout in minutes. A job that attempts to run longer than this
  timeout period ends with a status of TIMEOUT.
"""
create_recipe_job(Name, Outputs, RoleArn; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipeJobs", Dict{String, Any}("Name"=>Name, "Outputs"=>Outputs, "RoleArn"=>RoleArn); aws_config=aws_config)
create_recipe_job(Name, Outputs, RoleArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipeJobs", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Name"=>Name, "Outputs"=>Outputs, "RoleArn"=>RoleArn), params)); aws_config=aws_config)

"""
    create_schedule(cron_expression, name)
    create_schedule(cron_expression, name, params::Dict{String,<:Any})

Creates a new schedule for one or more DataBrew jobs. Jobs can be run at a specific date
and time, or at regular intervals.

# Arguments
- `cron_expression`: The date or dates and time or times when the jobs are to be run. For
  more information, see Cron expressions in the AWS Glue DataBrew Developer Guide.
- `name`: A unique name for the schedule. Valid characters are alphanumeric (A-Z, a-z,
  0-9), hyphen (-), period (.), and space.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobNames"`: The name or names of one or more jobs to be run.
- `"Tags"`: Metadata tags to apply to this schedule.
"""
create_schedule(CronExpression, Name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/schedules", Dict{String, Any}("CronExpression"=>CronExpression, "Name"=>Name); aws_config=aws_config)
create_schedule(CronExpression, Name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/schedules", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CronExpression"=>CronExpression, "Name"=>Name), params)); aws_config=aws_config)

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

Deletes a dataset from DataBrew.

# Arguments
- `name`: The name of the dataset to be deleted.

"""
delete_dataset(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/datasets/$(name)"; aws_config=aws_config)
delete_dataset(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/datasets/$(name)", params; aws_config=aws_config)

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

Deletes the specified DataBrew job.

# Arguments
- `name`: The name of the job to be deleted.

"""
delete_job(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/jobs/$(name)"; aws_config=aws_config)
delete_job(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/jobs/$(name)", params; aws_config=aws_config)

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

Deletes an existing DataBrew project.

# Arguments
- `name`: The name of the project to be deleted.

"""
delete_project(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/projects/$(name)"; aws_config=aws_config)
delete_project(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/projects/$(name)", params; aws_config=aws_config)

"""
    delete_recipe_version(name, recipe_version)
    delete_recipe_version(name, recipe_version, params::Dict{String,<:Any})

Deletes a single version of a DataBrew recipe.

# Arguments
- `name`: The name of the recipe.
- `recipe_version`: The version of the recipe to be deleted. You can specify a numeric
  versions (X.Y) or LATEST_WORKING. LATEST_PUBLISHED is not supported.

"""
delete_recipe_version(name, recipeVersion; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/recipes/$(name)/recipeVersion/$(recipeVersion)"; aws_config=aws_config)
delete_recipe_version(name, recipeVersion, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/recipes/$(name)/recipeVersion/$(recipeVersion)", params; aws_config=aws_config)

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

Deletes the specified DataBrew schedule.

# Arguments
- `name`: The name of the schedule to be deleted.

"""
delete_schedule(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/schedules/$(name)"; aws_config=aws_config)
delete_schedule(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("DELETE", "/schedules/$(name)", params; aws_config=aws_config)

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

Returns the definition of a specific DataBrew dataset.

# Arguments
- `name`: The name of the dataset to be described.

"""
describe_dataset(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/datasets/$(name)"; aws_config=aws_config)
describe_dataset(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/datasets/$(name)", params; aws_config=aws_config)

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

Returns the definition of a specific DataBrew job.

# Arguments
- `name`: The name of the job to be described.

"""
describe_job(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs/$(name)"; aws_config=aws_config)
describe_job(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs/$(name)", params; aws_config=aws_config)

"""
    describe_job_run(name, run_id)
    describe_job_run(name, run_id, params::Dict{String,<:Any})

Represents one run of a DataBrew job.

# Arguments
- `name`: The name of the job being processed during this run.
- `run_id`: The unique identifier of the job run.

"""
describe_job_run(name, runId; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs/$(name)/jobRun/$(runId)"; aws_config=aws_config)
describe_job_run(name, runId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs/$(name)/jobRun/$(runId)", params; aws_config=aws_config)

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

Returns the definition of a specific DataBrew project.

# Arguments
- `name`: The name of the project to be described.

"""
describe_project(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/projects/$(name)"; aws_config=aws_config)
describe_project(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/projects/$(name)", params; aws_config=aws_config)

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

Returns the definition of a specific DataBrew recipe corresponding to a particular version.

# Arguments
- `name`: The name of the recipe to be described.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"recipeVersion"`: The recipe version identifier. If this parameter isn't specified, then
  the latest published version is returned.
"""
describe_recipe(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/recipes/$(name)"; aws_config=aws_config)
describe_recipe(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/recipes/$(name)", params; aws_config=aws_config)

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

Returns the definition of a specific DataBrew schedule.

# Arguments
- `name`: The name of the schedule to be described.

"""
describe_schedule(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/schedules/$(name)"; aws_config=aws_config)
describe_schedule(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/schedules/$(name)", params; aws_config=aws_config)

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

Lists all of the DataBrew datasets.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: The token returned by a previous call to retrieve the next set of results.
"""
list_datasets(; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/datasets"; aws_config=aws_config)
list_datasets(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/datasets", params; aws_config=aws_config)

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

Lists all of the previous runs of a particular DataBrew job.

# Arguments
- `name`: The name of the job.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: The token returned by a previous call to retrieve the next set of results.
"""
list_job_runs(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs/$(name)/jobRuns"; aws_config=aws_config)
list_job_runs(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs/$(name)/jobRuns", params; aws_config=aws_config)

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

Lists all of the DataBrew jobs that are defined.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"datasetName"`: The name of a dataset. Using this parameter indicates to return only
  those jobs that act on the specified dataset.
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: A token generated by DataBrew that specifies where to continue pagination
  if a previous request was truncated. To get the next set of pages, pass in the NextToken
  value from the response object of the previous page call.
- `"projectName"`: The name of a project. Using this parameter indicates to return only
  those jobs that are associated with the specified project.
"""
list_jobs(; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs"; aws_config=aws_config)
list_jobs(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/jobs", params; aws_config=aws_config)

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

Lists all of the DataBrew projects that are defined.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: The token returned by a previous call to retrieve the next set of results.
"""
list_projects(; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/projects"; aws_config=aws_config)
list_projects(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/projects", params; aws_config=aws_config)

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

Lists the versions of a particular DataBrew recipe, except for LATEST_WORKING.

# Arguments
- `name`: The name of the recipe for which to return version information.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: The token returned by a previous call to retrieve the next set of results.
"""
list_recipe_versions(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/recipeVersions", Dict{String, Any}("name"=>name); aws_config=aws_config)
list_recipe_versions(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/recipeVersions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("name"=>name), params)); aws_config=aws_config)

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

Lists all of the DataBrew recipes that are defined.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: The token returned by a previous call to retrieve the next set of results.
- `"recipeVersion"`: Return only those recipes with a version identifier of LATEST_WORKING
  or LATEST_PUBLISHED. If RecipeVersion is omitted, ListRecipes returns all of the
  LATEST_PUBLISHED recipe versions. Valid values: LATEST_WORKING | LATEST_PUBLISHED
"""
list_recipes(; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/recipes"; aws_config=aws_config)
list_recipes(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/recipes", params; aws_config=aws_config)

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

Lists the DataBrew schedules that are defined.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"jobName"`: The name of the job that these schedules apply to.
- `"maxResults"`: The maximum number of results to return in this request.
- `"nextToken"`: The token returned by a previous call to retrieve the next set of results.
"""
list_schedules(; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/schedules"; aws_config=aws_config)
list_schedules(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/schedules", params; aws_config=aws_config)

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

Lists all the tags for a DataBrew resource.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) string that uniquely identifies the
  DataBrew resource.

"""
list_tags_for_resource(ResourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/tags/$(ResourceArn)"; aws_config=aws_config)
list_tags_for_resource(ResourceArn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("GET", "/tags/$(ResourceArn)", params; aws_config=aws_config)

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

Publishes a new version of a DataBrew recipe.

# Arguments
- `name`: The name of the recipe to be published.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the recipe to be published, for this version of the
  recipe.
"""
publish_recipe(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipes/$(name)/publishRecipe"; aws_config=aws_config)
publish_recipe(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/recipes/$(name)/publishRecipe", params; aws_config=aws_config)

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

Performs a recipe step within an interactive DataBrew session that's currently open.

# Arguments
- `name`: The name of the project to apply the action to.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientSessionId"`: A unique identifier for an interactive session that's currently open
  and ready for work. The action will be performed on this session.
- `"Preview"`: If true, the result of the recipe step will be returned, but not applied.
- `"RecipeStep"`:
- `"StepIndex"`: The index from which to preview a step. This index is used to preview the
  result of steps that have already been applied, so that the resulting view frame is from
  earlier in the view frame stack.
- `"ViewFrame"`:
"""
send_project_session_action(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/projects/$(name)/sendProjectSessionAction"; aws_config=aws_config)
send_project_session_action(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/projects/$(name)/sendProjectSessionAction", params; aws_config=aws_config)

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

Runs a DataBrew job.

# Arguments
- `name`: The name of the job to be run.

"""
start_job_run(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/jobs/$(name)/startJobRun"; aws_config=aws_config)
start_job_run(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/jobs/$(name)/startJobRun", params; aws_config=aws_config)

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

Creates an interactive session, enabling you to manipulate data in a DataBrew project.

# Arguments
- `name`: The name of the project to act upon.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AssumeControl"`: A value that, if true, enables you to take control of a session, even
  if a different client is currently accessing the project.
"""
start_project_session(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/projects/$(name)/startProjectSession"; aws_config=aws_config)
start_project_session(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/projects/$(name)/startProjectSession", params; aws_config=aws_config)

"""
    stop_job_run(name, run_id)
    stop_job_run(name, run_id, params::Dict{String,<:Any})

Stops a particular run of a job.

# Arguments
- `name`: The name of the job to be stopped.
- `run_id`: The ID of the job run to be stopped.

"""
stop_job_run(name, runId; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/jobs/$(name)/jobRun/$(runId)/stopJobRun"; aws_config=aws_config)
stop_job_run(name, runId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/jobs/$(name)/jobRun/$(runId)/stopJobRun", params; aws_config=aws_config)

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

Adds metadata tags to a DataBrew resource, such as a dataset, project, recipe, job, or
schedule.

# Arguments
- `resource_arn`: The DataBrew resource to which tags should be added. The value for this
  parameter is an Amazon Resource Name (ARN). For DataBrew, you can tag a dataset, a job, a
  project, or a recipe.
- `tags`: One or more tags to be assigned to the resource.

"""
tag_resource(ResourceArn, Tags; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/tags/$(ResourceArn)", Dict{String, Any}("Tags"=>Tags); aws_config=aws_config)
tag_resource(ResourceArn, Tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("POST", "/tags/$(ResourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Tags"=>Tags), params)); aws_config=aws_config)

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

Removes metadata tags from a DataBrew resource.

# Arguments
- `resource_arn`: A DataBrew resource from which you want to remove a tag or tags. The
  value for this parameter is an Amazon Resource Name (ARN).
- `tag_keys`: The tag keys (names) of one or more tags to be removed.

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

"""
    update_dataset(input, name)
    update_dataset(input, name, params::Dict{String,<:Any})

Modifies the definition of an existing DataBrew dataset.

# Arguments
- `input`:
- `name`: The name of the dataset to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Format"`: The file format of a dataset that is created from an S3 file or folder.
- `"FormatOptions"`:
- `"PathOptions"`: A set of options that defines how DataBrew interprets an S3 path of the
  dataset.
"""
update_dataset(Input, name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/datasets/$(name)", Dict{String, Any}("Input"=>Input); aws_config=aws_config)
update_dataset(Input, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/datasets/$(name)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Input"=>Input), params)); aws_config=aws_config)

"""
    update_profile_job(output_location, role_arn, name)
    update_profile_job(output_location, role_arn, name, params::Dict{String,<:Any})

Modifies the definition of an existing profile job.

# Arguments
- `output_location`:
- `role_arn`: The Amazon Resource Name (ARN) of the AWS Identity and Access Management
  (IAM) role to be assumed when DataBrew runs the job.
- `name`: The name of the job to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"EncryptionKeyArn"`: The Amazon Resource Name (ARN) of an encryption key that is used to
  protect the job.
- `"EncryptionMode"`: The encryption mode for the job, which can be one of the following:
   SSE-KMS - Server-side encryption with keys managed by AWS KMS.    SSE-S3 - Server-side
  encryption with keys managed by Amazon S3.
- `"JobSample"`: Sample configuration for Profile Jobs only. Determines the number of rows
  on which the Profile job will be executed. If a JobSample value is not provided for profile
  jobs, the default value will be used. The default value is CUSTOM_ROWS for the mode
  parameter and 20000 for the size parameter.
- `"LogSubscription"`: Enables or disables Amazon CloudWatch logging for the job. If
  logging is enabled, CloudWatch writes one log stream for each job run.
- `"MaxCapacity"`: The maximum number of compute nodes that DataBrew can use when the job
  processes data.
- `"MaxRetries"`: The maximum number of times to retry the job after a job run fails.
- `"Timeout"`: The job's timeout in minutes. A job that attempts to run longer than this
  timeout period ends with a status of TIMEOUT.
"""
update_profile_job(OutputLocation, RoleArn, name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/profileJobs/$(name)", Dict{String, Any}("OutputLocation"=>OutputLocation, "RoleArn"=>RoleArn); aws_config=aws_config)
update_profile_job(OutputLocation, RoleArn, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/profileJobs/$(name)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("OutputLocation"=>OutputLocation, "RoleArn"=>RoleArn), params)); aws_config=aws_config)

"""
    update_project(role_arn, name)
    update_project(role_arn, name, params::Dict{String,<:Any})

Modifies the definition of an existing DataBrew project.

# Arguments
- `role_arn`: The Amazon Resource Name (ARN) of the IAM role to be assumed for this request.
- `name`: The name of the project to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Sample"`:
"""
update_project(RoleArn, name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/projects/$(name)", Dict{String, Any}("RoleArn"=>RoleArn); aws_config=aws_config)
update_project(RoleArn, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/projects/$(name)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("RoleArn"=>RoleArn), params)); aws_config=aws_config)

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

Modifies the definition of the LATEST_WORKING version of a DataBrew recipe.

# Arguments
- `name`: The name of the recipe to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description of the recipe.
- `"Steps"`: One or more steps to be performed by the recipe. Each step consists of an
  action, and the conditions under which the action should succeed.
"""
update_recipe(name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/recipes/$(name)"; aws_config=aws_config)
update_recipe(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/recipes/$(name)", params; aws_config=aws_config)

"""
    update_recipe_job(outputs, role_arn, name)
    update_recipe_job(outputs, role_arn, name, params::Dict{String,<:Any})

Modifies the definition of an existing DataBrew recipe job.

# Arguments
- `outputs`: One or more artifacts that represent the output from running the job.
- `role_arn`: The Amazon Resource Name (ARN) of the AWS Identity and Access Management
  (IAM) role to be assumed when DataBrew runs the job.
- `name`: The name of the job to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"EncryptionKeyArn"`: The Amazon Resource Name (ARN) of an encryption key that is used to
  protect the job.
- `"EncryptionMode"`: The encryption mode for the job, which can be one of the following:
   SSE-KMS - Server-side encryption with keys managed by AWS KMS.    SSE-S3 - Server-side
  encryption with keys managed by Amazon S3.
- `"LogSubscription"`: Enables or disables Amazon CloudWatch logging for the job. If
  logging is enabled, CloudWatch writes one log stream for each job run.
- `"MaxCapacity"`: The maximum number of nodes that DataBrew can consume when the job
  processes data.
- `"MaxRetries"`: The maximum number of times to retry the job after a job run fails.
- `"Timeout"`: The job's timeout in minutes. A job that attempts to run longer than this
  timeout period ends with a status of TIMEOUT.
"""
update_recipe_job(Outputs, RoleArn, name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/recipeJobs/$(name)", Dict{String, Any}("Outputs"=>Outputs, "RoleArn"=>RoleArn); aws_config=aws_config)
update_recipe_job(Outputs, RoleArn, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/recipeJobs/$(name)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Outputs"=>Outputs, "RoleArn"=>RoleArn), params)); aws_config=aws_config)

"""
    update_schedule(cron_expression, name)
    update_schedule(cron_expression, name, params::Dict{String,<:Any})

Modifies the definition of an existing DataBrew schedule.

# Arguments
- `cron_expression`: The date or dates and time or times when the jobs are to be run. For
  more information, see Cron expressions in the AWS Glue DataBrew Developer Guide.
- `name`: The name of the schedule to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"JobNames"`: The name or names of one or more jobs to be run for this schedule.
"""
update_schedule(CronExpression, name; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/schedules/$(name)", Dict{String, Any}("CronExpression"=>CronExpression); aws_config=aws_config)
update_schedule(CronExpression, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = databrew("PUT", "/schedules/$(name)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("CronExpression"=>CronExpression), params)); aws_config=aws_config)


text:
# Execution control

export
    CuDim, cudacall

immutable CuDim3
    x::Cint
    y::Cint
    z::Cint
end

# Wrapper type for conveniently specifying the dimensions
# (e.g. `(len, 2)` instead of `CuDim3(len, 2, 1)`)
typealias CuDim Union{Int, Tuple{Int, Int}, Tuple{Int, Int, Int}}
CuDim3(g::Int) = CuDim3(g, 1, 1)
CuDim3(g::Tuple{Int, Int}) = CuDim3(g[1], g[2], 1)
CuDim3(g::Tuple{Int, Int, Int}) = CuDim3(g[1], g[2], g[3])

"""
Launch a CUDA function on a GPU.

This is a low-level call, prefer to use `cudacall` instead.
"""
function launch(f::CuFunction, griddim::CuDim3, blockdim::CuDim3,
                shmem::Int, stream::CuStream, args)
    all([isbits(arg) || isa(arg, DevicePtr) for arg in args]) ||
        throw(ArgumentError("Arguments to kernel should be bitstype or device pointer"))
    args = [isa(arg, DevicePtr) ? arg.inner : arg for arg in args]  # extract DevicePtr.inner::Ptr

    # If f has N parameters, then kernelParams needs to be an array of N pointers.
    # Each of kernelParams[0] through kernelParams[N-1] must point to a region of memory
    # from which the actual kernel parameter will be copied.
    argptrs = Ptr{Void}[Base.pointer_from_objref(arg) for arg in args]

    (griddim.x>0 && griddim.y>0 && griddim.z>0)    || throw(ArgumentError("Grid dimensions should be non-null"))
    (blockdim.x>0 && blockdim.y>0 && blockdim.z>0) || throw(ArgumentError("Block dimensions should be non-null"))

    @apicall(:cuLaunchKernel, (
        CuFunction_t,  			# function
        Cuint, Cuint, Cuint,  	# grid dimensions (x, y, z)
        Cuint, Cuint, Cuint,  	# block dimensions (x, y, z)
        Cuint,  				# shared memory bytes,
        CuStream_t,				# stream
        Ptr{Ptr{Void}}, 		# kernel parameters
        Ptr{Ptr{Void}}), 		# extra parameters
        f,
        griddim.x, griddim.y, griddim.z,
        blockdim.x, blockdim.y, blockdim.z,
        shmem, stream, argptrs, C_NULL)
end

"""
ccall-like interface to launching a CUDA function on a GPU
"""
function cudacall(f::CuFunction, griddim::CuDim, blockdim::CuDim, types, values...;
                  shmem=0, stream::CuStream=default_stream())
    tt = Base.to_tuple_type(types)

    # convert the values to match the kernel's signature (specified by the user)
    values = map(pair -> Base.unsafe_convert(pair[1], Base.cconvert(pair[1],pair[2])),
                 zip(tt.parameters,values))

    launch(f, CuDim3(griddim), CuDim3(blockdim), shmem, stream, values)
end

ext: jl


text:
using Oceananigans.Advection: UpwindBiasedFifthOrder, div_Uc, div_𝐯u, div_𝐯v, div_𝐯w
using Oceananigans.Fields: ZeroField, ConstantField
using Adapt

maybe_constant_field(u) = u
maybe_constant_field(u::Number) = ConstantField(u)

struct AdvectiveForcing{U, S, F, C}
    velocities :: U
    advection_scheme :: S
    advection_kernel_function :: F
    advected_field :: C
end

"""
    AdvectiveForcing(scheme=UpwindBiasedFifthOrder(), u=ZeroField(), v=ZeroField(), w=ZeroField())

Build a forcing term representing advection by the velocity field `u, v, w` with an advection `scheme`.

Example
=======

# Using a tracer field to model sinking particles

```jldoctest
using Oceananigans

# Physical parameters
gravitational_acceleration          = 9.81     # m s⁻²
ocean_density                       = 1026     # kg m⁻³
mean_particle_density               = 2000     # kg m⁻³
mean_particle_radius                = 1e-3     # m
ocean_molecular_kinematic_viscosity = 1.05e-6  # m² s⁻¹

# Terminal velocity of a sphere in viscous flow
Δb = gravitational_acceleration * (mean_particle_density - ocean_density) / ocean_density
ν = ocean_molecular_kinematic_viscosity
R = mean_particle_radius

w_Stokes = - 2/9 * Δb / ν * R^2 # m s⁻¹

settling = AdvectiveForcing(UpwindBiasedFifthOrder(), w=w_Stokes)

# output
AdvectiveForcing with the UpwindBiasedFifthOrder scheme:
├── u: ZeroField{Int64}
├── v: ZeroField{Int64}
└── w: ConstantField(-1.97096)
```
"""
function AdvectiveForcing(scheme=UpwindBiasedFifthOrder(); u=ZeroField(), v=ZeroField(), w=ZeroField())
    u, v, w = maybe_constant_field.((u, v, w))
    velocities = (; u, v, w)
    return AdvectiveForcing(velocities, scheme, nothing, nothing) # stub
end

function regularize_forcing(af::AdvectiveForcing, field, field_name, model_field_names)
    kernel_function = field_name === :u ? div_𝐯u :
                      field_name === :v ? div_𝐯v :
                      field_name === :w ? div_𝐯w : div_Uc

    return AdvectiveForcing(af.velocities, af.advection_scheme, kernel_function, field)
end

@inline (af::AdvectiveForcing)(i, j, k, grid, clock, model_fields) =
    - af.advection_kernel_function(i, j, k, grid, af.advection_scheme, af.velocities, af.advected_field)

Base.summary(af::AdvectiveForcing) = string("AdvectiveForcing with the ", nameof(typeof(af.advection_scheme)), " scheme")

function Base.show(io::IO, af::AdvectiveForcing)

    print(io, summary(af), ":", '\n')

    print(io, "├── u: ", prettysummary(af.velocities.u), '\n',
              "├── v: ", prettysummary(af.velocities.v), '\n',
              "└── w: ", prettysummary(af.velocities.w))
end

Adapt.adapt_structure(to, af::AdvectiveForcing) =
    AdvectiveForcing(adapt(to, af.velocities),
                     adapt(to, af.advection_scheme),
                     adapt(to, af.advection_kernel_function),
                     adapt(to, af.advected_field))


text:
import Chipmunk
Cp = Chipmunk
import SFML
Sf = SFML

function add_vect(vec1, vec2)
    Cp.Vect(vec1.x + vec2.x, vec1.y + vec2.y)
end

const height = 30
const width = 7

settings = Sf.ContextSettings()
settings.antialiasing_level = 3
window = Sf.RenderWindow(Sf.VideoMode(800, 600), "Pyramid Topple", settings, Sf.window_defaultstyle)
Sf.set_framerate_limit(window, 60)

event = Sf.Event()

gravity = Cp.Vect(0, -200)
space = Cp.Space()
Cp.set_gravity(space, gravity)

static = Cp.StaticBody()
groundshape = Cp.add_shape(space, Cp.SegmentShape(static, Cp.Vect(0, -600), Cp.Vect(800, -600), 10))
Cp.set_friction(groundshape, 1)
leftwallshape = Cp.add_shape(space, Cp.SegmentShape(static, Cp.Vect(0, 0), Cp.Vect(0, -600), 10))
Cp.set_friction(leftwallshape, 1)
rightwallshape = Cp.add_shape(space, Cp.SegmentShape(static, Cp.Vect(800, 0), Cp.Vect(800, -600), 10))
Cp.set_friction(rightwallshape, 1)

function add_domino(pos, flipped)
    mass = 1
    radius = 0.5
    moment = Cp.momentforbox(mass, width, height)

    body = Cp.add_body(space, Cp.Body(mass, moment))
    Cp.set_position(body, Cp.Vect(pos.x + 400, pos.y - 350))

    shape = !flipped ? Cp.BoxShape(body, width, height, 0) : Cp.BoxShape(body, height, width, 0)
    Cp.add_shape(space, shape)
    Cp.set_elasticity(shape, 0)
    Cp.set_friction(shape, 0.6)
end

n = 12;
for i = 0:n-1
    for j = 0:n-i-1
        offset = Cp.Vect((j - (n - 1 - i)*0.5)*1.5*height, (i + 0.5)*(height + 2*width) - width - 240);
        add_domino(offset, false);
        add_domino(add_vect(offset, Cp.Vect(0, (height + width)/2.0)), true);

        if j == 0
            add_domino(add_vect(offset, Cp.Vect(0.5*(width - height), height + width)), false);
        end

        if j != (n - i - 1)
            add_domino(add_vect(offset, Cp.Vect(height*0.75, (height + 3*width)/2.0)), true);
        else
            add_domino(add_vect(offset, Cp.Vect(0.5*(height - width), height + width)), false);
        end
    end
end

while Sf.isopen(window)
    while Sf.pollevent(window, event)
        if Sf.get_type(event) == Sf.EventType.CLOSED
            Sf.close(window)
        end
    end

    Cp.step(space, 1/60)

    Sf.clear(window, Sf.white)
    Cp.debug_draw(space, window)
    Sf.display(window)
end


text:
"""
    tenxsumr(;tenxfile::AbstractString="", outdir::AbstractString=".", group::AbstractString="", chunksize::Number=5000)

Extract the summary information of 10X-HDF5.

Input Arguments
---------
- `tenxfile` is the HDF5 file formatted by 10X Genomics.
- `outdir` is specified the directory you want to save the result.
- `group` is the group name of HDF5 (e.g. mm10).
- `chunksize` is the number of rows reading at once (e.g. 5000).

Output Files
---------
- `Sample_NoCounts.csv` : Sum of counts in each column.
- `Feature_Means.csv` : Mean in each row.
- `Feature_LogMeans.csv` : Log10(Mean+1) in each row.
- `Feature_SqrtMeans.csv` : sqrt(Mean+1) in each row.
- `Feature_Vars.csv` : Sample variance in each row.
- `Feature_LogVars.csv` : Log10(Var+1) in each row.
- `Feature_SqrtVars.csv` : sqrt(Var+1) in each row.
- `Feature_CV2s.csv` : Coefficient of Variation in each row.
"""

# Column-wise statistics
function tenxnocounts(tenxfile::AbstractString, group::AbstractString, chunksize::Number, N::Number, M::Number, idp::AbstractArray)
    nc = zeros(UInt32, M)
    # Each chunk
	for i in 1:fld(N, chunksize)+1
        startp = Int64((i-1)*chunksize+1)
        endp = Int64(i*chunksize)
        if N - endp + chunksize < chunksize
	        endp = N
        end
        X = loadchromium(tenxfile, group, idp, startp, endp, M, false)
        nc += sum(X, dims=1)[1,:]
	end
    return nc
end

# Row-wise statistics
function tenxstats(tenxfile::AbstractString, group::AbstractString, chunksize::Number, N::Number, M::Number, idp::AbstractArray, nc::AbstractArray)

    m = zeros(N)
    lm = zeros(N)
    sqrtm = zeros(N)

    v = zeros(N)
    lv = zeros(N)
    sqrtv = zeros(N)

    cpmm = zeros(N)
    lcpmm = zeros(N)
    sqrtcpmm = zeros(N)

    cpmv = zeros(N)
    lcpmv = zeros(N)
    sqrtcpmv = zeros(N)

    cptm = zeros(N)
    lcptm = zeros(N)
    sqrtcptm = zeros(N)

    cptv = zeros(N)
    lcptv = zeros(N)
    sqrtcptv = zeros(N)

    cpmedm = zeros(N)
    lcpmedm = zeros(N)
    sqrtcpmedm = zeros(N)

    cpmedv = zeros(N)
    lcpmedv = zeros(N)
    sqrtcpmedv = zeros(N)

    c = zeros(N)

    # Each chunk
    if N > chunksize
        lasti = fld(N, chunksize)+1
    else
        lasti = 1
    end

	for i in 1:lasti
        startp = Int64((i-1)*chunksize+1)
        endp = Int64(i*chunksize)
        if N - endp + chunksize < chunksize
	        endp = N
        end
        X = loadchromium(tenxfile, group, idp, startp, endp, M, false)
        logX = sparseLog10(X)
        sqrtX = sqrt.(X)

        cpmX = 1e6 .* X ./ nc'
        logcpmX = 1e6 .* logX ./ nc'
        sqrtcpmX = 1e6 .* sqrtX ./ nc'

        cptX = 1e4 .* X ./ nc'
        logcptX = 1e4 .* logX ./ nc'
        sqrtcptX = 1e4 .* sqrtX ./ nc'

        cpmedX = median(nc) .* X ./ nc'
        logcpmedX = median(nc) .* logX ./ nc'
        sqrtcpmedX = median(nc) .* sqrtX ./ nc'

        # Update
        m[startp:endp] = mean(X, dims=2)
        lm[startp:endp] = mean(logX, dims=2)
        sqrtm[startp:endp] = mean(sqrtX, dims=2)

        v[startp:endp] = var(X, dims=2)
        lv[startp:endp] = var(logX, dims=2)
        sqrtv[startp:endp] = var(sqrtX, dims=2)

        cpmm[startp:endp] = mean(cpmX, dims=2)
        lcpmm[startp:endp] = mean(logcpmX, dims=2)
        sqrtcpmm[startp:endp] = mean(sqrtcpmX, dims=2)

        cpmv[startp:endp] = var(cpmX, dims=2)
        lcpmv[startp:endp] = var(logcpmX, dims=2)
        sqrtcpmv[startp:endp] = var(sqrtcpmX, dims=2)

        cptm[startp:endp] = mean(cptX, dims=2)
        lcptm[startp:endp] = mean(logcptX, dims=2)
        sqrtcptm[startp:endp] = mean(sqrtcptX, dims=2)

        cptv[startp:endp] = var(cptX, dims=2)
        lcptv[startp:endp] = var(logcptX, dims=2)
        sqrtcptv[startp:endp] = var(sqrtcptX, dims=2)

        cpmedm[startp:endp] = mean(cpmedX, dims=2)
        lcpmedm[startp:endp] = mean(logcpmedX, dims=2)
        sqrtcpmedm[startp:endp] = mean(sqrtcpmedX, dims=2)

        cpmedv[startp:endp] = var(cpmedX, dims=2)
        lcpmedv[startp:endp] = var(logcpmedX, dims=2)
        sqrtcpmedv[startp:endp] = var(sqrtcpmedX, dims=2)

        c[startp:endp] = v[startp:endp] ./ (m[startp:endp] .* m[startp:endp])
	end
    return m, lm, sqrtm, v, lv, sqrtv, cpmm, lcpmm, sqrtcpmm, cpmv, lcpmv, sqrtcpmv, cptm, lcptm, sqrtcptm, cptv, lcptv, sqrtcptv, cpmedm, lcpmedm, sqrtcpmedm, cpmedv, lcpmedv, sqrtcpmedv, c
end

function tenxsumr(;tenxfile::AbstractString="", outdir::AbstractString=".", group::AbstractString="", chunksize::Number=5000)
	# Row / Column
	N, M = tenxnm(tenxfile, group)
	# Index Pointer
	idp = indptr(tenxfile, group)

    # 1 / 2 : Column-wise statistics
    println("1 / 2 : Column-wise statistics are calculated...")
    Sample_NoCounts = tenxnocounts(tenxfile, group, chunksize, N, M, idp)

    # 2 / 2 : Row-wise statistics
    println("2 / 2 : Row-wise statistics are calculated...")
    Feature_Means, Feature_LogMeans, Feature_SqrtMeans, Feature_Vars, Feature_LogVars, Feature_SqrtVars, Feature_CPMMeans, Feature_LogCPMMeans, Feature_SqrtCPMMeans, Feature_CPMVars, Feature_LogCPMVars, Feature_SqrtCPMVars, Feature_CPTMeans, Feature_LogCPTMeans, Feature_SqrtCPTMeans, Feature_CPTVars, Feature_LogCPTVars, Feature_SqrtCPTVars, Feature_CPMEDMeans, Feature_LogCPMEDMeans, Feature_SqrtCPMEDMeans, Feature_CPMEDVars, Feature_LogCPMEDVars, Feature_SqrtCPMEDVars, Feature_CV2s = tenxstats(tenxfile, group, chunksize, N, M, idp, Sample_NoCounts)

    # Save
    writecsv(joinpath(outdir, "Sample_NoCounts.csv"), Sample_NoCounts)

    writecsv(joinpath(outdir, "Feature_Means.csv"), Feature_Means)
    writecsv(joinpath(outdir, "Feature_LogMeans.csv"), Feature_LogMeans)
    writecsv(joinpath(outdir, "Feature_SqrtMeans.csv"), Feature_SqrtMeans)

    writecsv(joinpath(outdir, "Feature_CPMMeans.csv"), Feature_CPMMeans)
    writecsv(joinpath(outdir, "Feature_LogCPMMeans.csv"), Feature_LogCPMMeans)
    writecsv(joinpath(outdir, "Feature_SqrtCPMMeans.csv"), Feature_SqrtCPMMeans)

    writecsv(joinpath(outdir, "Feature_CPTMeans.csv"), Feature_CPTMeans)
    writecsv(joinpath(outdir, "Feature_LogCPTMeans.csv"), Feature_LogCPTMeans)
    writecsv(joinpath(outdir, "Feature_SqrtCPTMeans.csv"), Feature_SqrtCPTMeans)

    writecsv(joinpath(outdir, "Feature_CPMEDMeans.csv"), Feature_CPMEDMeans)
    writecsv(joinpath(outdir, "Feature_LogCPMEDMeans.csv"), Feature_LogCPMEDMeans)
    writecsv(joinpath(outdir, "Feature_SqrtCPMEDMeans.csv"), Feature_SqrtCPMEDMeans)

    writecsv(joinpath(outdir, "Feature_Vars.csv"), Feature_Vars)
    writecsv(joinpath(outdir, "Feature_LogVars.csv"), Feature_LogVars)
    writecsv(joinpath(outdir, "Feature_SqrtVars.csv"), Feature_SqrtVars)

    writecsv(joinpath(outdir, "Feature_CPMVars.csv"), Feature_CPMVars)
    writecsv(joinpath(outdir, "Feature_LogCPMVars.csv"), Feature_LogCPMVars)
    writecsv(joinpath(outdir, "Feature_SqrtCPMVars.csv"), Feature_SqrtCPMVars)

    writecsv(joinpath(outdir, "Feature_CPTVars.csv"), Feature_CPTVars)
    writecsv(joinpath(outdir, "Feature_LogCPTVars.csv"), Feature_LogCPTVars)
    writecsv(joinpath(outdir, "Feature_SqrtCPTVars.csv"), Feature_SqrtCPTVars)

    writecsv(joinpath(outdir, "Feature_CPMEDVars.csv"), Feature_CPMEDVars)
    writecsv(joinpath(outdir, "Feature_LogCPMEDVars.csv"), Feature_LogCPMEDVars)
    writecsv(joinpath(outdir, "Feature_SqrtCPMEDVars.csv"), Feature_SqrtCPMEDVars)

    writecsv(joinpath(outdir, "Feature_CV2s.csv"), Feature_CV2s)
end


text:
using StaticArrays

struct Magic
    mask::UInt64
    mul::UInt64
    shift::Int32
    index::Int32
end

const B_MAGIC = SVector(
    Magic(18049651735527936, 36626935706583168, 58, 0),
    Magic(70506452091904, 577103421212035084, 59, 64),
    Magic(275415828992, 18579548576744452, 59, 96),
    Magic(1075975168, 145333462000205832, 59, 128),
    Magic(38021120, 578169462159577104, 59, 160),
    Magic(8657588224, 3566007889787489280, 59, 192),
    Magic(2216338399232, 1126484182311424, 59, 224),
    Magic(567382630219776, 10841270582838278, 58, 256),
    Magic(9024825867763712, 9232874085095180368, 59, 320),
    Magic(18049651735527424, 94610879663866400, 59, 352),
    Magic(70506452221952, 720595748839882752, 59, 384),
    Magic(275449643008, 577168907618754688, 59, 416),
    Magic(9733406720, 11745394502829875216, 59, 448),
    Magic(2216342585344, 2293781233680, 59, 480),
    Magic(567382630203392, 4611689456584368322, 59, 512),
    Magic(1134765260406784, 9227876194845721088, 59, 544),
    Magic(4512412933816832, 4652219070325753472, 59, 576),
    Magic(9024825867633664, 1139164981535745, 59, 608),
    Magic(18049651768822272, 149744696202234368, 57, 640),
    Magic(70515108615168, 2884564383764070528, 57, 768),
    Magic(2491752130560, 577586687140955264, 57, 896),
    Magic(567383701868544, 30751147784409092, 57, 1024),
    Magic(1134765256220672, 2684297127705972736, 59, 1152),
    Magic(2269530512441344, 671107830517999625, 59, 1184),
    Magic(2256206450263040, 5206935775735972864, 59, 1216),
    Magic(4512412900526080, 1442279982105641224, 59, 1248),
    Magic(9024834391117824, 4648014982389432610, 57, 1280),
    Magic(18051867805491712, 4634208415688691744, 55, 1408),
    Magic(637888545440768, 282643258163200, 55, 1920),
    Magic(1135039602493440, 594620836657766400, 57, 2432),
    Magic(2269529440784384, 1218259035128328, 59, 2560),
    Magic(4539058881568768, 572020957925904, 59, 2592),
    Magic(1128098963916800, 473177067408607232, 59, 2624),
    Magic(2256197927833600, 1227232565041046532, 59, 2656),
    Magic(4514594912477184, 1126039501930752, 57, 2688),
    Magic(9592139778506752, 9225626037906833536, 55, 2816),
    Magic(19184279556981248, 18016606122707072, 55, 3328),
    Magic(2339762086609920, 158752994966390800, 57, 3840),
    Magic(4538784537380864, 864981421608436736, 59, 3968),
    Magic(9077569074761728, 1689167838986768, 59, 4000),
    Magic(562958610993152, 288388740725868576, 59, 4032),
    Magic(1125917221986304, 1128410919508228, 59, 4064),
    Magic(2814792987328512, 4574123258872064, 57, 4096),
    Magic(5629586008178688, 282162574148864, 57, 4224),
    Magic(11259172008099840, 4613940154854285828, 57, 4352),
    Magic(22518341868716544, 153162041186583584, 57, 4480),
    Magic(9007336962655232, 2310364209655186080, 59, 4608),
    Magic(18014673925310464, 4991114845651731974, 59, 4640),
    Magic(2216338399232, 1197575531136034, 59, 4672),
    Magic(4432676798464, 45638545897160708, 59, 4704),
    Magic(11064376819712, 36178897544970280, 59, 4736),
    Magic(22137335185408, 4900488141171326976, 59, 4768),
    Magic(44272556441600, 1729945275616862208, 59, 4800),
    Magic(87995357200384, 1225266105808912448, 59, 4832),
    Magic(35253226045952, 4749993329049665, 59, 4864),
    Magic(70506452091904, 12126810619538112803, 59, 4896),
    Magic(567382630219776, 9228450699350777856, 58, 4928),
    Magic(1134765260406784, 27022426827468800, 59, 4992),
    Magic(2832480465846272, 362680787772035106, 59, 5024),
    Magic(5667157807464448, 18858823716701184, 59, 5056),
    Magic(11333774449049600, 153122396054946817, 59, 5088),
    Magic(22526811443298304, 1155174678944354562, 59, 5120),
    Magic(9024825867763712, 325561029315010688, 59, 5152),
    Magic(18049651735527936, 2379105703026499648, 58, 5184),
)

const R_MAGIC = SVector(
    Magic(282578800148862, 144132922004340801, 52, 5248),
    Magic(565157600297596, 54043471211683840, 53, 9344),
    Magic(1130315200595066, 144126183468974112, 53, 11392),
    Magic(2260630401190006, 324276767504728180, 53, 13440),
    Magic(4521260802379886, 144119654875400224, 53, 15488),
    Magic(9042521604759646, 1369096502923821096, 53, 17536),
    Magic(18085043209519166, 4755802735545157768, 53, 19584),
    Magic(36170086419038334, 72059795487737088, 52, 21632),
    Magic(282578800180736, 4629841155498999840, 53, 25728),
    Magic(565157600328704, 414472178092744704, 54, 27776),
    Magic(1130315200625152, 140806216228864, 54, 28800),
    Magic(2260630401218048, 141287378391040, 54, 29824),
    Magic(4521260802403840, 218565336596217984, 54, 30848),
    Magic(9042521604775424, 577023779584087144, 54, 31872),
    Magic(18085043209518592, 4785093964988672, 54, 32896),
    Magic(36170086419037696, 563091721961604, 53, 33920),
    Magic(282578808340736, 3494794685233242112, 53, 35968),
    Magic(565157608292864, 76570264640430081, 54, 38016),
    Magic(1130315208328192, 9572348504605216, 54, 39040),
    Magic(2260630408398848, 9432160767313924, 54, 40064),
    Magic(4521260808540160, 27162885075503105, 54, 41088),
    Magic(9042521608822784, 564053760278536, 54, 42112),
    Magic(18085043209388032, 1103806726148, 54, 43136),
    Magic(36170086418907136, 4758354273057128588, 53, 44160),
    Magic(282580897300736, 2017823792981643552, 53, 46208),
    Magic(565159647117824, 81064948986282048, 54, 48256),
    Magic(1130317180306432, 585608835076587592, 54, 49280),
    Magic(2260632246683648, 18735697465180544, 54, 50304),
    Magic(4521262379438080, 1225260595101566976, 54, 51328),
    Magic(9042522644946944, 13853076853985771648, 54, 52352),
    Magic(18085043175964672, 10135865121571080, 54, 53376),
    Magic(36170086385483776, 281479273795714, 53, 54400),
    Magic(283115671060736, 2846276201486483521, 53, 56448),
    Magic(565681586307584, 9042452350517248, 54, 58496),
    Magic(1130822006735872, 2314920714668347658, 54, 59520),
    Magic(2261102847592448, 11605776274101899392, 54, 60544),
    Magic(4521664529305600, 144978309082515456, 54, 61568),
    Magic(9042787892731904, 3941079741106192, 54, 62592),
    Magic(18085034619584512, 1152923162531336232, 54, 63616),
    Magic(36170077829103616, 277058946049, 53, 64640),
    Magic(420017753620736, 13979314119359102976, 53, 66688),
    Magic(699298018886144, 576777415142227968, 54, 68736),
    Magic(1260057572672512, 9952957392692973569, 54, 69760),
    Magic(2381576680245248, 4632022628445585440, 54, 70784),
    Magic(4624614895390720, 4791838799900508164, 54, 71808),
    Magic(9110691325681664, 10386431038703304832, 54, 72832),
    Magic(18082844186263552, 578167368380055566, 54, 73856),
    Magic(36167887395782656, 9223389908356431876, 53, 74880),
    Magic(35466950888980736, 4785351629746432, 53, 76928),
    Magic(34905104758997504, 1166502672786817280, 54, 78976),
    Magic(34344362452452352, 2305864175425028608, 54, 80000),
    Magic(33222877839362048, 2305878267707566592, 54, 81024),
    Magic(30979908613181440, 146367005070098560, 54, 82048),
    Magic(26493970160820224, 9265034801249781761, 54, 83072),
    Magic(17522093256097792, 288380532772441088, 54, 84096),
    Magic(35607136465616896, 74768942432768, 53, 85120),
    Magic(9079539427579068672, 1161634186985665, 52, 87168),
    Magic(8935706818303361536, 17867200299265, 53, 91264),
    Magic(8792156787827803136, 634427336034833, 53, 93312),
    Magic(8505056726876686336, 1179952036171546709, 53, 95360),
    Magic(7930856604974452736, 563023102353410, 53, 97408),
    Magic(6782456361169985536, 563018740107266, 53, 99456),
    Magic(4485655873561051136, 146648617518631065, 53, 101504),
    Magic(9115426935197958144, 9799833905986027650, 52, 103552),
)

function magicindex(m::Magic, b::UInt64)
    m.index + Int32(((b & m.mask) * m.mul) >> m.shift) + 1
end

function indextou64(index::Int, bits::Int, mask::UInt64)
    result = UInt64(0)
    m = mask
    for i ∈ 0:(bits-1)
        j = trailing_zeros(m)
        m &= m - 1
        if (index & (1 << i)) != 0
            result |= UInt64(1) << j
        end
    end
    result
end

function square(file, rank)
    8 * file + rank
end

function computeslidingattacks(s, occ, dirs, ix)
    result = UInt64(0)
    (fl, rk) = fldmod(s, 8)
    while dirs[ix] ≠ 0
        (dx, dy) = ((dirs[ix] & 3) - 2, (dirs[ix] >> 2) - 2)
        (f, r) = (fl + dx, rk + dy)
        while 0 ≤ f ≤ 7 && 0 ≤ r ≤ 7
            s = UInt64(1) << square(f, r)
            result |= s
            (occ & s) ≠ 0 && break
            f += dx
            r += dy
        end
        ix += 1
    end
    result
end

function computeattackdb()
    result = Array{UInt64,1}(zeros(107648))
    dirs = [5, 7, 13, 15, 0, 6, 9, 11, 14, 0]
    for s ∈ 0:63
        for i ∈ 0:1
            m = i == 0 ? B_MAGIC[s+1] : R_MAGIC[s+1]
            bits = 64 - m.shift
            for j ∈ 0:((1<<bits)-1)
                b = indextou64(j, bits, m.mask)
                result[magicindex(m, b)] = computeslidingattacks(s, b, dirs, i * 5 + 1)
            end
        end
    end
    result
end

const ATTACK_DB = computeattackdb()

function bishopattacks(b::UInt64, s::Int)
    @inbounds ATTACK_DB[magicindex(B_MAGIC[s], b)]
end

function rookattacks(b::UInt64, s::Int)
    @inbounds ATTACK_DB[magicindex(R_MAGIC[s], b)]
end


text:
###### update ####################

function update(ta::TimeArray{T,N,D}, tstamp::D, val::AbstractArray{T,N}) where {T,N,D}

    if length(ta) == 0
        throw(ArgumentError(
            "updating empty time arrays is not supported, " *
            "please use a scalable approach"))
    elseif tstamp < maximum(timestamp(ta))
        throw(ArgumentError("only appending operations supported"))
    else
        t    = vcat(timestamp(ta), tstamp)
        vals = vcat(values(ta), val)
        uta  = TimeArray(t, vals, colnames(ta), meta(ta))
    end
    uta
end

function update(ta::TimeArray{T,N,D}, tstamp::D, val::T) where {T,N,D}

    if length(ta) == 0
        throw(ArgumentError(
            "updating empty time arrays is not supported, " *
            "please use a scalable approach"))
    elseif tstamp < maximum(timestamp(ta))
        throw(ArgumentError("only appending operations supported"))
    else
        t    = vcat(timestamp(ta), tstamp)
        vals = vcat(values(ta), val)
        uta  = TimeArray(t, vals, colnames(ta), meta(ta))
    end
    uta
end

###### rename ####################

function rename(ta::TimeArray, colnames::Vector{Symbol})
    length(colnames) == size(ta, 2) || throw(DimensionMismatch("Colnames length mismatch"))
    TimeArray(ta, colnames = colnames, unchecked = true)
end

rename(ta::TimeArray, colnames::Symbol) = rename(ta, [colnames])

function rename(ta::TimeArray, args::Pair{Symbol,Symbol}...)
    d_colnames = Dict{Symbol,Symbol}(args...)
    _colnames = copy(colnames(ta))
    for (i, colname) in enumerate(_colnames)
        if colname in keys(d_colnames)
            _colnames[i] = d_colnames[colname]
        end
    end
    rename(ta, _colnames)
end

function rename(f::Base.Callable, ta::TimeArray, colnametyp::Type{Symbol} = Symbol)
    _colnames = copy(colnames(ta))
    for (i, colname) in enumerate(_colnames)
        _colnames[i] = f(colname)
    end
    rename(ta, _colnames)
end

function rename(f::Base.Callable, ta::TimeArray, colnametyp::Type{String})
    f = Symbol ∘ f ∘ string
    rename(f, ta)
end


text:
# Utilities to generate a grid with the following inputs

@inline adapt_if_vector(to, var) = var
@inline adapt_if_vector(to, var::AbstractArray) = Adapt.adapt(to, var)

@inline show_coordinate(Δ::Number, T)            = "Regular, with spacing $Δ"
@inline show_coordinate(Δ::Number, ::Type{Flat}) = "Flattened"
@inline show_coordinate(Δ::AbstractVector, T)    = "Stretched, with spacing min=$(minimum(parent(Δ))), max=$(maximum(parent(Δ)))"

get_domain_extent(coord, N)                 = (coord[1], coord[2])
get_domain_extent(coord::Function, N)       = (coord(1), coord(N+1))
get_domain_extent(coord::AbstractVector, N) = (coord[1], coord[N+1])

get_coord_face(coord::Nothing, i) = 1
get_coord_face(coord::Function, i) = coord(i)
get_coord_face(coord::AbstractVector, i) = CUDA.@allowscalar coord[i]

lower_exterior_Δcoordᶜ(topology,        Fi, Hcoord) = [Fi[end - Hcoord + i] - Fi[end - Hcoord + i - 1] for i = 1:Hcoord]
lower_exterior_Δcoordᶜ(::Type{Bounded}, Fi, Hcoord) = [Fi[2]  - Fi[1] for i = 1:Hcoord]

upper_exterior_Δcoordᶜ(topology,        Fi, Hcoord) = [Fi[i + 1] - Fi[i] for i = 1:Hcoord]
upper_exterior_Δcoordᶜ(::Type{Bounded}, Fi, Hcoord) = [Fi[end]   - Fi[end - 1] for i = 1:Hcoord]

# generate a stretched coordinate passing the explicit coord faces as vector of functionL
function generate_coordinate(FT, topology, N, H, coord, architecture)

    # Ensure correct type for F and derived quantities
    interiorF = zeros(FT, N+1)

    for i = 1:N+1
        interiorF[i] = get_coord_face(coord, i)
    end

    L = interiorF[N+1] - interiorF[1]

    # Build halo regions
    ΔF₋ = lower_exterior_Δcoordᶜ(topology, interiorF, H)
    ΔF₊ = reverse(upper_exterior_Δcoordᶜ(topology, interiorF, H))

    c¹, cᴺ⁺¹ = interiorF[1], interiorF[N+1]

    F₋ = [c¹   - sum(ΔF₋[i:H]) for i = 1:H]          # locations of faces in lower halo
    F₊ = reverse([cᴺ⁺¹ + sum(ΔF₊[i:H]) for i = 1:H]) # locations of faces in width of top halo region

    F = vcat(F₋, interiorF, F₊)

    # Build cell centers, cell center spacings, and cell interface spacings
    TC = total_length(Center, topology, N, H)
     C = [ (F[i + 1] + F[i]) / 2 for i = 1:TC ]
    ΔF = [  C[i] - C[i - 1]      for i = 2:TC ]

    # Trim face locations for periodic domains
    TF = total_length(Face, topology, N, H)
    F  = F[1:TF]

    ΔC = [F[i + 1] - F[i] for i = 1:TF-1]

    ΔF = OffsetArray(ΔF, -H)
    ΔC = OffsetArray(ΔC, -H)

    # Seems needed to avoid out-of-bounds error in viscous dissipation
    # operators wanting to access ΔF[N+2].
    ΔF = OffsetArray(cat(ΔF[0], ΔF..., ΔF[N], dims=1), -H-1)

    ΔF = OffsetArray(arch_array(architecture, ΔF.parent), ΔF.offsets...)
    ΔC = OffsetArray(arch_array(architecture, ΔC.parent), ΔC.offsets...)

    F = OffsetArray(F, -H)
    C = OffsetArray(C, -H)

    # Convert to appropriate array type for arch
    F = OffsetArray(arch_array(architecture, F.parent), F.offsets...)
    C = OffsetArray(arch_array(architecture, C.parent), C.offsets...)

    return L, F, C, ΔF, ΔC
end

# generate a regular coordinate passing the domain extent (2-tuple) and number of points
function generate_coordinate(FT, topology, N, H, coord::Tuple{<:Number, <:Number}, architecture)

    @assert length(coord) == 2

    c₁, c₂ = coord
    @assert c₁ < c₂
    L = FT(c₂ - c₁)

    # Convert to get the correct type also when using single precision
    ΔF = ΔC = Δ = convert(FT, L / N)

    F₋ = c₁ - H * Δ
    F₊ = F₋ + total_extent(topology, H, Δ, L)

    C₋ = F₋ + Δ / 2
    C₊ = C₋ + L + Δ * (2H - 1)

    TF = total_length(Face,   topology, N, H)
    TC = total_length(Center, topology, N, H)

    F = range(F₋, F₊, length = TF)
    C = range(C₋, C₊, length = TC)

    F = OffsetArray(F, -H)
    C = OffsetArray(C, -H)

    return L, F, C, ΔF, ΔC
end

# Flat domains
function generate_coordinate(FT, ::Type{Flat}, N, H, coord::Tuple{<:Number, <:Number}, architecture)
    return FT(1), range(1, 1, length=N), range(1, 1, length=N), FT(1), FT(1)
end


text:
rmprocs(workers()...); addprocs(2); @assert nprocs() == 3
using Test
@everywhere using ArrayChannels

function test_serialise()
    @testset "ArrayChannel Serialisation" begin
        X = ArrayChannel(Float64, procs(), 2, 2)
        X[1,1] = 2.0; X[2,2] = 4.0

        id = X.rrid

        @sync begin
            @async put!(X, 2)
            @async @spawnat 2 take!(X, 1)
        end

        @test (@fetchfrom 2 X[1,1]) == 2.0
    end
end

function test_synchronisation()
    @testset "Test synchronisation by put!, take!" begin
        X = ArrayChannel(Int64, [1,2], 100000)
        T = Vector{Future}(undef, 5)
        @sync for i in 1:5
            T[i] = @spawnat 2 begin
                take!(X, 1)
                return X[1]
            end
            fill!(X, i)
            put!(X, 2)
        end
        for (i, t) in enumerate(T)
            @test fetch(t) == i
        end
    end
end

function test_target_other_channel()
    @testset "Target another channel with put!" begin
        X = ArrayChannel(Int64, [1,2], 10)
        Y = ArrayChannel(Int64, [1,2], 10)

        fill!(X, 10)
        @sync @spawnat 2 fill!(Y, 20)

        local test_value
        @sync begin
            @async put!(X, 2, Y.rrid)
            test_value = @spawnat 2 begin
                take!(Y, 1)
                Y[1,1]
            end
        end
        @test fetch(test_value) == 10
    end
end

function test_put_take_init()
    @testset "Test ArrayChannel Logistics:" begin
        X = ArrayChannel(Float64, procs(), 2, 2)
        B = copy(X.buffer.src)
        X[1,1] = 2.0; X[2,2] = 4.0
        X[1,2] = 0.0; X[2,1] = 0.0
        rrid = X.rrid

        A = [2.0 0.0; 0.0 4.0]

        D = remotecall_fetch(2, rrid) do id
            Z = ac_get_from(id)
            return (Z[1,1], Z[2,2])
        end

        # Test for no deadlocks
        @sync for x in 1:100
            @async put!(X)
            @async begin
                remotecall_wait(2, rrid) do id
                    take!(ac_get_from(id))
                end
            end
        end

        # After a put the array remains unchanged
        @test A == X.buffer.src

        id = X.rrid

        # Don't point to the same array
        Y = remotecall_fetch(ac_get_from, 2, id)

        @test Y.buffer.src  == X.buffer.src == A
        @test Y !== X

        # Test that components successfully serialised.
        D = remotecall_fetch(2, id) do id
            Z = ac_get_from(id)
            return (Z[1,1], Z[2,2])
        end
        @test D == (2.0, 4.0)
    end
end


text:
# This file was generated, do not modify it. # hide
using Gnuplot, Colors, ColorSchemes, Random
Random.seed!(123)
function custom_palette(colormap=:viridis)
    cmap = get(colorschemes[colormap], LinRange(0,1,256))
    ctmp = "0 '#$(hex(cmap[1]))',"
    for i in 2:256; ctmp = ctmp*"$(i-1) '#$(hex(cmap[i]))'," end;
    "set palette defined("*ctmp[1:end-1]*")"
end
# Archimedes spiral
a = 1.5
b = -2.4
t = LinRange(0,5*π,500)
x = (a .+ b*t) .* cos.(t)
y = (a .+ b*t) .* sin.(t)
@gp " " :-  # this is just to reset session
@gp :- "set multiplot layout 3,3; set key off;
    unset ytics; unset xtics; unset border" :-
colormaps = [:magma, :viridis, :plasma, :inferno, :berlin,
    :leonardo, :devon, :spring, :ice]
for i in 1:9
    @gp :- i title = "$(colormaps[i])" "set size square" :-
    @gp(:-, x, y, t, "w l lw 3 dt 1 lc palette",
        custom_palette(colormaps[i]),"set cbtics out nomirror", :-)
end
@gp
save(term="pngcairo size 900,800", output="plt_ex9.png") # hide


text:
# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
    AbstractThinning

A method for thinning spatial point processes and patterns.
"""
abstract type AbstractThinning end

"""
    thin(p, t)

Thin spatial point process `p` with thinning method `t`.
"""
function thin end

"""
    thin(pp, t)

Thin spatial point pattern `pp` with thinning method `t`.
"""
thin(::PointSet, ::AbstractThinning)

#-----------------
# IMPLEMENTATIONS
#-----------------
include("thinning/random.jl")


text:
function Constant(arg0::jdouble)
    return Constant((jdouble,), arg0)
end

function value(obj::Constant, arg0::DerivativeStructure)
    return jcall(obj, "value", DerivativeStructure, (DerivativeStructure,), arg0)
end

function value(obj::Constant, arg0::jdouble)
    return jcall(obj, "value", jdouble, (jdouble,), arg0)
end


text:
module ACE

using Base: NamedTuple
using Reexport

# external imports that are useful for all submodules
include("imports.jl")

@extimports
@baseimports

# TODO
# - move to imports

using ACEbase.ObjectPools: acquire!, release!, VectorPool
using ForwardDiff: derivative
import ChainRules: rrule, ZeroTangent, NoTangent
import ACEbase: evaluate, evaluate_d, gradtype, valtype,
                acquire_B!, release_B!, acquire_dB!, release_dB!,
                ACEBasis, acquire!, release!

# TODO: gradtype should have a standard fallback

abstract type AbstractACEModel end

abstract type AbstractProperty end

function coco_init end
function coco_zeros end
function coco_filter end
function coco_dot end
function coco_type end

# TODO
# * decide on rand(basis) interface

# * move these the following definitions to ACEbase
function _rrule_evaluate end
function _rrule_evaluate_d end

getlabel(basis::ACEBasis) = hasproperty(basis, :label) ? basis.label : ""

"""
This function is crucial to ACE internals. It implements the operation
```
(x, y) -> ∑_i x[i] * y[i]
```
i.e. like `dot` but without taking conjugates.
"""
contract(X1::AbstractVector, X2::AbstractVector) =
            sum(contract(x1, x2) for (x1, x2) in zip(X1, X2))

contract(x1::Union{Number, AbstractProperty},
         x2::Union{Number, AbstractProperty}) = x1 * x2

contract(X1::AbstractVector, x2::Union{Number, AbstractProperty}) = X1 * x2
contract(x1::Union{Number, AbstractProperty}, X2::AbstractVector) = x1 * X2

"""
sum of squares (without conjugation!)
"""
sumsq(x) = contract(x, x)

"""
norm-squared, i.e. sum xi * xi'
"""
normsq(x) = dot(x, x)

include("auxiliary.jl")
include("transforms/lambdas.jl")

include("pools.jl")
include("ad.jl")
include("chain.jl")

include("rotations3d.jl")
using ACE.Rotations3D

include("polynomials/wigner.jl")

include("states.jl")
include("symmetrygroups.jl")
include("properties.jl")

contract(X1::AbstractVector{<: DState}, x2::DState) = contract.(X1, Ref(x2))

include("prototypes.jl")

# methods to transform stuff:
#  - a state X into a variable that can be fed into a basis
#  - distance transforms
#  - transformations of an inner basis, e.g. linear transformations
include("transforms/transforms.jl"); @reexport using ACE.Transforms

# basic polynomial building blocks
include("polynomials/sphericalharmonics.jl")
include("polynomials/orthpolys.jl"); @reexport using ACE.OrthPolys

# 1p basis wrappers
include("b1pcomponent.jl")
include("b1pcomponents/Rn.jl")
include("b1pcomponents/Ylm.jl")
include("b1pcomponents/scal.jl")
include("b1pcomponents/trig.jl")

# todo - move this once we get to it...
include("discrete1pbasis.jl")

# the main product 1p basis
include("product_1pbasis.jl")

# basis selectors used to specify finite subsets of basis functions
include("basisselectors.jl")
# ... amongst other things used to initialize sparse basis sets
include("sparsegrids.jl")

# the permutation-invariant, and symmerized bases
include("pibasis.jl")
include("symmbasis.jl")

# some experimental stuff
include("multiplier.jl")

# models and model evaluators

include("linearmodel.jl")

include("evaluator.jl")
# include("grapheval.jl")

include("random.jl")
@reexport using ACE.Random

include("utils.jl")
@reexport using ACE.Utils

include("testing/testing.jl")

# include("ad.jl")

# ---------------- some extra experimental dispatching

evaluate(basis::SymmetricBasis, Xs::AbstractVector) =
      evaluate(basis, ACEConfig(Xs))

evaluate_d(basis::SymmetricBasis, Xs::AbstractVector) =
      evaluate_d(basis, ACEConfig(Xs))

evaluate(model::LinearACEModel, Xs::AbstractVector) =
      evaluate(model, ACEConfig(Xs))

grad_config(model::LinearACEModel, Xs::AbstractVector) =
      grad_config(model, ACEConfig(Xs))

grad_params(model::LinearACEModel, Xs::AbstractVector) =
      grad_params(model, ACEConfig(Xs))

end # module

# TODO:
# # orthogonal basis
# include("orth.jl")

# include("utils/importv5.jl")
# include("compat/compat.jl")
# include("export/export.jl")
# - bond model
# - pure basis
# - real basis
# - regularisers
# - descriptors
# - random potentials
# TODO -> move elsewhere!!!
# include("rpi/rpi_degrees.jl")


text:
######################################################################
# Cancellable tasks & tasks with timeouts.
# TODO: What about @sync & @async compatibility?
# -----
# Licensed under MIT License
module CancellableTasks
import ..ExtraFun
import ..ExtraFun: @await, CancellationError, TimeoutError

struct CancellableTask
    wrapped::Task
end
export with_cancel
function with_cancel(cb, schedule_immediately::Bool = true)
    task = CancellableTask(@task cb())
    if schedule_immediately
        schedule(task)
    end
end

"""`cancel(cancellable_task, reason = nothing)`
Cancel a blocking/yielding task with a `CancellationError(reason)`. The task may intercept this error in a try ... catch
block if necessary."""
function ExtraFun.cancel(task::CancellableTask, reason = nothing)
    if task.wrapped._state == 0
        schedule(task.wrapped, CancellationError(reason), error=true)
    end
    return task
end

Base.schedule(task::CancellableTask) = (schedule(task.wrapped); task)
Base.schedule(task::CancellableTask, val; error::Bool = false) = (schedule(task.wrapped, val, error=error); task)
Base.wait(task::CancellableTask) = wait(task.wrapped)
Base.fetch(task::CancellableTask) = fetch(task.wrapped)

struct TimeoutTask
    task_cb::Task
    task_timeout::Task
    _scheduled_once::Ref{Bool}
end
export with_timeout
function with_timeout(cb, timeout::Real; schedule_immediately::Bool = true)
    if timeout <= 0
        return with_cancel(cb, schedule_immediately)
    end

    task_cb = @task begin
        result = cb()
        schedule(task_timeout, CancellationError(), error=true)
        return result
    end
    task_timeout = @task begin
        sleep(timeout)
        schedule(task_cb, TimeoutError(), error=true)
    end

    wrapper = TimeoutTask(task_cb, task_timeout, Ref{Bool}(false))
    if schedule_immediately
        schedule(wrapper)
        wrapper._scheduled_once[] = true
    end
    wrapper
end

function ExtraFun.cancel(task::TimeoutTask, reason = nothing)
    if task.task_cb._state == 0
        err = CancellationError(reason)
        schedule(task.task_cb,      err, error=true)
        schedule(task.task_timeout, err, error=true)
    end
    return task
end

function Base.schedule(task::TimeoutTask)
    schedule(task.task_cb)
    schedule(task.task_timeout)
    return task
end
function Base.schedule(task::TimeoutTask, val; error::Bool = false)
    schedule(task.task_cb, val, error=true)
    if error
        schedule(task.task_timeout, val, error=true)
    else
        schedule(task.task_timeout)
    end
    return task
end
function Base.wait(task::TimeoutTask)
    if !task._scheduled_once[]
        schedule(task.task_timeout)
        _scheduled_once[] = true
    end
    wait(task.task_cb)
    task
end
function Base.fetch(task::TimeoutTask)
    wait(task)
    return fetch(task.task_cb)
end

end # module CancellableTasks

max_stars_repo_name: Kirusifix/ExtraFun

max_issues_repo_name: Kirusifix/ExtraFun

max_forks_repo_name: Kirusifix/ExtraFun


text:
#
# This file is part of ThreadingExperiments.jl
#
# Paul Bayer, 2020
# license: MIT
#
using ThreadingExperiments, .Threads

onthread(threadid, 1)
onthread(threadid, 2)

onthread(3) do
    threadid()
end

onthread(2) do # Machin-like series
    qpi = 0.0
    for i in 1:10_000
        qpi += (-1)^(i+1)/(2i-1)
    end
    qpi*4
end


text:
"""
Dynamical equations with faults for MulticopterEnv.
# Variables
f ∈ R: total thrust
M ∈ R^3: moment
Λ: effectiveness matrix
"""
function Dynamics!(multicopter::MulticopterEnv)
    @Loggable function dynamics!(dX, X, p, t; u, Λ)
        @assert all(diag(Λ) .>= 0.0) && all(diag(Λ) .<= 1.0)
        @nested_log :FDI Λ = Matrix(Λ)  # to assign off-diagonal terms for diffeq (if Λ <: Diagonal)
        @nested_onlylog :input u_cmd = u
        @nested_log :input u_saturated = FlightSims.saturate(multicopter, u)
        @nested_log :input u_faulted = Λ * u_saturated
        @nested_onlylog :input u_actual = u_faulted
        # @show u_faulted ./ u_saturated, t  # for debugging
        @nested_log :input ν = FlightSims.input_to_force_moment(multicopter, u_faulted)  # for manual input_to_force_moment transformation, extend this method
        f, M = ν[1], ν[2:4]
        @nested_log FlightSims.__Dynamics!(multicopter)(dX, X, p, t; f=f, M=M)  # :state, :input
    end
end


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

module TestGivens

using Test, LinearAlgebra, Random
using LinearAlgebra: mul1!, mul2!

# Test givens rotations
@testset for elty in (Float32, Float64, ComplexF32, ComplexF64)
    if elty <: Real
        raw_A = convert(Matrix{elty}, randn(10,10))
    else
        raw_A = convert(Matrix{elty}, complex.(randn(10,10),randn(10,10)))
    end
    @testset for A in (raw_A, view(raw_A, 1:10, 1:10))
        Ac = copy(A)
        R = LinearAlgebra.Rotation(LinearAlgebra.Givens{elty}[])
        for j = 1:8
            for i = j+2:10
                G, _ = givens(A, j+1, i, j)
                mul2!(G, A)
                mul1!(A, adjoint(G))
                mul2!(G, R)

                @test mul2!(G,Matrix{elty}(I, 10, 10)) == [G[i,j] for i=1:10,j=1:10]

                @testset "transposes" begin
                    @test copy(G')*G*Matrix(elty(1)I, 10, 10) ≈ Matrix(I, 10, 10)
                    @test copy(R')*(R*Matrix(elty(1)I, 10, 10)) ≈ Matrix(I, 10, 10)
                    @test_throws ErrorException transpose(G)
                    @test_throws ErrorException transpose(R)
                end
            end
        end
        @test_throws ArgumentError givens(A, 3, 3, 2)
        @test_throws ArgumentError givens(one(elty),zero(elty),2,2)
        G, _ = givens(one(elty),zero(elty),11,12)
        @test_throws DimensionMismatch mul2!(G, A)
        @test_throws DimensionMismatch mul1!(A, adjoint(G))
        @test abs.(A) ≈ abs.(hessfact(Ac).H)
        @test norm(R*Matrix{elty}(I, 10, 10)) ≈ one(elty)

        I10 = Matrix{elty}(I, 10, 10)
        G, _ = givens(one(elty),zero(elty),9,10)
        @test (G*I10)' * (G*I10) ≈ I10
        K, _ = givens(zero(elty),one(elty),9,10)
        @test (K*I10)' * (K*I10) ≈ I10

        @testset "Givens * vectors" begin
            if isa(A, Array)
                x = A[:, 1]
            else
                x = view(A, 1:10, 1)
            end
            G, r = givens(x[2], x[4], 2, 4)
            @test (G*x)[2] ≈ r
            @test abs((G*x)[4]) < eps(real(elty))
            @inferred givens(x[2], x[4], 2, 4)

            G, r = givens(x, 2, 4)
            @test (G*x)[2] ≈ r
            @test abs((G*x)[4]) < eps(real(elty))
            @inferred givens(x, 2, 4)

            G, r = givens(x, 4, 2)
            @test (G*x)[4] ≈ r
            @test abs((G*x)[2]) < eps(real(elty))
        end
    end
end

end # module TestGivens

max_stars_repo_name: ashaywalke/julia

max_issues_repo_name: ashaywalke/julia

max_forks_repo_name: ashaywalke/julia


text:
"""
    kDPP(m::Int, kernel::Kernel)

k-DPP (Determinantal Point Process) will return a subset of `X` of size `m`,
according to DPP probability
"""
struct kDPP{K<:Kernel} <: OffIPSA
    m::Int
    kernel::K
    function kDPP(m::Int, kernel::K) where {K<:Kernel}
        m > 0 || throw(ArgumentError("The number of inducing points m should be positive"))
        return new{K}(m, kernel)
    end
end

function inducingpoints(rng::AbstractRNG, alg::kDPP, X::AbstractVector; kwargs...)
    if alg.m >= length(X)
        return edge_case(alg.m, length(X), X)
    end
    return kdpp_ip(rng, X, alg.m, alg.kernel)
end

Base.show(io::IO, ::kDPP) = print(io, "k-DPP selection of inducing points")

function kdpp_ip(rng::AbstractRNG, X::AbstractVector, m::Int, kernel::Kernel)
    return X[rand(rng, DPP(kernel, X)(m))] # Sample m indices from a DPP
end


text:
"""
    Quartic{T}

The scalar conservation law with flux
``
  \\partial_t u + \\partial_x u^4 = 0
``
in one space dimensions using `T` as scalar type.
"""
struct Quartic{T} <: ScalarBalanceLaw{T,1} end

function Quartic(T=Float64)
  Quartic{T}()
end

function Base.show(io::IO, model::Quartic{T}) where {T}
  print(io, "Scalar conservation law {T=", T, "} with flux f(u) = u^4")
end

"""
    flux(u, model::Quartic)

Compute the flux of `u` for `model`.
"""
@inline flux(u, model::Quartic) = (u^2)^2

"""
    speed(u::Real, model::Quartic)

Compute the speed f'(`u`) for `model`.
"""
@inline speed(u, model::Quartic) = 4u^3

################################################################################

"""
    (::GodunovFlux)(uₗ::T, uᵣ::T, model::Quartic{T}) where {T}

Compute Godunov's flux between `uₗ` and `uᵣ` for `model`.
"""
function (::GodunovFlux)(uₗ::T, uᵣ::T, model::Quartic{T}) where {T}
    if uₗ < uᵣ
      if uₗ < 0 && 0 < uᵣ
        zero(T)
      else
        min(flux(uₗ, model), flux(uᵣ, model))
      end
    else
      max(flux(uₗ, model), flux(uᵣ, model))
    end
end

"""
    (::EnergyConservativeFlux)(uₗ::T, uᵣ::T, model::Quartic{T}) where {T}

Compute the energy (L₂ entropy) conservative flux between `uₗ` and `uᵣ` for `model`.
"""
function (flux::EnergyConservativeFlux)(uₗ::T, uᵣ::T, model::Quartic{T}) where T<:Real
    ((uₗ^2)^2 + uₗ^3*uᵣ + uₗ^2*uᵣ^2 + uₗ*uᵣ^3 + (uᵣ^2)^2) / 5
end

################################################################################

@inline function add_numerical_fluxes_inner_loop2!(du, fluxes, u, balance_law::Quartic,
                                                    Nx, basis, boundaries_included::Val{false},
                                                    jacx, parallel)
    Pp1 = length(basis.nodes)
    Rl = basis.interp_left
    Rr = basis.interp_right
    one_5 = 1 / 5
    two_5 = 2 / 5

    # add numerical fluxes
    @inbounds for ix in Base.OneTo(Nx)
        Rul = zero(eltype(u))
        Rur = zero(eltype(u))
        for nx in Base.OneTo(Pp1)
            Rul += Rl[nx]*u[nx,ix]
            Rur += Rr[nx]*u[nx,ix]
        end
        Ru2l = zero(eltype(u))
        Ru2r = zero(eltype(u))
        for nx in Base.OneTo(Pp1)
            tmp = u[nx,ix]^2
            Ru2l += Rl[nx]*tmp
            Ru2r += Rr[nx]*tmp
        end
        Ru3l = zero(eltype(u))
        Ru3r = zero(eltype(u))
        for nx in Base.OneTo(Pp1)
            tmp = u[nx,ix]^3
            Ru3l += Rl[nx]*tmp
            Ru3r += Rr[nx]*tmp
        end
        Ru4l = zero(eltype(u))
        Ru4r = zero(eltype(u))
        for nx in Base.OneTo(Pp1)
            tmp = (u[nx,ix]^2)^2
            Ru4l += Rl[nx]*tmp
            Ru4r += Rr[nx]*tmp
        end

        for nx in Base.OneTo(Pp1)
            du[nx,ix] += (  (fluxes[ix  ]   - two_5 * Ru4l
                                            - one_5 * Ru2l^2
                                            - one_5 * Ru2l * Rul^2
                                            - one_5 * (Rul^2)^2
                                            - two_5 * u[nx,ix] * Ru3l
                                            + one_5 * u[nx,ix] * Ru2l * Rul
                                            + one_5 * u[nx,ix] * Rul^3) * Rl[nx]
                          - (fluxes[ix+1]   - two_5 * Ru4r
                                            - one_5 * Ru2r^2
                                            - one_5 * Ru2r * Rur^2
                                            - one_5 * (Rur^2)^2
                                            - two_5 * u[nx,ix] * Ru3r
                                            + one_5 * u[nx,ix] * Ru2r * Rur
                                            + one_5 * u[nx,ix] * Rur^3) * Rr[nx]
                            ) * jacx / basis.weights[nx]
        end
    end

    nothing
end


text:
"""
测试单带的正方格子
"""

using Plots
using MARY_fRG.Fermi
using MARY_fRG.FlowEquation

#阻止画图
ENV["GKSwstype"] = "100"

"""
从头开始运行代码
"""
function run_ec()
    model = common_square_lattice(0.20)
    Γ4 = ECGamma4(
        model, 4.0, 16, 50
    )
    Γ4.V .+= 1.0
    lval = 0.
    lstep = 0.01
    for idx in 1:1:701
        bubb_pp, bubb_fs, bubb_nfs, bubb_ex, bubb_nex = all_bubble_ec_mt(Γ4, lval)
        dl = dl_ec_mt(Γ4, bubb_pp, bubb_fs, bubb_nfs, bubb_ex, bubb_nex)
        Γ4.V .+= dl .* lstep
        if idx % 50 == 1
            plt = heatmap(Γ4.V[1, 1, 1, 1, :, :, 1])
            png(plt, "Gamma4"*string(idx))
        end
        lval += lstep
    end
end

#run_ec()

function run_tf()
    model = common_square_lattice(0.00)
    Γ4 = TFGamma4(
        model, 8.0, 16, 100
    )
    Γ4.V .+= 1.0
    lval = 0.
    lstep = 0.01
    for idx in 1:1:701
        bubb_pp, bubb_fs, bubb_ex = all_bubble_tf_mt(Γ4, lval)
        dl = dl_tf_mt(Γ4, bubb_pp, bubb_fs, bubb_ex)
        Γ4.V .+= dl .* lstep
        if idx % 50 == 1
            plt = heatmap(Γ4.V[1, 1, 1, 1, :, :, 1])
            png(plt, "Gamma4"*string(idx))
        end
        lval += lstep
    end
end

run_tf()


text:
using DiffEqGPU, CUDA, StochasticDiffEq, Test

function lorenz(du,u,p,t)
 @inbounds begin
     du[1] = p[1]*(u[2]-u[1])
     du[2] = u[1]*(p[2]-u[3]) - u[2]
     du[3] = u[1]*u[2] - p[3]*u[3]
 end
 nothing
end

function multiplicative_noise(du,u,p,t)
 @inbounds begin
     du[1] = 0.1*u[1]
     du[2] = 0.1*u[2]
     du[3] = 0.1*u[3]
 end
 nothing
end

CUDA.allowscalar(false)
u0 = Float32[1.0;0.0;0.0]
tspan = (0.0f0,10.0f0)
p = (10.0f0,28.0f0,8/3f0)
prob = SDEProblem(lorenz,multiplicative_noise,u0,tspan,p)
const pre_p = [rand(Float32,3) for i in 1:10]
prob_func = (prob,i,repeat) -> remake(prob,p=pre_p[i].*p)
monteprob = EnsembleProblem(prob, prob_func = prob_func)

@info "Explicit Methods"

#Performance check with nvvp
# CUDAnative.CUDAdrv.@profile
@time sol = solve(monteprob,SOSRI(),EnsembleGPUArray(),trajectories=10,saveat=1.0f0)