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

"""
    create_dataset(dataset_type, project_name)
    create_dataset(dataset_type, project_name, params::Dict{String,<:Any})

Creates a new dataset in an Amazon Lookout for Vision project. CreateDataset can create a
training or a test dataset from a valid dataset source (DatasetSource). If you want a
single dataset project, specify train for the value of DatasetType. To have a project with
separate training and test datasets, call CreateDataset twice. On the first call, specify
train for the value of DatasetType. On the second call, specify test for the value of
DatasetType.  This operation requires permissions to perform the
lookoutvision:CreateDataset operation.

# Arguments
- `dataset_type`: The type of the dataset. Specify train for a training dataset. Specify
  test for a test dataset.
- `project_name`: The name of the project in which you want to create a dataset.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DatasetSource"`: The location of the manifest file that Amazon Lookout for Vision uses
  to create the dataset. If you don't specify DatasetSource, an empty dataset is created and
  the operation synchronously returns. Later, you can add JSON Lines by calling
  UpdateDatasetEntries.  If you specify a value for DataSource, the manifest at the S3
  location is validated and used to create the dataset. The call to CreateDataset is
  asynchronous and might take a while to complete. To find out the current status, Check the
  value of Status returned in a call to DescribeDataset.
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  CreateDataset completes only once. You choose the value to pass. For example, An issue,
  such as an network outage, might prevent you from getting a response from CreateDataset. In
  this case, safely retry your call to CreateDataset by using the same ClientToken parameter
  value. An error occurs if the other input parameters are not the same as in the first
  request. Using a different value for ClientToken is considered a new call to CreateDataset.
  An idempotency token is active for 8 hours.
"""
function create_dataset(
    DatasetType, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/datasets",
        Dict{String,Any}(
            "DatasetType" => DatasetType, "X-Amzn-Client-Token" => string(uuid4())
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_dataset(
    DatasetType,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/datasets",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DatasetType" => DatasetType, "X-Amzn-Client-Token" => string(uuid4())
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_model(output_config, project_name)
    create_model(output_config, project_name, params::Dict{String,<:Any})

Creates a new version of a model within an an Amazon Lookout for Vision project.
CreateModel is an asynchronous operation in which Amazon Lookout for Vision trains, tests,
and evaluates a new version of a model.  To get the current status, check the Status field
returned in the response from DescribeModel. If the project has a single dataset, Amazon
Lookout for Vision internally splits the dataset to create a training and a test dataset.
If the project has a training and a test dataset, Lookout for Vision uses the respective
datasets to train and test the model.  After training completes, the evaluation metrics are
stored at the location specified in OutputConfig.  This operation requires permissions to
perform the lookoutvision:CreateModel operation. If you want to tag your model, you also
require permission to the lookoutvision:TagResource operation.

# Arguments
- `output_config`: The location where Amazon Lookout for Vision saves the training results.
- `project_name`: The name of the project in which you want to create a model version.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A description for the version of the model.
- `"KmsKeyId"`: The identifier for your AWS Key Management Service (AWS KMS) customer
  master key (CMK). The key is used to encrypt training and test images copied into the
  service for model training. Your source images are unaffected. If this parameter is not
  specified, the copied images are encrypted by a key that AWS owns and manages.
- `"Tags"`: A set of tags (key-value pairs) that you want to attach to the model.
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  CreateModel completes only once. You choose the value to pass. For example, An issue, such
  as an network outage, might prevent you from getting a response from CreateModel. In this
  case, safely retry your call to CreateModel by using the same ClientToken parameter value.
  An error occurs if the other input parameters are not the same as in the first request.
  Using a different value for ClientToken is considered a new call to CreateModel. An
  idempotency token is active for 8 hours.
"""
function create_model(
    OutputConfig, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models",
        Dict{String,Any}(
            "OutputConfig" => OutputConfig, "X-Amzn-Client-Token" => string(uuid4())
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_model(
    OutputConfig,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "OutputConfig" => OutputConfig, "X-Amzn-Client-Token" => string(uuid4())
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Creates an empty Amazon Lookout for Vision project. After you create the project, add a
dataset by calling CreateDataset. This operation requires permissions to perform the
lookoutvision:CreateProject operation.

# Arguments
- `project_name`: The name for the project.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  CreateProject completes only once. You choose the value to pass. For example, An issue,
  such as an network outage, might prevent you from getting a response from CreateProject. In
  this case, safely retry your call to CreateProject by using the same ClientToken parameter
  value. An error occurs if the other input parameters are not the same as in the first
  request. Using a different value for ClientToken is considered a new call to CreateProject.
  An idempotency token is active for 8 hours.
"""
function create_project(ProjectName; aws_config::AbstractAWSConfig=global_aws_config())
    return lookoutvision(
        "POST",
        "/2020-11-20/projects",
        Dict{String,Any}(
            "ProjectName" => ProjectName, "X-Amzn-Client-Token" => string(uuid4())
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_project(
    ProjectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ProjectName" => ProjectName, "X-Amzn-Client-Token" => string(uuid4())
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an existing Amazon Lookout for Vision dataset.  If your the project has a single
dataset, you must create a new dataset before you can create a model. If you project has a
training dataset and a test dataset consider the following.    If you delete the test
dataset, your project reverts to a single dataset project. If you then train the model,
Amazon Lookout for Vision internally splits the remaining dataset into a training and test
dataset.   If you delete the training dataset, you must create a training dataset before
you can create a model.   This operation requires permissions to perform the
lookoutvision:DeleteDataset operation.

# Arguments
- `dataset_type`: The type of the dataset to delete. Specify train to delete the training
  dataset. Specify test to delete the test dataset. To delete the dataset in a single dataset
  project, specify train.
- `project_name`: The name of the project that contains the dataset that you want to delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  DeleteDataset completes only once. You choose the value to pass. For example, An issue,
  such as an network outage, might prevent you from getting a response from DeleteDataset. In
  this case, safely retry your call to DeleteDataset by using the same ClientToken parameter
  value. An error occurs if the other input parameters are not the same as in the first
  request. Using a different value for ClientToken is considered a new call to DeleteDataset.
  An idempotency token is active for 8 hours.
"""
function delete_dataset(
    datasetType, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "DELETE",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)",
        Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4()));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_dataset(
    datasetType,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "DELETE",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4())), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_model(model_version, project_name)
    delete_model(model_version, project_name, params::Dict{String,<:Any})

Deletes an Amazon Lookout for Vision model. You can't delete a running model. To stop a
running model, use the StopModel operation. It might take a few seconds to delete a model.
To determine if a model has been deleted, call ListProjects and check if the version of the
model (ModelVersion) is in the Models array.  This operation requires permissions to
perform the lookoutvision:DeleteModel operation.

# Arguments
- `model_version`: The version of the model that you want to delete.
- `project_name`: The name of the project that contains the model that you want to delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  DeleteModel completes only once. You choose the value to pass. For example, An issue, such
  as an network outage, might prevent you from getting a response from DeleteModel. In this
  case, safely retry your call to DeleteModel by using the same ClientToken parameter value.
  An error occurs if the other input parameters are not the same as in the first request.
  Using a different value for ClientToken is considered a new call to DeleteModel. An
  idempotency token is active for 8 hours.
"""
function delete_model(
    modelVersion, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "DELETE",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)",
        Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4()));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_model(
    modelVersion,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "DELETE",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4())), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an Amazon Lookout for Vision project. To delete a project, you must first delete
each version of the model associated with the project. To delete a model use the
DeleteModel operation. You also have to delete the dataset(s) associated with the model.
For more information, see DeleteDataset. The images referenced by the training and test
datasets aren't deleted.  This operation requires permissions to perform the
lookoutvision:DeleteProject operation.

# Arguments
- `project_name`: The name of the project to delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  DeleteProject completes only once. You choose the value to pass. For example, An issue,
  such as an network outage, might prevent you from getting a response from DeleteProject. In
  this case, safely retry your call to DeleteProject by using the same ClientToken parameter
  value. An error occurs if the other input parameters are not the same as in the first
  request. Using a different value for ClientToken is considered a new call to DeleteProject.
  An idempotency token is active for 8 hours.
"""
function delete_project(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return lookoutvision(
        "DELETE",
        "/2020-11-20/projects/$(projectName)",
        Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4()));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_project(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "DELETE",
        "/2020-11-20/projects/$(projectName)",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4())), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Describe an Amazon Lookout for Vision dataset. This operation requires permissions to
perform the lookoutvision:DescribeDataset operation.

# Arguments
- `dataset_type`: The type of the dataset to describe. Specify train to describe the
  training dataset. Specify test to describe the test dataset. If you have a single dataset
  project, specify train
- `project_name`: The name of the project that contains the dataset that you want to
  describe.

"""
function describe_dataset(
    datasetType, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_dataset(
    datasetType,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_model(model_version, project_name)
    describe_model(model_version, project_name, params::Dict{String,<:Any})

Describes a version of an Amazon Lookout for Vision model. This operation requires
permissions to perform the lookoutvision:DescribeModel operation.

# Arguments
- `model_version`: The version of the model that you want to describe.
- `project_name`: The project that contains the version of a model that you want to
  describe.

"""
function describe_model(
    modelVersion, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_model(
    modelVersion,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Describes an Amazon Lookout for Vision project. This operation requires permissions to
perform the lookoutvision:DescribeProject operation.

# Arguments
- `project_name`: The name of the project that you want to describe.

"""
function describe_project(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_project(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    detect_anomalies(body, content-_type, model_version, project_name)
    detect_anomalies(body, content-_type, model_version, project_name, params::Dict{String,<:Any})

Detects anomalies in an image that you supply.  The response from DetectAnomalies includes
a boolean prediction that the image contains one or more anomalies and a confidence value
for the prediction.  Before calling DetectAnomalies, you must first start your model with
the StartModel operation. You are charged for the amount of time, in minutes, that a model
runs and for the number of anomaly detection units that your model uses. If you are not
using a model, use the StopModel operation to stop your model.   This operation requires
permissions to perform the lookoutvision:DetectAnomalies operation.

# Arguments
- `body`: The unencrypted image bytes that you want to analyze.
- `content-_type`: The type of the image passed in Body. Valid values are image/png (PNG
  format images) and image/jpeg (JPG format images).
- `model_version`: The version of the model that you want to use.
- `project_name`: The name of the project that contains the model version that you want to
  use.

"""
function detect_anomalies(
    Body,
    Content_Type,
    modelVersion,
    projectName;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)/detect",
        Dict{String,Any}(
            "Body" => Body, "headers" => Dict{String,Any}("Content-Type" => Content_Type)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function detect_anomalies(
    Body,
    Content_Type,
    modelVersion,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)/detect",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Body" => Body,
                    "headers" => Dict{String,Any}("Content-Type" => Content_Type),
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_dataset_entries(dataset_type, project_name)
    list_dataset_entries(dataset_type, project_name, params::Dict{String,<:Any})

Lists the JSON Lines within a dataset. An Amazon Lookout for Vision JSON Line contains the
anomaly information for a single image, including the image location and the assigned
label. This operation requires permissions to perform the lookoutvision:ListDatasetEntries
operation.

# Arguments
- `dataset_type`: The type of the dataset that you want to list. Specify train to list the
  training dataset. Specify test to list the test dataset. If you have a single dataset
  project, specify train.
- `project_name`: The name of the project that contains the dataset that you want to list.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"anomalyClass"`: Specify normal to include only normal images. Specify anomaly to only
  include anomalous entries. If you don't specify a value, Amazon Lookout for Vision returns
  normal and anomalous images.
- `"createdAfter"`: Only includes entries after the specified date in the response. For
  example, 2020-06-23T00:00:00.
- `"createdBefore"`: Only includes entries before the specified date in the response. For
  example, 2020-06-23T00:00:00.
- `"labeled"`: Specify true to include labeled entries, otherwise specify false. If you
  don't specify a value, Lookout for Vision returns all entries.
- `"maxResults"`: The maximum number of results to return per paginated call. The largest
  value you can specify is 100. If you specify a value greater than 100, a
  ValidationException error occurs. The default value is 100.
- `"nextToken"`: If the previous response was incomplete (because there is more data to
  retrieve), Amazon Lookout for Vision returns a pagination token in the response. You can
  use this pagination token to retrieve the next set of dataset entries.
- `"sourceRefContains"`: Perform a \"contains\" search on the values of the source-ref key
  within the dataset. For example a value of \"IMG_17\" returns all JSON Lines where the
  source-ref key value matches *IMG_17*.
"""
function list_dataset_entries(
    datasetType, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)/entries";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_dataset_entries(
    datasetType,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)/entries",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the versions of a model in an Amazon Lookout for Vision project. This operation
requires permissions to perform the lookoutvision:ListModels operation.

# Arguments
- `project_name`: The name of the project that contains the model versions that you want to
  list.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return per paginated call. The largest
  value you can specify is 100. If you specify a value greater than 100, a
  ValidationException error occurs. The default value is 100.
- `"nextToken"`: If the previous response was incomplete (because there is more data to
  retrieve), Amazon Lookout for Vision returns a pagination token in the response. You can
  use this pagination token to retrieve the next set of models.
"""
function list_models(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/models";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_models(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects/$(projectName)/models",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the Amazon Lookout for Vision projects in your AWS account. This operation requires
permissions to perform the lookoutvision:ListProjects operation.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return per paginated call. The largest
  value you can specify is 100. If you specify a value greater than 100, a
  ValidationException error occurs. The default value is 100.
- `"nextToken"`: If the previous response was incomplete (because there is more data to
  retrieve), Amazon Lookout for Vision returns a pagination token in the response. You can
  use this pagination token to retrieve the next set of projects.
"""
function list_projects(; aws_config::AbstractAWSConfig=global_aws_config())
    return lookoutvision(
        "GET",
        "/2020-11-20/projects";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_projects(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "GET",
        "/2020-11-20/projects",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of tags attached to the specified Amazon Lookout for Vision model. This
operation requires permissions to perform the lookoutvision:ListTagsForResource operation.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the model for which you want to list
  tags.

"""
function list_tags_for_resource(
    resourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "GET",
        "/2020-11-20/tags/$(resourceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    resourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "GET",
        "/2020-11-20/tags/$(resourceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    start_model(min_inference_units, model_version, project_name)
    start_model(min_inference_units, model_version, project_name, params::Dict{String,<:Any})

Starts the running of the version of an Amazon Lookout for Vision model. Starting a model
takes a while to complete. To check the current state of the model, use DescribeModel. A
model is ready to use when its status is HOSTED. Once the model is running, you can detect
custom labels in new images by calling DetectAnomalies.  You are charged for the amount of
time that the model is running. To stop a running model, call StopModel.  This operation
requires permissions to perform the lookoutvision:StartModel operation.

# Arguments
- `min_inference_units`: The minimum number of inference units to use. A single inference
  unit represents 1 hour of processing and can support up to 5 Transaction Pers Second (TPS).
  Use a higher number to increase the TPS throughput of your model. You are charged for the
  number of inference units that you use.
- `model_version`: The version of the model that you want to start.
- `project_name`: The name of the project that contains the model that you want to start.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  StartModel completes only once. You choose the value to pass. For example, An issue, such
  as an network outage, might prevent you from getting a response from StartModel. In this
  case, safely retry your call to StartModel by using the same ClientToken parameter value.
  An error occurs if the other input parameters are not the same as in the first request.
  Using a different value for ClientToken is considered a new call to StartModel. An
  idempotency token is active for 8 hours.
"""
function start_model(
    MinInferenceUnits,
    modelVersion,
    projectName;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)/start",
        Dict{String,Any}(
            "MinInferenceUnits" => MinInferenceUnits,
            "X-Amzn-Client-Token" => string(uuid4()),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function start_model(
    MinInferenceUnits,
    modelVersion,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)/start",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "MinInferenceUnits" => MinInferenceUnits,
                    "X-Amzn-Client-Token" => string(uuid4()),
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    stop_model(model_version, project_name)
    stop_model(model_version, project_name, params::Dict{String,<:Any})

Stops the hosting of a running model. The operation might take a while to complete. To
check the current status, call DescribeModel.  After the model hosting stops, the Status of
the model is TRAINED. This operation requires permissions to perform the
lookoutvision:StopModel operation.

# Arguments
- `model_version`: The version of the model that you want to stop.
- `project_name`: The name of the project that contains the model that you want to stop.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  StopModel completes only once. You choose the value to pass. For example, An issue, such as
  an network outage, might prevent you from getting a response from StopModel. In this case,
  safely retry your call to StopModel by using the same ClientToken parameter value. An error
  occurs if the other input parameters are not the same as in the first request. Using a
  different value for ClientToken is considered a new call to StopModel. An idempotency token
  is active for 8 hours.
"""
function stop_model(
    modelVersion, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)/stop",
        Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4()));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function stop_model(
    modelVersion,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/projects/$(projectName)/models/$(modelVersion)/stop",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("X-Amzn-Client-Token" => string(uuid4())), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Adds one or more key-value tags to an Amazon Lookout for Vision model. For more
information, see Tagging a model in the Amazon Lookout for Vision Developer Guide.  This
operation requires permissions to perform the lookoutvision:TagResource operation.

# Arguments
- `tags`: The key-value tags to assign to the model.
- `resource_arn`: The Amazon Resource Name (ARN) of the model to assign the tags.

"""
function tag_resource(Tags, resourceArn; aws_config::AbstractAWSConfig=global_aws_config())
    return lookoutvision(
        "POST",
        "/2020-11-20/tags/$(resourceArn)",
        Dict{String,Any}("Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    Tags,
    resourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "POST",
        "/2020-11-20/tags/$(resourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Tags" => Tags), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Removes one or more tags from an Amazon Lookout for Vision model. For more information, see
Tagging a model in the Amazon Lookout for Vision Developer Guide.  This operation requires
permissions to perform the lookoutvision:UntagResource operation.

# Arguments
- `resource_arn`: The Amazon Resource Name (ARN) of the model from which you want to remove
  tags.
- `tag_keys`: A list of the keys of the tags that you want to remove.

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

"""
    update_dataset_entries(changes, dataset_type, project_name)
    update_dataset_entries(changes, dataset_type, project_name, params::Dict{String,<:Any})

Adds one or more JSON Line entries to a dataset. A JSON Line includes information about an
image used for training or testing an Amazon Lookout for Vision model. The following is an
example JSON Line. Updating a dataset might take a while to complete. To check the current
status, call DescribeDataset and check the Status field in the response. This operation
requires permissions to perform the lookoutvision:UpdateDatasetEntries operation.

# Arguments
- `changes`: The entries to add to the dataset.
- `dataset_type`: The type of the dataset that you want to update. Specify train to update
  the training dataset. Specify test to update the test dataset. If you have a single dataset
  project, specify train.
- `project_name`: The name of the project that contains the dataset that you want to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"X-Amzn-Client-Token"`: ClientToken is an idempotency token that ensures a call to
  UpdateDatasetEntries completes only once. You choose the value to pass. For example, An
  issue, such as an network outage, might prevent you from getting a response from
  UpdateDatasetEntries. In this case, safely retry your call to UpdateDatasetEntries by using
  the same ClientToken parameter value. An error occurs if the other input parameters are not
  the same as in the first request. Using a different value for ClientToken is considered a
  new call to UpdateDatasetEntries. An idempotency token is active for 8 hours.
"""
function update_dataset_entries(
    Changes, datasetType, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return lookoutvision(
        "PATCH",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)/entries",
        Dict{String,Any}("Changes" => Changes, "X-Amzn-Client-Token" => string(uuid4()));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_dataset_entries(
    Changes,
    datasetType,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return lookoutvision(
        "PATCH",
        "/2020-11-20/projects/$(projectName)/datasets/$(datasetType)/entries",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "Changes" => Changes, "X-Amzn-Client-Token" => string(uuid4())
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
function PhysicalModule(mod::Module)
    PhysicalModule(mod.magic_number, mod.generator_magic_number, spirv_version(mod.version), mod.bound, mod.schema, PhysicalInstruction.(mod.instructions))
end

function PhysicalInstruction(inst::Instruction)
    operands = physical_operands(inst)
    PhysicalInstruction(1 + length(operands) + !isnothing(inst.type_id) + !isnothing(inst.result_id), inst.opcode, inst.type_id, inst.result_id, operands)
end

function physical_operands(inst::Instruction)
    operands = Word[]
    opcode = inst.opcode
    op_infos = info(inst)
    arguments = inst.arguments
    for (i, (info, arg)) in enumerate(zip(op_infos, arguments))
        kind = info.kind
        if hasproperty(info, :quantifier)
            quantifier = info.quantifier
            @match quantifier begin
                "*" => begin
                    foreach(arguments[i:end]) do arg
                        add_operand!(operands, arg, kind)
                    end
                    break
                end
                "?" => begin
                    nmissing = length(arguments) - (i - 1)
                    nopt = count(x -> hasproperty(x, :quantifier) && x.quantifier == "?", op_infos[i:end])
                    if nmissing == nopt
                        add_operand!(operands, arg, kind)
                    elseif nmissing > 0
                        error("Unsupported number of values: found $nmissing values for $nopt optional arguments")
                    end
                end
            end
        else
            add_operand!(operands, arg, kind)
        end
    end

    operands
end

function add_operand!(operands, arg, kind)
    @match kind begin
        &LiteralString => begin
            utf8_chars = collect(transcode(UInt8, arg))
            push!(utf8_chars, '\0')
            nrem = length(utf8_chars) % 4
            if nrem ≠ 0
                append!(utf8_chars, zeros(4 - nrem))
            end
            append!(operands, reinterpret(UInt32, utf8_chars))
        end
        _ => @match arg begin
            ::Vector{Word} => append!(operands, arg)
            _ => begin
                if kind isa Literal
                    sizeof(arg) <= 4 || error("Literals with a size greater than 32 bits are not supported.")
                end
                push!(operands, arg)
            end
        end
    end
end

function write(io::IO, mod::PhysicalModule)
    write(io, assemble(mod))
end

assemble(obj) = assemble!(Word[], obj)

function assemble!(words, inst::PhysicalInstruction)
    push!(words, UInt32(inst.word_count) << 16 + inst.opcode)

    # add optional type ID and result ID
    for field in (:type_id, :result_id)
        val = getproperty(inst, field)
        if !isnothing(val)
            push!(words, val)
        end
    end

    append!(words, inst.operands)

    words
end

function assemble!(words, mod::PhysicalModule)
    append!(words, Word[
        mod.magic_number,
        mod.version,
        mod.generator_magic_number,
        mod.bound,
        mod.schema,
    ])

    foreach(mod.instructions) do inst
        assemble!(words, inst)
    end

    words
end

assemble(mod::Module) = assemble(PhysicalModule(mod))

ext: jl


text:
#=
Load a DEM as a Point3 set in a factor graph
=#

using Images
using ImageView
using FileIO
using Interpolations
using Caesar
using RoME
using RoME: MeanMaxPPE
using ProgressMeter
using TensorCast

using TensorCast
using Cairo
using RoMEPlotting
using Gadfly
Gadfly.set_default_plot_size(35cm, 25cm)
using ImageMagick

## only run this if planning big solves

using Distributed
# localprocs
# addprocs(5);

##

if true
  machines = []
  allmc = split(ENV["JL_CLUSTER_HY"], ';')
  for mc in allmc
    push!(machines, (mc,20))
  end
  prc_rmt = addprocs(machines)
end

##

using Caesar, Cairo, RoMEPlotting
@everywhere using Caesar
@everywhere using Cairo, RoMEPlotting
@everywhere Gadfly.set_default_plot_size(35cm,25cm)

##

@everywhere prjPath = dirname(dirname(pathof(Caesar)))

@everywhere include(joinpath(prjPath,"examples/dev/scalar/CommonUtils.jl"))
@everywhere using .CommonUtils

##

# # load dem (18x18km span, ~17m/px)
x_min, x_max = -9000, 9000
y_min, y_max = -9000, 9000
# north is regular map image up
x, y, img = RoME.generateField_CanyonDEM(1, 100, x_is_north=false, x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)

# imshow(img)

## modify to generate elevation measurements (data/smallData as in Boxy) and priors

dem = Interpolations.LinearInterpolation((x,y), img) # interpolated DEM
elevation(p) = dem[getPPE(fg, p, :simulated).suggested[1:2]'...]
sigma_e = 0.01 # elevation measurement uncertainty

## test buildDEMSimulated

im = (j->((i->dem[i,j]).(x))).(y);
@cast im_[i,j] := im[j][i];
@assert norm(im_ - img) < 1e-10

##

function cb(fg_, lastpose)
  global dem, img

  # query DEM at ground truth
  z_e = elevation(lastpose)

  # generate noisy measurement
  @info "Callback for DEM LevelSet priors" lastpose ls(fg_, lastpose) z_e

  # create prior
  hmd = LevelSetGridNormal(img, (x,y), z_e, sigma_e, N=10000, sigma_scale=1)
  pr = PartialPriorPassThrough(hmd, (1,2))
  addFactor!(fg_, [lastpose], pr, tags=[:DEM;], graphinit=false, nullhypo=0.1)
  nothing
end

## Testing

# 0. init empty FG w/ datastore
fg = initfg()
storeDir = joinLogPath(fg,"data")
mkpath(storeDir)
datastore = FolderStore{Vector{UInt8}}(:default_folder_store, storeDir)
addBlobStore!(fg, datastore)

# new feature, going to temporarily disable as WIP
getSolverParams(fg).attemptGradients = false

##

# 1. load DEM into the factor graph
# point uncertainty - 2.5m horizontal, 1m vertical
# horizontal uncertainty chosen so that 3sigma is approx half the resolution
if false
  sigma = diagm([2.5, 2.5, 1.0])
  @time loadDEM!(fg, img, (x), (y), meshEdgeSigma=sigma);
end

##

# 2. generate trajectory

μ0 = [-7000;-2000.0;pi/2]
@time generateCanonicalFG_Helix2DSlew!(11, posesperturn=30, radius=1500, dfg=fg, μ0=μ0, graphinit=false, postpose_cb=cb) #, slew_x=1/20)
deleteFactor!(fg, :x0f1)

# ensure specific solve settings
getSolverParams(fg).useMsgLikelihoods = true
getSolverParams(fg).graphinit = false
getSolverParams(fg).treeinit = true

## optional prior at start

mu0 = getPPE(fg, :x0, :simulated).suggested
pr0 = PriorPose2(MvNormal(mu0, 0.01.*[1;1;1;]))
addFactor!(fg, [:x0], pr0)

## for debugging

# getSolverParams(fg).drawtree = true
# getSolverParams(fg).showtree = true

# doautoinit!(fg, :x1)
# initAll!(fg)

# smtasks = Task[]
# tree, _, = solveTree!(fg; smtasks, recordcliqs=ls(fg))
# repeatCSMStep!(hists[3],6)

##

for i in 1:10

smtasks = Task[];
tree = solveTree!(fg; storeOld=true , smtasks );

end

##

# mkdir("/tmp/caesar/results")
# mkdir("/tmp/caesar/results/scalar")
# # starting from 1, solve a total of LAST+STEP-1 number of times, plotting after each STEP number of solves
# STEP, LAST = 5, 41
# for (a,z) in [(i%STEP,i+STEP-1) for i in 1:STEP:LAST]
#   for _ in a:z
#     solveTree!(fg, storeOld=true);
#   end
#   plotSLAM2D(fg, drawContour=false, drawPoints=false, drawEllipse=true) |> PDF("/tmp/caesar/results/scalar/h1500_150_01nh_$(z).pdf",20cm,20cm)
# end

##

plotSLAM2D_KeyAndRef(fg)

##

# function plotSLAM2D_Scalar(fg, img)
pl_ = plotSLAM2D(fg, solveKey=:simulated,
                    x_off=-x_min, y_off=-y_min,
                    scale=100/(x_max-x_min),
                    drawContour=false, drawPoints=false, drawEllipse=false,
                    manualColor="black", drawTriads=false);
#

# IGNORE VIZ OF THIS PLOT SINCE SPY INTERNALLY DOES A TRANSPOSE FROM [i,j]' --> (x,y)
pl_m = Gadfly.spy(img')

##

union!(pl_.layers, pl_m.layers); pl_

##

## redraw z_e level to see if index orders are right

locs = (x->getPPE(fg[x], :simulated).suggested[1:2]).(sortDFG(ls(fg)))
# PartialPriorPassThrough type, with a LevelSetGridNormal
z_es = (x->getFactorType(fg[x]).Z.level).(sortDFG(lsf(fg, tags=[:DEM;])))
@cast locs_[j,i] := locs[j][i]

Gadfly.plot(x=locs_[:,1], y=locs_[:,2], color=z_es)

## check level set
_roi = img .- z_es[50]
# DEPRECATED USE NEW LevelSetGridNormal and HeatmapDensityGrid
# kp, weights = IIF.sampleLevelSetGaussian!(_roi, sigma_e, x, y)

Gadfly.plot(x=kp[1,:],y=kp[2,:], color=weights, Geom.Geom.point)

# imshow(st[3])

##

pl = plotHMSLevel(fg, :x47);

im = convert(Matrix{RGB}, pl);
# io = IOBuffer()
# stre = draw(PNG(io), pl)

# imshow(im)

##

vars = ls(fg) |> sortDFG
st = plotToVideo_Distributed( x->plotHMSLevel(fg, x), vars );

##

sks = listSolveKeys(fg) |> collect |> sortDFG
sks = [setdiff(sks, [:default]); :default]

# PL = plotSLAM2D_KeyAndSim(fg);

##

st = plotToVideo_Distributed( x->plotSLAM2D_KeyAndSim(fg, x), sks);

##

# check: query variables
getPPE(fg, :x0, :simulated).suggested
getPPE(fg, :pt50_50, :simulated).suggested

# check: fetch and plot pose variables

traj = ls(fg, r"x\d+") |> sortDFG
traj_xy = traj .|> x->getPPE(fg, x, :simulated).suggested[1:2]

@cast _xy[i,j] := traj_xy[i][j]

plot(
    layer(x=_xy[:,1],y=_xy[:,2], Geom.point),
    layer(x=_xy[:,1],y=_xy[:,2], Geom.path)
)

# 3. simulate elevation measurements
dem = Interpolations.LinearInterpolation((x,y), img) # interpolated DEM
elevation(p) = dem[getPPE(fg, p, :simulated).suggested[1:2]'...]

poses = ls(fg, r"x\d+") |> sortDFG

# fetch interpolated elevation at ground truth position

for p in poses
    e = elevation(p)

    # add to variable

end

# 4a. add pairwise constraints between poses and points

# 4b. add pairwise constraints between poses (cross-over)

## ================================================================================================
## DEV TESTING BELOW
## ================================================================================================

using ImageView
using UnicodePlots

imshow(img)

## Aliasing sampler on sparse selection of points

@info "DEM value range" minimum(img) maximum(img)

function getLevelSet(data,x, y, level, tol=0.01)
    """
    Get the grid positions at the specified height
    """
    roi = data.-level
    mask = abs.(roi) .< tol

    idx2d = findall(mask)  # 2D indices
    # idx   = i:length(idx)
    (v->[x[v.I[1]],y[v.I[2]]]).(idx2d)
end

# go bigger than measurement noise, to also accomodate heatmap sampling process errors
R_meas = 1e-4

# selection of interest, assume elevation measurement aroun d0.5 is of interest
level = 0.5
img_soi = img .- level
immask = abs.(img_soi) .< 0.01

imshow(immask)

img_sparse = img_soi[immask]
idx = findall(immask)

idx_pos = (v->[x[v[1]],y[v[2]]]).(idx)

x_spacing = x[2]-x[1]
y_spacing = y[2]-y[1]

# imgT = zeros(100,100)
# imgT[10,:] .= 1.0

# imshow(imgT)

# OPTION A
kerBw_ = 0.7*0.5*(x_spacing + y_spacing) # 70%
kerBw = [kerBw_; kerBw_]
@cast kerPos_[i,j] := idx_pos[j][i]
kerPos = collect(kerPos_)
P_pre = kde!(kerPos, kerBw)
pts_preIS = sample(P_pre, 10000)[1]

# draw flipped so consistent with imshow img[i,j] == img[x,y]
imscatter(img_) = scatterplot(img_[2,:], -img_[1,:])
imscatter(pts_preIS)

#
densityplot(pts_preIS[2,:], -pts_preIS[1,:])

# weigth the samples according to interpolated heatmap

# still need heatmap interpolation eval for weighting
# weight = heatmap(pts_preIS)
# R_meas helps control sample process sensitivity
hm = Interpolations.LinearInterpolation((x,y), img .- level ) # interpolated DEM

@cast vec_preIS[j][i] := pts_preIS[i,j]

delev = Vector{Float64}(undef, length(vec_preIS))

function hack(w_, i, u)
    w_[i] = hm(u...)
    nothing
end

##

using ProgressMeter

##

@showprogress 1 "wait what...?" for (i,u) in enumerate(vec_preIS)
  @show i, u
  if 9000 < abs(u[1]) || 9000 < abs(u[2])
    delev[i] = 0.0
    continue
  end
  hack(delev, i, u)
end

##

weights = exp.(-(0.5/R_meas)*(abs.(delev)).^2)
weights ./= sum(weights)

histogram(weights, nbins=20, closed=:left)

# get final kde

# bw = [0.1;0.1]
prior = kde!(pts_preIS, kerBw)

##

# # OPTION B
# # where are the values of interest
# # make a distribution
# img_sparse .^= 2
# img_pdf = exp.(-img_sparse*0.5/R_meas)
# img_pdf ./= sum(img_pdf)
# bss = AliasingScalarSampler([id_idx;]./1.0, img_pdf)
# id_smpls = rand(bss, 10000) .|> Int

# ## how to super sample massive low res grid 2D scalar field

# # binary threshold the gridded heatmap

# # OPTION A
# # P_pre is weighted KDE kernels with fat bandwidth at each grid point (scalar dictates weight of kernel)
# # pts_preIS = sample(P_pre, 10000)
# # weight = heatmap(pts_preIS)

# # OPTION B
# # get first "grid points of interest" from heatmap with aliasing sampler
# # mush those grid points to loose clusters (conv with Guassian)

# # importance weight those loose cluster points against the heatmap-interpolated
# N = 1000
# ## how to build weighted KDE (not full DEM pipeline yet)
# # mock heatmap
# mock = MvNormal([0.0;0.0], [1.0 0.5; 0.5 1.0])
# pts = rand(Mv, N)
# # mock mask
# mask = pts[2,:] .< 0.5

# # importance weighted samples from "heatmap
# weights = ones(N)
# weights[mask] .= 0.01
# weights ./= sum(weights)
# postMushWeightedPts = pts

# bw = [0.1;0.1]
# # this is the KDE belief to build a prior for localizaton from low res map (final step)
# P = kde!(postMushWeightedPts, bw, weights)

##

##

using KernelDensityEstimatePlotting
Gadfly.set_default_plot_size(35cm,25cm)

plotKDE(prior)

#


text:
using FileIO
using Parameters

include("network_sims_gpu.jl")
include("random_matrices.jl")
include("LS_calculations.jl")

dir = "results1"

# Eta values and network seeds
etas = collect(-1.0f0:0.1f0:1.0f0)
J_seeds = 5000:5019

# Slurm array index chooses which parameters to use
ind = parse(Int64, ARGS[1])
eta, J_seed = [(eta, J_seed) for eta in etas, J_seed in J_seeds][ind]

# Other parameters
N = 3000
g = 1.5f0
alpha = 0.0f0 # g-independent autapse scaling param
dt = 0.01f0
t_bound = 2000.0f0
wait = 200.0f0
t_ONS = 1.0f0
h0_seed = 6000
dh_seed = 7000
Q0_seed = 8000

# Generate network topology with autapse scaling g
J = generate_J(N, eta, seed=J_seed)
h0 = generate_nv(N, Float32, seed=h0_seed)
dh = generate_nv(N, Float32, seed=dh_seed)
Q0 = generate_ONB(N, Float32, seed=Q0_seed)

# The actual calculation uses library code
Lspec, max_LE = sim_lyapunov_gpu(J, g, h0, dh, Q0; dt=dt, t_bound=t_bound,
			     t_ONS=t_ONS, wait=wait, return_max_LE=true,
			     alpha=alpha, printstuff=false)

dimensionality = KY_attractor_dim(Lspec)
entropy_rate = KS_entropy_rate(Lspec)

fn = "Lspec_E$(ind)_eta=$(eta)_J_seed=$(J_seed)_g=$(g)_alpha=$(alpha).jld2"
out = Dict{String,Any}("N" => N,
		       "eta" => eta,
		       "J_seed" => J_seed,
		       "g" => g,
		       "alpha" => alpha,
		       "dt" => dt,
		       "t_bound" => t_bound,
		       "wait" => wait,
		       "t_ONS" => t_ONS,
		       "h0_seed" => h0_seed,
		       "dh_seed" => dh_seed,
		       "Q0_seed" => Q0_seed,
		       "Lspec" => Lspec,
		       "max_LE" => max_LE,
		       "dimensionality" => dimensionality,
		       "entropy_rate" => entropy_rate
		       )

save(joinpath(dir, fn), out)


text:
function topmodule(mods)
    function ischild(c, mod)
        ok = false
        pc = parentmodule(c)
        while pc !== c
            pc === Main && return false  # mostly important for passing the tests
            if isdefined(mod, nameof(pc))
                ok = true
                break
            end
            c = pc
        end
        return ok
    end

    mods = collect(mods)
    mod = first(mods)
    for m in Iterators.drop(mods, 1)
        # Easy cases
        if isdefined(mod, nameof(m))
        elseif isdefined(m, nameof(mod))
            mod = m
        else
            # Check parents of each
            if ischild(m, mod)
            elseif ischild(mod, m)
                mod = m
            else
                return nothing
            end
        end
    end
    return mod
end

function addmodules!(mods, parameters)
    for p in parameters
        if isa(p, DataType)
            push!(mods, Base.moduleroot(p.name.module))
            addmodules!(mods, p.parameters)
        end
    end
    return mods
end

function methods_with_generators(m::Module)
    meths = Method[]
    for name in names(m; all=true)
        isdefined(m, name) || continue
        f = getfield(m, name)
        if isa(f, Function)
            for method in methods(f)
                if isdefined(method, :generator)
                    push!(meths, method)
                end
            end
        end
    end
    return meths
end

# Code to look up keyword-function "body methods"
const lookup_kwbody_str = """
const __bodyfunction__ = Dict{Method,Any}()

# Find keyword "body functions" (the function that contains the body
# as written by the developer, called after all missing keyword-arguments
# have been assigned values), in a manner that doesn't depend on
# gensymmed names.
# `mnokw` is the method that gets called when you invoke it without
# supplying any keywords.
function __lookup_kwbody__(mnokw::Method)
    function getsym(arg)
        isa(arg, Symbol) && return arg
        @assert isa(arg, GlobalRef)
        return arg.name
    end

    f = get(__bodyfunction__, mnokw, nothing)
    if f === nothing
        fmod = mnokw.module
        # The lowered code for `mnokw` should look like
        #   %1 = mkw(kwvalues..., #self#, args...)
        #        return %1
        # where `mkw` is the name of the "active" keyword body-function.
        ast = Base.uncompressed_ast(mnokw)
        if isa(ast, Core.CodeInfo) && length(ast.code) >= 2
            callexpr = ast.code[end-1]
            if isa(callexpr, Expr) && callexpr.head == :call
                fsym = callexpr.args[1]
                if isa(fsym, Symbol)
                    f = getfield(fmod, fsym)
                elseif isa(fsym, GlobalRef)
                    if fsym.mod === Core && fsym.name === :_apply
                        f = getfield(mnokw.module, getsym(callexpr.args[2]))
                    elseif fsym.mod === Core && fsym.name === :_apply_iterate
                        f = getfield(mnokw.module, getsym(callexpr.args[3]))
                    else
                        f = getfield(fsym.mod, fsym.name)
                    end
                else
                    f = missing
                end
            else
                f = missing
            end
        else
            f = missing
        end
        __bodyfunction__[mnokw] = f
    end
    return f
end
"""

const lookup_kwbody_ex = Expr(:toplevel)
start = 1
while true
    global start
    ex, start = Meta.parse(lookup_kwbody_str, start)
    if ex !== nothing
        push!(lookup_kwbody_ex.args, ex)
    else
        break
    end
end

function can_eval(mod::Module, str::AbstractString)
    try
        ex = Meta.parse(str)
        Core.eval(mod, ex)
    catch
        return false
    end
    return true
end

tupletypestring(params) = "Tuple{" * join(params, ',') * '}'
tupletypestring(fstr::AbstractString, params::AbstractVector{<:AbstractString}) =
    tupletypestring([fstr; params])

tuplestring(params) = isempty(params) ? "()" : '(' * join(params, ',') * ",)"

wrap_precompile(ttstr::AbstractString) = "precompile(" * ttstr * ')'

function add_if_evals!(pclist, mod::Module, fstr, params, tt; prefix = "")
    ttstr = tupletypestring(fstr, params)
    if can_eval(mod, ttstr)
        push!(pclist, prefix*wrap_precompile(ttstr))
    else
        @debug "Module $mod: skipping $tt due to eval failure"
    end
    return pclist
end

function reprcontext(mod::Module, @nospecialize(T::Type))
    # First try without the full module context
    rplain = repr(T)
    try
        ex = Meta.parse(rplain)
        Core.eval(mod, ex)
        return rplain
    catch
        # Add full module context
        return repr(T; context=:module=>nothing)
    end
end

function handle_kwbody(topmod::Module, m::Method, paramrepr, tt, fstr="fbody")
    nameparent = Symbol(match(r"^#([^#]*)#", String(m.name)).captures[1])
    if !isdefined(m.module, nameparent)   # TODO: replace debugging with error-handling
        @show m m.name
    end
    fparent = getfield(m.module, nameparent)
    pttstr = tuplestring(paramrepr[m.nkw+2:end])
    whichstr = "which($(repr(fparent)), $pttstr)"
    if can_eval(topmod, whichstr)
        ttstr = tuplestring(paramrepr)
        pcstr = """
        let fbody = try __lookup_kwbody__($whichstr) catch missing end
                if !ismissing(fbody)
                    precompile($fstr, $ttstr)
                end
            end"""  # extra indentation because `write` will indent 1st line
        if can_eval(topmod, pcstr)
            return pcstr
        else
            @debug "Module $topmod: skipping $tt due to kwbody lookup failure"
        end
    else
        @debug "Module $topmod: skipping $tt due to kwbody caller lookup failure"
    end
    return nothing
end

function parcel(tinf::AbstractVector{Tuple{Float64,Core.MethodInstance}}; subst=Vector{Pair{String, String}}(), blacklist=String[])
    pc = Dict{Symbol, Vector{String}}()      # output
    modgens = Dict{Module, Vector{Method}}() # methods with generators in a module
    mods = OrderedSet{Module}()                     # module of each parameter for a given method
    for (t, mi) in reverse(tinf)
        isdefined(mi, :specTypes) || continue
        tt = mi.specTypes
        m = mi.def
        isa(m, Method) || continue
        # Determine which module to assign this method to. All the types in the arguments
        # need to be defined; we collect all the corresponding modules and assign it to the
        # "topmost".
        empty!(mods)
        push!(mods, Base.moduleroot(m.module))
        addmodules!(mods, tt.parameters)
        topmod = topmodule(mods)
        if topmod === nothing
            @debug "Skipping $tt due to lack of a suitable top module"
            continue
        end
        # If we haven't yet started the list for this module, initialize
        topmodname = nameof(topmod)
        if !haskey(pc, topmodname)
            pc[topmodname] = String[]
            # For testing our precompile directives, we might need to have lookup available
            if VERSION >= v"1.4.0-DEV.215" && topmod !== Core && !isdefined(topmod, :__bodyfunction__)
                Core.eval(topmod, lookup_kwbody_ex)
            end
        end
        # Create the string representation of the signature
        # Use special care with keyword functions, anonymous functions
        p = tt.parameters[1]   # the portion of the signature related to the function itself
        paramrepr = map(T->reprcontext(topmod, T), Iterators.drop(tt.parameters, 1))  # all the rest of the args

        # blacklist remover
        pc[topmodname] = blacklist_remover!(pc[topmodname], blacklist)

        if any(str->occursin('#', str), paramrepr)
            @debug "Skipping $tt due to argument types having anonymous bindings"
            continue
        end
        mname, mmod = String(p.name.name), m.module   # m.name strips the kw identifier
        mkw = match(kwrex, mname)
        mkwbody = match(kwbodyrex, mname)
        isgen = match(genrex, mname) !== nothing
        isanon = match(anonrex, mname) !== nothing || match(innerrex, mname) !== nothing
        isgen && (mkwbody = nothing)
        if VERSION < v"1.4.0-DEV.215"  # before this version, we can't robustly look up kwbody callers (missing `nkw`)
            isanon |= mkwbody !== nothing  # treat kwbody methods the same way we treat anonymous functions
            mkwbody = nothing
        end
        if mkw !== nothing
            # Keyword function
            fname = mkw.captures[1] === nothing ? mkw.captures[2] : mkw.captures[1]
            fkw = "Core.kwftype(typeof($mmod.$fname))"
            add_if_evals!(pc[topmodname], topmod, fkw, paramrepr, tt)
        elseif mkwbody !== nothing
            ret = handle_kwbody(topmod, m, paramrepr, tt)
            if ret !== nothing
                push!(pc[topmodname], ret)
            end
        elseif isgen
            # Generator for a @generated function
            if !haskey(modgens, m.module)
                callers = modgens[m.module] = methods_with_generators(m.module)
            else
                callers = modgens[m.module]
            end
            for caller in callers
                if nameof(caller.generator.gen) == m.name
                    # determine whether the generator is being called from a kwbody method
                    sig = Base.unwrap_unionall(caller.sig)
                    cname, cmod = String(sig.parameters[1].name.name), caller.module
                    cparamrepr = map(repr, Iterators.drop(sig.parameters, 1))
                    csigstr = tuplestring(cparamrepr)
                    mkwc = match(kwbodyrex, cname)
                    if mkwc === nothing
                        getgen = "typeof(which($cmod.$(caller.name),$csigstr).generator.gen)"
                        add_if_evals!(pc[topmodname], topmod, getgen, paramrepr, tt)
                    else
                        if VERSION >= v"1.4.0-DEV.215"
                            getgen = "which(Core.kwfunc($cmod.$(mkwc.captures[1])),$csigstr).generator.gen"
                            ret = handle_kwbody(topmod, caller, cparamrepr, tt) #, getgen)
                            if ret !== nothing
                                push!(pc[topmodname], ret)
                            end
                        else
                            # Bail and treat as if anonymous
                            prefix = "isdefined($mmod, Symbol(\"$mname\")) && "
                            fstr = "getfield($mmod, Symbol(\"$mname\"))"  # this is universal, var is Julia 1.3+
                            add_if_evals!(pc[topmodname], topmod, fstr, paramrepr, tt; prefix=prefix)
                        end
                    end
                    break
                end
            end
        elseif isanon
            # Anonymous function, wrap in an `isdefined`
            prefix = "isdefined($mmod, Symbol(\"$mname\")) && "
            fstr = "getfield($mmod, Symbol(\"$mname\"))"  # this is universal, var is Julia 1.3+
            add_if_evals!(pc[topmodname], topmod, fstr, paramrepr, tt; prefix=prefix)
        else
            add_if_evals!(pc[topmodname], topmod, reprcontext(topmod, p), paramrepr, tt)
        end
    end
    return pc
end

"""
Search and removes blacklist from pcI

# Examples
```julia
blacklist = ["hi","bye"]
pcI = ["good","bad","hi","bye","no"]

SnoopCompile.blacklist_remover!(pcI, blacklist)
```
"""
function blacklist_remover!(pcI, blacklist)
    idx = Int[]
    for (iLine, line) in enumerate(pcI)
        if any(occursin.(blacklist, line))
            push!(idx, iLine)
        end
    end
    deleteat!(pcI, idx)
    return pcI
end


text:
using DataStructures

"""
vl_graph_plot(
    df_nodes,
    df_edges,
    node_label       = true,
    node_labelsize   = 10,
    tooltip          = true,
    node_size        = 500,
    node_color       = "#9ecae9",
    node_sizefield   = nothing,
    node_colorfield  = nothing,
    node_colorscheme = "blues",
    node_opacity     = 1.0,
    edge_opacity     = 0.5,
    edge_weightfield = nothing,
    width            = 600,
    height           = 400
    )
Contains the VegaLite specification for producing the plot.
"""
function vl_graph_plot(
    graph_nodes,
    graph_edges;
    ew               = false,
    node_label       = true,
    node_labelsize   = 10,
    tooltip          = true,
    node_size        = 500,
    node_color       = "#9ecae9",
    node_sizefield   = nothing,
    node_colorfield  = nothing,
    node_sizefieldtype   = "q",
    node_colorfieldtype  = "q",
    node_colorscheme = "blues",
    node_opacity     = 1.0,
    edge_opacity     = 0.5,
    edge_weightfield = nothing,
    edge_weightfieldtype = "o",
    width            = 600,
    height           = 400
    )

  # Adjusting for extra fields
  if node_sizefield  != nothing
        graph_nodes[!,:node_sizefield] = node_sizefield
        node_sizefield = :node_sizefield
  end
  if node_colorfield  != nothing
        graph_nodes[!,:node_colorfield] = node_colorfield
        node_colorfield = :node_colorfield
  end

  if edge_weightfield  != nothing
        ew = true
        graph_edges[!,:ew] = edge_weightfield
        edge_weightfield = :edge_weightfield

  end

  # vegalite.jl plotting specification
  # v1 - plotting nodes
    v1 =@vlplot(
    data = graph_nodes,
    layer=[
    {
        mark={"type"=:circle,clip=false,
              opacity=node_opacity,
              size=node_size,
              color=node_color},
        # There is some bug with VegaLite
        # when trying to put [] aroung tooltip
        #  tooltip={field="keywords","type"="nominal"},
        x={"node_x:q",axis=nothing},
        y={"node_y:q",axis=nothing},
        size={field=node_sizefield,
              type=node_sizefieldtype,
              scale={rangeMin=100},
              legend=nothing},
        color={field=node_colorfield,
               type=node_colorfieldtype,
               scale={scheme=node_colorscheme}},
        width=width,
        height=height,
        selection={
            grid={
                    type=:interval,
                    bind=:scales,
                    }}
        },
    {
        mark={"type"=:text,clip=false,opacity=1,size=node_labelsize},
        x={"node_x:q",axis=nothing},
        y={"node_y:q",axis=nothing},
        text={"keywords","type"="nominal"}
    }]
    )

  # v2 - plotting edges
    v2 =@vlplot(
        data = graph_edges,
        layer=[
        {
          mark={"type"=:line,color="gray",clip=false,size=0.8,opacity=edge_opacity},
          encoding={
          x={"edges_x:q",axis=nothing},
          y={"edges_y:q",axis=nothing},
          detail={"pairs:o"}},
          width=width,
          height=height
        }
              ]
    );

    # Adjusting fields to avoid warnings when plotting
    if tooltip == true
      v1.layer[1]["encoding"]["tooltip"] = OrderedDict[OrderedDict("field"=>"keywords","type"=>"nominal")]
    end

    if ew == true
      v2.layer[1]["encoding"]["size"] = OrderedDict("field"=>"ew","type"=>edge_weightfieldtype,"legend"=>nothing)
    end

    if node_label == false
      deleteat!(v1.layer,2)
    end

    if node_sizefield  == nothing
        delete!(v1.layer[1]["encoding"],"size")
    end
    if node_colorfield == nothing
        delete!(v1.layer[1]["encoding"],"color")
    end

    graph_plot = @vlplot(view={stroke=nothing})+v2+v1
    return graph_plot
end


text:
struct BackSum{I<:InducedMap}
    im::I
end

struct BackSumEval{I<:InducedMap,Q}
    S::BackSum{I}
    q::Q
    domainonly::Bool
end
(S::BackSum)(Q, domainonly::Bool=false) = BackSumEval(S, Q, domainonly)

(SQ::BackSumEval)(hx::HofbauerPoint{T}; maxdepth=1000, dvmin=eps(T)) where T =
    backsumrecursion(SQ, hx, one(T), 0, maxdepth, dvmin)
(SQ::BackSumEval)(x; kwargs...) =
    SQ(HofbauerPoint(x,SQ.S.im.r_ind); kwargs...)

function backsumrecursion(SQ::BackSumEval, x::HofbauerPoint{T}, dv, n, depthleft, dvmin) where T
    im = SQ.S.im
    backsum = zero(T)
    # TODO: accomodate NeutralBranch
    for e in bedge(im.he, x.graphind)
        (nx, dvprod) = mapinvP(im.he, e, x)
        ndv = abs(dv)*dvprod # check this is OK for neutral branches
        is_d_ind = nx.graphind == im.d_ind
        (~SQ.domainonly || is_d_ind) && (backsum += SQ.q(nx.x,ndv,n))
        abs(ndv) ≥ dvmin && nx.graphind != im.r_ind && ~is_d_ind && depthleft > 0 &&
            (backsum += backsumrecursion(SQ, nx, ndv, n+1, depthleft-1, dvmin))
    end
    backsum
end


text:
"""
    fit_tensors(directions, adcs)

Fit effective diffusion tensors to directionalized ADCs.
The six components of the symmetric diffusion tensors are fitted by least
squares to the gradient directions and resulting ADCs.
"""
function fit_tensors(directions, adcs)
    T = eltype(directions)

    adc = reduce(hcat, adcs)
    ncompartment, ndirection = size(adc)

    # Get vectors of gradient interactions
    d = directions
    G = zeros(ndirection, 6)
    G[:, 1] = d[1, :] .^ 2
    G[:, 2] = d[2, :] .^ 2
    G[:, 3] = d[3, :] .^ 2
    G[:, 4] = 2 .* d[1, :] .* d[2, :]
    G[:, 5] = 2 .* d[1, :] .* d[3, :]
    G[:, 6] = 2 .* d[2, :] .* d[3, :]

    tensors = Vector{SMatrix{3,3,T,9}}(undef, ncompartment)
    for icmpt = 1:ncompartment
        # Deduce vector of effective diffusion tensor components usinag a least squares fit
        # of the directions to the computed ADCs
        Dvec = G \ adc[icmpt, :]

        # Tensorize component vectors (symmetric)
        tensors[icmpt] = SMatrix{3,3}(
            [
                Dvec[1, :] Dvec[4, :] Dvec[5, :]
                Dvec[4, :] Dvec[2, :] Dvec[6, :]
                Dvec[5, :] Dvec[6, :] Dvec[3, :]
            ],
        )

    end

    tensors
end


text:
struct PDESystem <: ModelingToolkit.AbstractSystem
  eqs
  bcs
  domain
  indvars
  depvars
  ps
  default_p
  @add_kwonly function PDESystem(eqs, bcs, domain, indvars, depvars, ps = SciMLBase.NullParameters(), default_p = nothing)
      new(eqs, bcs, domain, indvars, depvars, ps, default_p)
  end
end

Base.getproperty(x::PDESystem, sym::Symbol) = getfield(x, sym)

Base.summary(prob::PDESystem) = string(nameof(typeof(prob)))
function Base.show(io::IO, sys::PDESystem)
  println(io,summary(sys))
  println(io,"Equations: ", sys.eqs)
  println(io,"Boundary Conditions: ", sys.bcs)
  println(io,"Domain: ", sys.domain)
  println(io,"Dependent Variables: ", sys.depvars)
  println(io,"Independent Variables: ", sys.indvars)
  println(io,"Parameters: ", sys.ps)
  println(io,"Default Parameter Values", sys.default_p)
  return nothing
end


text:
# This file is part of IntegerSequences.
# Copyright Peter Luschny. License is MIT.

(@__DIR__) ∉ LOAD_PATH && push!(LOAD_PATH, (@__DIR__))

module OrderedSetPartitionsTypeM

using Nemo, Polynomials, GeneralizedBinomial, DataStructures, Products
using Setpartitions, IntPartitions

export ModuleOrderedSetPartitionsTypeM, OrderedSetPolynomials, OrderedSetPartitions
export ShapePartitions, OrderedShapePartitions, CardinalityOfShapePartitions
export P097805, P131689, P241171, P278073, P278074
export L278073, L278074, L241171, L088218, T131689

# by shape

# by length
export L131689, T241171, T278073, T278074
# diagonal
#
# row sum
export L094088, L243664, L243665
# alt row sum
export L028296, L002115, L211212
# central
export L210029, L281478, L281479, L281480

## Ordered set partitions of m-type
"""

| type | m = 0 | m = 1 | m = 2 | m = 3 | m = 4 |
|------|-------|-------|-------|-------|-------|
| by shape | [A178803](https://oeis.org/A178803) | [A133314](https://oeis.org/A133314) | [A327022](https://oeis.org/A327022) | [A327023](https://oeis.org/A327023) | [A327024](https://oeis.org/A327024) |
| by length | [A318144](https://oeis.org/A318144) | [A131689](https://oeis.org/A131689) | [A241171](https://oeis.org/A241171) | [A278073](https://oeis.org/A278073) | [A278074](https://oeis.org/A278074) |
| diagonal | [A000142](https://oeis.org/A000142) | [A000142](https://oeis.org/A000142) | [A000680](https://oeis.org/A000680) | [A014606](https://oeis.org/A014606) | [A014608](https://oeis.org/A014608) |
| row sum | [A101880](https://oeis.org/A101880) | [A000670](https://oeis.org/A000670) | [A094088](https://oeis.org/A094088) | [A243664](https://oeis.org/A243664) | [A243665](https://oeis.org/A243665) |
| alt row sum | [A260845](https://oeis.org/A260845) | [A033999](https://oeis.org/A033999) | [A028296](https://oeis.org/A028296) | [A002115](https://oeis.org/A002115) | [A211212](https://oeis.org/A211212) |
| central | [A053529](https://oeis.org/A053529) | [A210029](https://oeis.org/A210029) | [A281478](https://oeis.org/A281478) | [A281479](https://oeis.org/A281479) | [A281480](https://oeis.org/A281480) |
"""
const ModuleOrderedSetPartitionsTypeM = ""

const CacheP = Dict{Tuple{Int,Int}, fmpz_poly}()
"""

Return the polynomial where the coefficients are the number of ordered set partitions of an n-set with shape type m.
"""
function OrderedSetPolynomials(m::Int, n::Int)
    global CacheP
    R, x = PolynomialRing(ZZ, "x")
    function recP(m, n)
        n == 0 && return R(1)
        haskey(CacheP, (m, n)) && return CacheP[(m, n)]
        p = sum(binomial(ZZ(m * n), ZZ(m * k)) * recP(m, n - k) * x for k ∈ 1:n)
        CacheP[(m, n)] = p
        sum(binomial(ZZ(m * n), ZZ(m * k)) * recP(m, n - k) * x for k ∈ 1:n)
    end
    recP(m, n)
end

"""

Return the number of ordered set partitions of an n-set with shape type m.
"""
OrderedSetPartitions(m::Int, n::Int) = Coeffs(OrderedSetPolynomials(m, n))

"""

Return the polynomial where the coefficients are the number of the ordered set partitions of an n-set with shape type 0.
# Examples
```julia-repl
julia> P097805(3)
fmpz_poly
x^3 + 2*x^2 + x
```
"""
P097805(n) = OrderedSetPolynomials(0, n)

"""

Return the polynomial where the coefficients are the number of the ordered set partitions of an n-set which are of shape type 1.
# Examples
```julia-repl
julia> P131689(3)
6*x^3 + 6*x^2 + x
```
"""
P131689(n) = OrderedSetPolynomials(1, n)

"""

Return the number of ordered set partitions of an n-set which are of shape type 1.
# Examples
```julia-repl
julia> L131689(3)
4-element Array{fmpz,1}:
[0, 1, 6, 6]
```
"""
L131689(n) = Coeffs(OrderedSetPolynomials(1, n))

"""

Return ``len`` rows of the triangle of ordered set partitions of n.
# Examples
```julia-repl
julia> T131689(3, 5)
5-element Array{Array{fmpz,1},1}:
[1]
[0, 1]
[0, 1, 2]
[0, 1, 6, 6]
[0, 1, 14, 36, 24]
```
"""
T131689(n, len) = Coeffs(n -> OrderedSetPolynomials(1, n), len)

"""

Return the n-th ordered set polynomials of shape type 2.
# Examples
```julia-repl
julia> P131689(3)
90*x^3 + 30*x^2 + x
```
"""
P241171(n) = OrderedSetPolynomials(2, n)

"""

Return the number of ordered set partitions of a 2n set into even sized blocks (as a list).
# Examples
```julia-repl
julia> L241171(3)
4-element Array{fmpz,1}:
[0, 1, 30, 90]
```
"""
L241171(n) = Coeffs(OrderedSetPolynomials(2, n))

"""

Return ``len`` rows of the triangle ordered set partitions of a 2n set into even sized blocks.
# Examples
```julia-repl
julia> T241171(3, 5)
5-element Array{Array{fmpz,1},1}:
[1]
[0, 1]
[0, 1, 6]
[0, 1, 30, 90]
[0, 1, 126, 1260, 2520]
```
"""
T241171(n, len) = Coeffs(n -> OrderedSetPolynomials(2, n), len)

"""

Return the n-th ordered set polynomials of shape type 3.
# Examples
```julia-repl
julia> P278073(3)
1680*x^3 + 168*x^2 + x
```
"""
P278073(n) = OrderedSetPolynomials(3, n)

"""

Return the number of ordered set partitions of a 3n set into 3-sized blocks (as a list).
# Examples
```julia-repl
julia> L278073(3)
4-element Array{fmpz,1}:
[0, 1, 168, 1680]
```
"""
L278073(n) = Coeffs(OrderedSetPolynomials(3, n))

"""

Return ``len`` rows of the triangle ordered set partitions of a 3n set into 3-sized blocks.
# Examples
```julia-repl
julia> T278073(3, 5)
5-element Array{Array{fmpz,1},1}:
[1]
[0, 1]
[0, 1, 20]
[0, 1, 168, 1680]
[0, 1, 1364, 55440, 369600]
```
"""
T278073(n, len) = Coeffs(n -> OrderedSetPolynomials(3, n), len)

"""

Return the n-th ordered set polynomials of shape type 4.
# Examples
```julia-repl
julia> P278074(3)
34650*x^3 + 990*x^2 + x
```
"""
P278074(n) = OrderedSetPolynomials(4, n)

"""

Return the number of ordered set partitions of a 4n set into 4-sized blocks (as a list).
# Examples
```julia-repl
julia> L278074(3)
4-element Array{fmpz,1}:
[0, 1, 990, 34650]
```
"""
L278074(n) = Coeffs(OrderedSetPolynomials(4, n))

"""

Return ``len`` rows of the triangle ordered set partitions of a 4n set into 4-sized blocks.
# Examples
```julia-repl
julia> T278074(3, 5)
5-element Array{Array{fmpz,1},1}:
[1]
[0, 1]
[0, 1, 70]
[0, 1, 990, 34650]
[0, 1, 16510, 2702700, 63063000]
```
"""
T278074(n, len) = Coeffs(n -> OrderedSetPolynomials(4, n), len)

"""

Return the central column of the triangle of the ordered set partitions of shape type 0.
# Examples
```julia-repl
julia> L088218(6)
6-element Array{fmpz,1}:
[1, 1, 3, 10, 35, 126]
```
"""
L088218(len) = Central(n -> OrderedSetPolynomials(0, n), len)
"""

Return the central column of the triangle of the ordered set partitions of shape type 1.
# Examples
```julia-repl
julia> L210029(6)
6-element Array{fmpz,1}:
[1, 1, 14, 540, 40824, 5103000]
```
"""
L210029(len) = Central(n -> OrderedSetPolynomials(1, n), len)

"""

Return the central column of the triangle of the ordered set partitions of shape type 2.
# Examples
```julia-repl
julia> L281478(6)
6-element Array{fmpz,1}:
[1, 1, 126, 126720, 494053560, 5283068427000]
```
"""
L281478(len) = Central(n -> OrderedSetPolynomials(2, n), len)

"""

Return the central column of the triangle of the ordered set partitions of shape type 3.
# Examples
```julia-repl
julia> L281479(6)
6-element Array{fmpz,1}:
[1, 1, 1364, 42771456, 10298900437056, 11287986820196486400]
```
"""
L281479(len) = Central(n -> OrderedSetPolynomials(3, n), len)
"""

Return the central column of the triangle of the ordered set partitions of shape type 4.
# Examples
```julia-repl
julia> L281480(5)
5-element Array{fmpz,1}:
[1, 1, 16510, 17651304000, 286988816206755000]
```
"""
L281480(len) = Central(n -> OrderedSetPolynomials(4, n), len)

"""

Return the number of the ordered set partitions of shape type 2.
# Examples
```julia-repl
julia> L094088(5)
5-element Array{fmpz,1}:
[1, 1, 7, 121, 3907, 202741]
```
"""
L094088(len) = CoeffSum(n -> OrderedSetPolynomials(2, n), len)

"""

Return the number of the ordered set partitions of shape type 3.
# Examples
```julia-repl
julia> L243664(5)
5-element Array{fmpz,1}:
[1, 1, 21, 1849, 426405]
```
"""
L243664(len) = CoeffSum(n -> OrderedSetPolynomials(3, n), len)
"""

Return the number of the ordered set partitions of shape type 4.
# Examples
```julia-repl
julia> L243665(5)
5-element Array{fmpz,1}:
[1, 1, 71, 35641, 65782211]
```
"""
L243665(len) = CoeffSum(n -> OrderedSetPolynomials(4, n), len)

"""

Return the alternating row sums of the ordered set polynomials of shape type 2.
# Examples
```julia-repl
julia> L028296(7)
7-element Array{fmpz,1}:
[1, -1, 5, -61, 1385, -50521, 2702765]
```
"""
L028296(len) = CoeffAltSum(n -> OrderedSetPolynomials(2, n), len)

"""

Return the alternating row sums of the ordered set polynomials of shape type 3.
# Examples
```julia-repl
julia> L002115(7)
7-element Array{fmpz,1}:
[1, -1, 19, -1513, 315523, -136085041, 105261234643]
```
"""
L002115(len) = CoeffAltSum(n -> OrderedSetPolynomials(3, n), len)

"""

Return the alternating row sums of the ordered set polynomials of shape type 4.
# Examples
```julia-repl
julia> L211212(6)
6-element Array{fmpz,1}:
[1, -1, 69, -33661, 60376809, -288294050521]
```
"""
L211212(len) = CoeffAltSum(n -> OrderedSetPolynomials(4, n), len)

# ------------------ shapes ----------------------------------

"""

Return the number of set partitions with a given shape.
# Examples
```julia-repl
julia> CardinalityOfShapePartitions([3, 3, 3, 2, 2])
600600
```
"""
function CardinalityOfShapePartitions(Shape)
    Shape == [] && return fmpz(1)
    M = Multinomial(Shape)
    R = values(counter(Shape))
    P =  ∏([F!(s) for s ∈ R])
    div(M, P)
end

"""

--
"""
function CardinalityOfOrderedShapePartitions(Shape)
    Shape == [] && return fmpz(1)
    M = Multinomial(Shape)*F!(length(Shape))
    R = values(counter(Shape))
    P = ∏([F!(s) for s ∈ R])
    div(M, P)
end

"""

--
"""
function ShapePartitions(m, n, k)
    shapes = (map(x -> x*m, p) for p ∈ IntegerPartitions(n, k))
    isempty(shapes) && return [fmpz(0)]
    return [CardinalityOfShapePartitions(s) for s ∈ shapes]
end

"""

--
"""
function ShapePartitions(m, n)
    reduce(vcat, [ShapePartitions(m, n, k) for k ∈ 0:n])
end

"""

--
"""
function OrderedShapePartitions(m, n, k)
    shapes = (map(x -> x*m, p) for p ∈ IntegerPartitions(n, k))
    isempty(shapes) && return [fmpz(0)]
    return [CardinalityOfOrderedShapePartitions(s) for s ∈ shapes]
end

"""

--
"""
function OrderedShapePartitions(m, n)
    reduce(vcat, [OrderedShapePartitions(m, n, k) for k ∈ 0:n])
end

#"""
# Examples
#```julia-repl
#for n in 0:5 OrderedShapePartitions(0, n) |> Println end
#[1]
#[0, 1]
#[0, 1, 1]
#[0, 1, 1, 1]
#[0, 1, 1, 1, 1, 1]
#[0, 1, 1, 1, 1, 1, 1, 1]
#```
#"""
# ERROR A123110(n) = OrderedShapePartitions(0, n)

"""

Return the number of set partitions of n with block sizes given by the k-th integer partition of n when the integer partitions are sorted in Hindenburg order. This is row n of the triangle of multinomial coefficients.
# Examples
```julia-repl
for n in 0:5 OrderedShapePartitions(1, n) |> Println end
[1]
[0, 1]
[0, 1, 2]
[0, 1, 6, 6]
[0, 1, 8, 6, 36, 24]
[0, 1, 10, 20, 60, 90, 240, 120]
```
"""
L049019(n) = OrderedShapePartitions(1, n)

"""

Return the number of set partitions of 2n with block sizes given by the even parts of integer partition of 2n when the integer partitions are sorted in Hindenburg order.
# Examples
```julia-repl
for n in 0:5 OrderedShapePartitions(2, n) |> Println end
[1]
[0, 1]
[0, 1, 6]
[0, 1, 30, 90]
[0, 1, 56, 70, 1260, 2520]
[0, 1, 90, 420, 3780, 9450, 75600, 113400]
```
"""
L327022(n) = OrderedShapePartitions(2, n)

"""

Return the number of set partitions of 3n with block sizes given by the parts of integer partition of 3n which are multiples of 3 and when the integer partitions are sorted in Hindenburg order.
# Examples
```julia-repl
for n in 0:5 OrderedShapePartitions(3, n) |> Println end
[1]
[0, 1]
[0, 1, 20]
[0, 1, 168, 1680]
[0, 1, 440, 924, 55440, 369600]
[0, 1, 910, 10010, 300300, 1261260, 33633600, 168168000]
```
"""
L327023(n) = OrderedShapePartitions(3, n)

"""

Return the number of set partitions of 4n with block sizes given by the parts of integer partition of 4n which are multiples of 4 and when the integer partitions are sorted in Hindenburg order.
# Examples
```julia-repl
for n in 0:5 OrderedShapePartitions(4, n) |> Println end
8-element Array{fmpz,1}:
[1]
[0, 1]
[0, 1, 70]
[0, 1, 990, 34650]
[0, 1, 3640, 12870, 2702700, 63063000]
[0, 1, 9690, 251940, 26453700, 187065450, 17459442000, 305540235000]
```
"""
L327024(n) = OrderedShapePartitions(4, n)

#START-TEST-########################################################

using Test, SeqTests, SeqUtils

function test()

    @testset "OrdSetPolyMType" begin
        @test string(P097805(3)) == "x^3+2*x^2+x"

        @test string(P131689(4)) == "24*x^4+36*x^3+14*x^2+x"
        @test L131689(4) == [0, 1, 14, 36, 24]
        @test T131689(4, 5) == [[1], [0, 1], [0, 1, 2], [0, 1, 6, 6], [0, 1, 14, 36, 24]]

        @test string(P241171(4)) == "2520*x^4+1260*x^3+126*x^2+x"
        @test L241171(4) == [0, 1, 126, 1260, 2520]
        @test T241171(4, 4) == [[1], [0, 1], [0, 1, 6], [0, 1, 30, 90]]

        @test string(P278073(5)) == "168168000*x^5+33633600*x^4+1561560*x^3+10920*x^2+x"
        @test L278073(5) == [0, 1, 10920, 1561560, 33633600, 168168000]
        @test T278073(5, 4) == [[1], [0, 1], [0, 1, 20], [0, 1, 168, 1680]]

        @test string(P278074(4)) == "63063000*x^4+2702700*x^3+16510*x^2+x"
        @test L278074(4) == [0, 1, 16510, 2702700, 63063000]
        @test T278074(4, 4) == [[1], [0, 1], [0, 1, 70], [0, 1, 990, 34650]]

        @test L088218(7) == [1, 1, 3, 10, 35, 126, 462]
        @test L210029(7) == [1, 1, 14, 540, 40824, 5103000, 953029440]
        @test L281478(6) == [1, 1, 126, 126720, 494053560, 5283068427000]
        @test L281479(5) == [1, 1, 1364, 42771456, 10298900437056]
        @test L281480(5) == [1, 1, 16510, 17651304000, 286988816206755000]

        @test L094088(7) == [1, 1, 7, 121, 3907, 202741, 15430207]
        @test L243664(7) == [1, 1, 21, 1849, 426405, 203374081, 173959321557]
        @test L243665(6) == [1, 1, 71, 35641, 65782211, 323213457781]

        @test L028296(7) == [1, -1, 5, -61, 1385, -50521, 2702765]
        @test L002115(7) == [1, -1, 19, -1513, 315523, -136085041, 105261234643]
        @test L211212(6) == [1, -1, 69, -33661, 60376809, -288294050521]
    end

    @testset "SetPartMType" begin

    end

    @testset "SetPartByShape" begin
        @test ShapePartitions(3, 4) == [0, 1, 220, 462, 9240, 15400]
    end
end

function demo()
    for m ∈ 1:4, n ∈ 0:6
        Coeffs(OrderedSetPolynomials(m, n)) |> Println
    end
    for m ∈ 1:4, n ∈ 0:6
        EgfCoeffs(OrderedSetPolynomials(m, n)) |> Println
    end

    CardinalityOfShapePartitions([3, 3, 3, 2, 2]) |> println
    for n ∈ 0:4
        [ShapePartitions(3, n, k) for k ∈ 0:n] |> println
    end

    for m ∈ 0:4
        println("\nOrderedSetPartitions m = $m")
        for n ∈ 0:6
            OrderedSetPartitions(m, n) |> Println
        end
    end

    for m ∈ 0:4
        println("\nOrderedShapePartitions m = $m")
        for n ∈ 0:6
            OrderedShapePartitions(m, n) |> Println
        end
    end
end

function perf() end

function main()
    test()
    demo()
    perf()
end

main()

end # module

#=
"""

Return the number of ordered set partitions of an ``n``-set which are of shape type ``0``.
# Examples
```julia-repl
julia> L097805(3)
4-element Array{fmpz,1}:
[0, 1, 2, 1]
```
"""
L097805(n) = Coeffs(OrderedSetPolynomials(0, n))

"""

Return the first ``len`` rows of the triangle of compositions of ``n``.
# Examples
```julia-repl
julia> T097805(3, 5)
5-element Array{Array{fmpz,1},1}:
[1]
[0, 1]
[0, 1, 1]
[0, 1, 2, 1]
[0, 1, 3, 3, 1]
```
"""
T097805(n, len) = Coeffs(n -> OrderedSetPolynomials(0, n), len)

=#

#=

('--', 0, '---------------------')

by Shape    Amissing

[0] [1]
[1] [0, 1]
[2] [0, 1, 1]
[3] [0, 1, 1, 1]
[4] [0, 1, 1, 1, 1, 1]
[5] [0, 1, 1, 1, 1, 1, 1, 1]
[6] [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

[1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1]

by Length   A008284
[0] [1]
[1] [0, 1]
[2] [0, 1, 1]
[3] [0, 1, 1, 1]
[4] [0, 1, 2, 1, 1]
[5] [0, 1, 2, 2, 1, 1]
[6] [0, 1, 3, 3, 2, 1, 1]

[1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 0, 1, 2, 2, 1, 1]

RowSum      A000041 [1, 1, 2, 3, 5, 7, 11]
AlterRowSum A081362 [1, -1, 0, -1, 1, -1, 1]
Diagonal    A000012 [1, 1, 1, 1, 1, 1, 1]
Central     A000041 [1, 1, 2, 3, 5, 7]

('--', 1, '---------------------')

by Shape    A036040
[0] [1]
[1] [1]
[2] [1, 1]
[3] [1, 3, 1]
[4] [1, 4, 3, 6, 1]
[5] [1, 5, 10, 10, 15, 10, 1]
[6] [1, 6, 15, 10, 15, 60, 15, 20, 45, 15, 1]

[1, 1, 1, 1, 1, 3, 1, 1, 4, 3, 6, 1]

by Length   A048993
[0] [1]
[1] [0, 1]
[2] [0, 1, 1]
[3] [0, 1, 3, 1]
[4] [0, 1, 7, 6, 1]
[5] [0, 1, 15, 25, 10, 1]
[6] [0, 1, 31, 90, 65, 15, 1]

by Recur/k!
[0] [1]
[1] [0, 1]
[2] [0, 1, 1]
[3] [0, 1, 3, 1]
[4] [0, 1, 7, 6, 1]
[5] [0, 1, 15, 25, 10, 1]
[6] [0, 1, 31, 90, 65, 15, 1]

by Recurrence
[0] [1]
[1] [0, 1]
[2] [0, 1, 2]
[3] [0, 1, 6, 6]
[4] [0, 1, 14, 36, 24]
[5] [0, 1, 30, 150, 240, 120]
[6] [0, 1, 62, 540, 1560, 1800, 720]

[1, 0, 1, 0, 1, 1, 0, 1, 3, 1, 0, 1, 7, 6, 1, 0, 1, 15, 25, 10, 1]

RowSum      A000110 [1, 1, 2, 5, 15, 52, 203]
AlterRowSum A000587 [1, -1, 0, 1, 1, -2, -9]
Diagonal    A000012 [1, 1, 1, 1, 1, 1, 1]
Central     A007820 [1, 1, 7, 90, 1701, 42525]

('--', 2, '---------------------')

by Shape    A257490
[0] [1]
[1] [1]
[2] [1, 3]
[3] [1, 15, 15]
[4] [1, 28, 35, 210, 105]
[5] [1, 45, 210, 630, 1575, 3150, 945]
[6] [1, 66, 495, 462, 1485, 13860, 5775, 13860, 51975, 51975, 10395]

[1, 1, 1, 3, 1, 15, 15, 1, 28, 35, 210, 105]

by Length   A156289
[0] [1]
[1] [0, 1]
[2] [0, 1, 3]
[3] [0, 1, 15, 15]
[4] [0, 1, 63, 210, 105]
[5] [0, 1, 255, 2205, 3150, 945]
[6] [0, 1, 1023, 21120, 65835, 51975, 10395]

by Recur/k!
[0] [1]
[1] [0, 1]
[2] [0, 1, 3]
[3] [0, 1, 15, 15]
[4] [0, 1, 63, 210, 105]
[5] [0, 1, 255, 2205, 3150, 945]
[6] [0, 1, 1023, 21120, 65835, 51975, 10395]

by Recurrence
[0] [1]
[1] [0, 1]
[2] [0, 1, 6]
[3] [0, 1, 30, 90]
[4] [0, 1, 126, 1260, 2520]
[5] [0, 1, 510, 13230, 75600, 113400]
[6] [0, 1, 2046, 126720, 1580040, 6237000, 7484400]

[1, 0, 1, 0, 1, 3, 0, 1, 15, 15, 0, 1, 63, 210, 105, 0, 1, 255, 2205, 3150, 945]

RowSum      A005046 [1, 1, 4, 31, 379, 6556, 150349]
AlterRowSum A260884 [1, -1, 2, -1, -43, 254, 4157]
Diagonal    A001147 [1, 1, 3, 15, 105, 945, 10395]
Central     A327416 [1, 1, 63, 21120, 20585565, 44025570225]

('--', 3, '---------------------')

by Shape    A327003
[0] [1]
[1] [1]
[2] [1, 10]
[3] [1, 84, 280]
[4] [1, 220, 462, 9240, 15400]
[5] [1, 455, 5005, 50050, 210210, 1401400, 1401400]
[6] [1, 816, 18564, 24310, 185640, 4084080, 2858856, 13613600, 85765680, 285885600, 190590400]

[1, 1, 1, 10, 1, 84, 280, 1, 220, 462, 9240, 15400]

by Length   A291451
[0] [1]
[1] [0, 1]
[2] [0, 1, 10]
[3] [0, 1, 84, 280]
[4] [0, 1, 682, 9240, 15400]
[5] [0, 1, 5460, 260260, 1401400, 1401400]
[6] [0, 1, 43690, 7128576, 99379280, 285885600, 190590400]

by Recur/k!
[0] [1]
[1] [0, 1]
[2] [0, 1, 10]
[3] [0, 1, 84, 280]
[4] [0, 1, 682, 9240, 15400]
[5] [0, 1, 5460, 260260, 1401400, 1401400]
[6] [0, 1, 43690, 7128576, 99379280, 285885600, 190590400]

by Recurrence
[0] [1]
[1] [0, 1]
[2] [0, 1, 20]
[3] [0, 1, 168, 1680]
[4] [0, 1, 1364, 55440, 369600]
[5] [0, 1, 10920, 1561560, 33633600, 168168000]
[6] [0, 1, 87380, 42771456, 2385102720, 34306272000, 137225088000]

[1, 0, 1, 0, 1, 10, 0, 1, 84, 280, 0, 1, 682, 9240, 15400, 0, 1, 5460, 260260, 1401400, 1401400]

RowSum      A291973 [1, 1, 11, 365, 25323, 3068521, 583027547]
AlterRowSum A291974 [1, -1, 9, -197, 6841, -254801, -3000807]
Diagonal    A025035 [1, 1, 10, 280, 15400, 1401400, 190590400]
Central     A327417 [1, 1, 682, 7128576, 429120851544, 94066556834970720]

('--', 4, '---------------------')

by Shape    A327004
[0] [1]
[1] [1]
[2] [1, 35]
[3] [1, 495, 5775]
[4] [1, 1820, 6435, 450450, 2627625]
[5] [1, 4845, 125970, 4408950, 31177575, 727476750, 2546168625]
[6] [1, 10626, 735471, 1352078, 25741485, 1338557220, 1577585295, 15616500900, 165646455975, 1932541986375, 4509264634875]

[1, 1, 1, 35, 1, 495, 5775, 1, 1820, 6435, 450450, 2627625]

by Length   A291452
[0] [1]
[1] [0, 1]
[2] [0, 1, 35]
[3] [0, 1, 495, 5775]
[4] [0, 1, 8255, 450450, 2627625]
[5] [0, 1, 130815, 35586525, 727476750, 2546168625]
[6] [0, 1, 2098175, 2941884000, 181262956875, 1932541986375, 4509264634875]

by Recur/k!
[0] [1]
[1] [0, 1]
[2] [0, 1, 35]
[3] [0, 1, 495, 5775]
[4] [0, 1, 8255, 450450, 2627625]
[5] [0, 1, 130815, 35586525, 727476750, 2546168625]
[6] [0, 1, 2098175, 2941884000, 181262956875, 1932541986375, 4509264634875]

by Recurrence
[0] [1]
[1] [0, 1]
[2] [0, 1, 70]
[3] [0, 1, 990, 34650]
[4] [0, 1, 16510, 2702700, 63063000]
[5] [0, 1, 261630, 213519150, 17459442000, 305540235000]
[6] [0, 1, 4196350, 17651304000, 4350310965000, 231905038365000, 3246670537110000]

[1, 0, 1, 0, 1, 35, 0, 1, 495, 5775, 0, 1, 8255, 450450, 2627625, 0, 1, 130815, 35586525, 727476750, 2546168625]

RowSum      A291975 [1, 1, 36, 6271, 3086331, 3309362716, 6626013560301]
AlterRowSum A291976 [1, -1, 34, -5281, 2185429, -1854147586, 2755045819549]
Diagonal    A025036 [1, 1, 35, 5775, 2627625, 2546168625, 4509264634875]
Central     A327418 [1, 1, 8255, 2941884000, 11957867341948125, 294040106448733743008625]

=#

max_stars_repo_name: OpenLibMathSeq/Sequences

max_issues_repo_name: OpenLibMathSeq/Sequences


text:
## (c) 2016 David A. van Leeuwen

## Unit tests for ../src/arithmetic.jl

## This code is licensed under the MIT license
## See the file LICENSE.md in this distribution

## test arithmetic operations
print("arithmetic, ")

x = NamedArray(randn(5, 10))
@test (-x).array == -(x.array)
for op in (:+, :-, :*, :.+, :.-, :.*, :./)
	@eval begin
		for y in (true, Int8(3), Int16(3), Int32(3), Int64(3), Float32(π), Float64(π), BigFloat(π))
			z = ($op)(x, y)
			@test z.array == ($op)(x.array, y)
			@test allnames(z) == allnames(x)
			z = ($op)(y, x)
			@test z.array == ($op)(y, x.array)
			@test allnames(z) == allnames(x)
		end
	end
end
for op in (:+, :-, :.+, :.-, :.*, :./)
	for T in (Bool, Int8, Int16, Int32, Int64, Float32, Float64)
		@eval begin
			y = rand($T, 5, 10)
			z = ($op)(x, y)
			@test z.array == ($op)(x.array, y)
			@test allnames(z) == allnames(x)
			z = ($op)(y, x)
			@test z.array == ($op)(y, x.array)
			@test allnames(z) == allnames(x)
		end
	end
end


text:
# using Revise, Plots
using Parameters, Setfield, LinearAlgebra, Test
using BifurcationKit, Test
const BK = BifurcationKit

norminf = (x) -> norm(x, Inf)
recordFromSolution(x, p) = (u1 = x[1], u2 = x[2])
####################################################################################################
function lur!(dz, z, p, t)
	@unpack α, β = p
	x, y, z = z
	dz[1] = y
	dz[2] =	z
	dz[3] = -α * z - β * y - x + x^2
	dz
end

lur(z, p) = lur!(similar(z), z, p, 0)
jet = BK.getJet(lur; matrixfree=false)

par_lur = (α = 1.0, β = 0.)
z0 = zeros(3)

opts_br = ContinuationPar(pMin = -0.4, pMax = 1.8, ds = -0.01, dsmax = 0.01, nInversion = 8, detectBifurcation = 3, maxBisectionSteps = 25, nev = 3, plotEveryStep = 20, maxSteps = 1000, theta = 0.3)
	opts_br = @set opts_br.newtonOptions.verbose = false
	br, = continuation(jet[1], jet[2], z0, par_lur, (@lens _.β), opts_br;
	recordFromSolution = recordFromSolution,
	tangentAlgo = BorderedPred(),
	bothside = true, normC = norminf)

# plot(br)
####################################################################################################
# newton parameters
optn_po = NewtonPar(tol = 1e-8,  maxIter = 25)

# continuation parameters
opts_po_cont = ContinuationPar(dsmax = 0.03, ds= 0.0001, dsmin = 1e-4, pMax = 1.8, pMin=-5., maxSteps = 130, newtonOptions = (@set optn_po.tol = 1e-8), nev = 3, precisionStability = 1e-4, detectBifurcation = 3, plotEveryStep = 20, saveSolEveryStep=1, nInversion = 6)

Mt = 90 # number of time sections
	br_po, = continuation(
	jet..., br, 1, opts_po_cont,
	PeriodicOrbitTrapProblem(M = Mt);
	ampfactor = 1., δp = 0.01,
	updateSectionEveryStep = 1,
	jacobianPO = :Dense,
	verbosity = 0,	plot = false,
	recordFromSolution = (x, p) -> (xtt=reshape(x[1:end-1],3,Mt); return (max = maximum(xtt[1,:]), min = minimum(xtt[1,:]), period = x[end])),
	finaliseSolution = (z, tau, step, contResult; prob = nothing, kwargs...) -> begin
			return z.u[end] < 40
			true
		end,
	normC = norminf)

# plot(br, br_po)
# plot(br_po, vars=(:param, :period))

# aBS from PD
br_po_pd, = BK.continuation(br_po, 1, setproperties(br_po.contparams, detectBifurcation = 3, maxSteps = 51, ds = 0.01, dsmax = 0.01, plotEveryStep = 10);
	verbosity = 0, plot = false,
	ampfactor = .1, δp = -0.005,
	usedeflation = false,
	jacobianPO = :Dense,
	updateSectionEveryStep = 1,
	recordFromSolution = (x, p) -> (xtt=reshape(x[1:end-1],3,Mt); return (max = maximum(xtt[1,:]), min = minimum(xtt[1,:]), period = x[end])),
	normC = norminf
	)

# plot(br, br_po, br_po_pd, xlims=(0.5,0.65))
####################################################################################################
using OrdinaryDiffEq

probsh = ODEProblem(lur!, copy(z0), (0., 1000.), par_lur; atol = 1e-10, rtol = 1e-7)

optn_po = NewtonPar(tol = 1e-7, maxIter = 25)

# continuation parameters
opts_po_cont = ContinuationPar(dsmax = 0.01, ds= -0.001, dsmin = 1e-4, maxSteps = 130, newtonOptions = (@set optn_po.tol = 1e-8), precisionStability = 1e-5, detectBifurcation = 3, plotEveryStep = 10, saveSolEveryStep = 0, nInversion = 6, nev = 3)

br_po, = continuation(
	jet..., br, 1, opts_po_cont,
	ShootingProblem(15, probsh, Rodas4P(); parallel = true, reltol = 1e-9);
	ampfactor = 1., δp = 0.0051,
	updateSectionEveryStep = 1,
	jacobianPO = :autodiffDense,
	# verbosity = 3,	plot = true,
	# recordFromSolution = (x, p) -> (return (max = getMaximum(p.prob, x, @set par_lur.β = p.p), period = getPeriod(p.prob, x, @set par_lur.β = p.p))),
	# plotSolution = plotSH,
	# finaliseSolution = (z, tau, step, contResult; prob = nothing, kwargs...) -> begin
	# 		BK.haseigenvalues(contResult) && Base.display(contResult.eig[end].eigenvals)
	# 		return z.u[end] < 30 && length(contResult.specialpoint) < 3
	# 		true
	# 	end,
	callbackN = BK.cbMaxNorm(10),
	normC = norminf)

# plot(br, br_po)
# plot(br_po, vars=(:param, :period))

@test br_po.specialpoint[1].param ≈ 0.6273246 rtol = 1e-4
@test br_po.specialpoint[2].param ≈ 0.5417461 rtol = 1e-4

# aBS from PD
br_po_pd, = BK.continuation(br_po, 1, setproperties(br_po.contparams, detectBifurcation = 3, maxSteps = 50, ds = 0.01, plotEveryStep = 1);
	# verbosity = 3, plot = true,
	ampfactor = .3, δp = -0.005,
	jacobianPO = :autodiffDense,
	normC = norminf,
	callbackN = BK.cbMaxNorm(10),
	)

# plot(br_po, br_po_pd)
#######################################
opts_po_cont_ps = @set opts_po_cont.newtonOptions.tol = 1e-7
@set opts_po_cont_ps.dsmax = 0.0025
br_po, = continuation(jet..., br, 1, opts_po_cont_ps,
	PoincareShootingProblem(2, probsh, Rodas4P(); parallel = true, reltol = 1e-6);
	ampfactor = 1., δp = 0.0051, #verbosity = 3,plot=true,
	updateSectionEveryStep = 1,
	jacobianPO = :autodiffDenseAnalytical,
	callbackN = BK.cbMaxNorm(10),
	normC = norminf)

# aBS from PD
br_po_pd, = BK.continuation(br_po, 1, setproperties(br_po.contparams, detectBifurcation = 3, maxSteps = 50, ds = 0.01, plotEveryStep = 1);
	# verbosity = 3, plot = true,
	ampfactor = .3, δp = -0.005,
	jacobianPO = :autodiffDenseAnalytical,
	normC = norminf,
	callbackN = BK.cbMaxNorm(10),
	)