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

"""
    associate_configuration_items_to_application(application_configuration_id, configuration_ids)
    associate_configuration_items_to_application(application_configuration_id, configuration_ids, params::Dict{String,<:Any})

Associates one or more configuration items with an application.

# Arguments
- `application_configuration_id`: The configuration ID of an application with which items
  are to be associated.
- `configuration_ids`: The ID of each configuration item to be associated with an
  application.

"""
associate_configuration_items_to_application(applicationConfigurationId, configurationIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("AssociateConfigurationItemsToApplication", Dict{String, Any}("applicationConfigurationId"=>applicationConfigurationId, "configurationIds"=>configurationIds); aws_config=aws_config)
associate_configuration_items_to_application(applicationConfigurationId, configurationIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("AssociateConfigurationItemsToApplication", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("applicationConfigurationId"=>applicationConfigurationId, "configurationIds"=>configurationIds), params)); aws_config=aws_config)

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

Deletes one or more import tasks, each identified by their import ID. Each import task has
a number of records that can identify servers or applications.  AWS Application Discovery
Service has built-in matching logic that will identify when discovered servers match
existing entries that you've previously discovered, the information for the
already-existing discovered server is updated. When you delete an import task that contains
records that were used to match, the information in those matched records that comes from
the deleted records will also be deleted.

# Arguments
- `import_task_ids`: The IDs for the import tasks that you want to delete.

"""
batch_delete_import_data(importTaskIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("BatchDeleteImportData", Dict{String, Any}("importTaskIds"=>importTaskIds); aws_config=aws_config)
batch_delete_import_data(importTaskIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("BatchDeleteImportData", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("importTaskIds"=>importTaskIds), params)); aws_config=aws_config)

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

Creates an application with the given name and description.

# Arguments
- `name`: Name of the application to be created.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"description"`: Description of the application to be created.
"""
create_application(name; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("CreateApplication", Dict{String, Any}("name"=>name); aws_config=aws_config)
create_application(name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("CreateApplication", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("name"=>name), params)); aws_config=aws_config)

"""
    create_tags(configuration_ids, tags)
    create_tags(configuration_ids, tags, params::Dict{String,<:Any})

Creates one or more tags for configuration items. Tags are metadata that help you
categorize IT assets. This API accepts a list of multiple configuration items.

# Arguments
- `configuration_ids`: A list of configuration items that you want to tag.
- `tags`: Tags that you want to associate with one or more configuration items. Specify the
  tags that you want to create in a key-value format. For example:  {\"key\": \"serverType\",
  \"value\": \"webServer\"}

"""
create_tags(configurationIds, tags; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("CreateTags", Dict{String, Any}("configurationIds"=>configurationIds, "tags"=>tags); aws_config=aws_config)
create_tags(configurationIds, tags, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("CreateTags", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationIds"=>configurationIds, "tags"=>tags), params)); aws_config=aws_config)

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

Deletes a list of applications and their associations with configuration items.

# Arguments
- `configuration_ids`: Configuration ID of an application to be deleted.

"""
delete_applications(configurationIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DeleteApplications", Dict{String, Any}("configurationIds"=>configurationIds); aws_config=aws_config)
delete_applications(configurationIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DeleteApplications", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationIds"=>configurationIds), params)); aws_config=aws_config)

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

Deletes the association between configuration items and one or more tags. This API accepts
a list of multiple configuration items.

# Arguments
- `configuration_ids`: A list of configuration items with tags that you want to delete.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"tags"`: Tags that you want to delete from one or more configuration items. Specify the
  tags that you want to delete in a key-value format. For example:  {\"key\": \"serverType\",
  \"value\": \"webServer\"}
"""
delete_tags(configurationIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DeleteTags", Dict{String, Any}("configurationIds"=>configurationIds); aws_config=aws_config)
delete_tags(configurationIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DeleteTags", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationIds"=>configurationIds), params)); aws_config=aws_config)

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

Lists agents or connectors as specified by ID or other filters. All agents/connectors
associated with your user account can be listed if you call DescribeAgents as is without
passing any parameters.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"agentIds"`: The agent or the Connector IDs for which you want information. If you
  specify no IDs, the system returns information about all agents/Connectors associated with
  your AWS user account.
- `"filters"`: You can filter the request using various logical operators and a key-value
  format. For example:   {\"key\": \"collectionStatus\", \"value\": \"STARTED\"}
- `"maxResults"`: The total number of agents/Connectors to return in a single page of
  output. The maximum value is 100.
- `"nextToken"`: Token to retrieve the next set of results. For example, if you previously
  specified 100 IDs for DescribeAgentsRequestagentIds but set DescribeAgentsRequestmaxResults
  to 10, you received a set of 10 results along with a token. Use that token in this query to
  get the next set of 10.
"""
describe_agents(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeAgents"; aws_config=aws_config)
describe_agents(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeAgents", params; aws_config=aws_config)

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

Retrieves attributes for a list of configuration item IDs.  All of the supplied IDs must be
for the same asset type from one of the following:   server   application   process
connection   Output fields are specific to the asset type specified. For example, the
output for a server configuration item includes a list of attributes about the server, such
as host name, operating system, number of network cards, etc. For a complete list of
outputs for each asset type, see Using the DescribeConfigurations Action in the AWS
Application Discovery Service User Guide.

# Arguments
- `configuration_ids`: One or more configuration IDs.

"""
describe_configurations(configurationIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeConfigurations", Dict{String, Any}("configurationIds"=>configurationIds); aws_config=aws_config)
describe_configurations(configurationIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeConfigurations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationIds"=>configurationIds), params)); aws_config=aws_config)

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

Lists exports as specified by ID. All continuous exports associated with your user account
can be listed if you call DescribeContinuousExports as is without passing any parameters.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"exportIds"`: The unique IDs assigned to the exports.
- `"maxResults"`: A number between 1 and 100 specifying the maximum number of continuous
  export descriptions returned.
- `"nextToken"`: The token from the previous call to DescribeExportTasks.
"""
describe_continuous_exports(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeContinuousExports"; aws_config=aws_config)
describe_continuous_exports(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeContinuousExports", params; aws_config=aws_config)

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

 DescribeExportConfigurations is deprecated. Use DescribeImportTasks, instead.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"exportIds"`: A list of continuous export IDs to search for.
- `"maxResults"`: A number between 1 and 100 specifying the maximum number of continuous
  export descriptions returned.
- `"nextToken"`: The token from the previous call to describe-export-tasks.
"""
describe_export_configurations(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeExportConfigurations"; aws_config=aws_config)
describe_export_configurations(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeExportConfigurations", params; aws_config=aws_config)

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

Retrieve status of one or more export tasks. You can retrieve the status of up to 100
export tasks.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"exportIds"`: One or more unique identifiers used to query the status of an export
  request.
- `"filters"`: One or more filters.    AgentId - ID of the agent whose collected data will
  be exported
- `"maxResults"`: The maximum number of volume results returned by DescribeExportTasks in
  paginated output. When this parameter is used, DescribeExportTasks only returns maxResults
  results in a single page along with a nextToken response element.
- `"nextToken"`: The nextToken value returned from a previous paginated DescribeExportTasks
  request where maxResults was used and the results exceeded the value of that parameter.
  Pagination continues from the end of the previous results that returned the nextToken
  value. This value is null when there are no more results to return.
"""
describe_export_tasks(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeExportTasks"; aws_config=aws_config)
describe_export_tasks(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeExportTasks", params; aws_config=aws_config)

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

Returns an array of import tasks for your account, including status information, times,
IDs, the Amazon S3 Object URL for the import file, and more.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"filters"`: An array of name-value pairs that you provide to filter the results for the
  DescribeImportTask request to a specific subset of results. Currently, wildcard values
  aren't supported for filters.
- `"maxResults"`: The maximum number of results that you want this request to return, up to
  100.
- `"nextToken"`: The token to request a specific page of results.
"""
describe_import_tasks(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeImportTasks"; aws_config=aws_config)
describe_import_tasks(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeImportTasks", params; aws_config=aws_config)

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

Retrieves a list of configuration items that have tags as specified by the key-value pairs,
name and value, passed to the optional parameter filters. There are three valid tag filter
names:   tagKey   tagValue   configurationId   Also, all configuration items associated
with your user account that have tags can be listed if you call DescribeTags as is without
passing any parameters.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"filters"`: You can filter the list using a key-value format. You can separate these
  items by using logical operators. Allowed filters include tagKey, tagValue, and
  configurationId.
- `"maxResults"`: The total number of items to return in a single page of output. The
  maximum value is 100.
- `"nextToken"`: A token to start the list. Use this token to get the next set of results.
"""
describe_tags(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeTags"; aws_config=aws_config)
describe_tags(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DescribeTags", params; aws_config=aws_config)

"""
    disassociate_configuration_items_from_application(application_configuration_id, configuration_ids)
    disassociate_configuration_items_from_application(application_configuration_id, configuration_ids, params::Dict{String,<:Any})

Disassociates one or more configuration items from an application.

# Arguments
- `application_configuration_id`: Configuration ID of an application from which each item
  is disassociated.
- `configuration_ids`: Configuration ID of each item to be disassociated from an
  application.

"""
disassociate_configuration_items_from_application(applicationConfigurationId, configurationIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DisassociateConfigurationItemsFromApplication", Dict{String, Any}("applicationConfigurationId"=>applicationConfigurationId, "configurationIds"=>configurationIds); aws_config=aws_config)
disassociate_configuration_items_from_application(applicationConfigurationId, configurationIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("DisassociateConfigurationItemsFromApplication", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("applicationConfigurationId"=>applicationConfigurationId, "configurationIds"=>configurationIds), params)); aws_config=aws_config)

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

Deprecated. Use StartExportTask instead. Exports all discovered configuration data to an
Amazon S3 bucket or an application that enables you to view and evaluate the data. Data
includes tags and tag associations, processes, connections, servers, and system
performance. This API returns an export ID that you can query using the
DescribeExportConfigurations API. The system imposes a limit of two configuration exports
in six hours.

"""
export_configurations(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("ExportConfigurations"; aws_config=aws_config)
export_configurations(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("ExportConfigurations", params; aws_config=aws_config)

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

Retrieves a short summary of discovered assets. This API operation takes no request
parameters and is called as is at the command prompt as shown in the example.

"""
get_discovery_summary(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("GetDiscoverySummary"; aws_config=aws_config)
get_discovery_summary(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("GetDiscoverySummary", params; aws_config=aws_config)

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

Retrieves a list of configuration items as specified by the value passed to the required
parameter configurationType. Optional filtering may be applied to refine search results.

# Arguments
- `configuration_type`: A valid configuration identified by Application Discovery Service.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"filters"`: You can filter the request using various logical operators and a key-value
  format. For example:   {\"key\": \"serverType\", \"value\": \"webServer\"}  For a complete
  list of filter options and guidance about using them with this action, see Using the
  ListConfigurations Action in the AWS Application Discovery Service User Guide.
- `"maxResults"`: The total number of items to return. The maximum value is 100.
- `"nextToken"`: Token to retrieve the next set of results. For example, if a previous call
  to ListConfigurations returned 100 items, but you set ListConfigurationsRequestmaxResults
  to 10, you received a set of 10 results along with a token. Use that token in this query to
  get the next set of 10.
- `"orderBy"`: Certain filter criteria return output that can be sorted in ascending or
  descending order. For a list of output characteristics for each filter, see Using the
  ListConfigurations Action in the AWS Application Discovery Service User Guide.
"""
list_configurations(configurationType; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("ListConfigurations", Dict{String, Any}("configurationType"=>configurationType); aws_config=aws_config)
list_configurations(configurationType, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("ListConfigurations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationType"=>configurationType), params)); aws_config=aws_config)

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

Retrieves a list of servers that are one network hop away from a specified server.

# Arguments
- `configuration_id`: Configuration ID of the server for which neighbors are being listed.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: Maximum number of results to return in a single page of output.
- `"neighborConfigurationIds"`: List of configuration IDs to test for one-hop-away.
- `"nextToken"`: Token to retrieve the next set of results. For example, if you previously
  specified 100 IDs for ListServerNeighborsRequestneighborConfigurationIds but set
  ListServerNeighborsRequestmaxResults to 10, you received a set of 10 results along with a
  token. Use that token in this query to get the next set of 10.
- `"portInformationNeeded"`: Flag to indicate if port and protocol information is needed as
  part of the response.
"""
list_server_neighbors(configurationId; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("ListServerNeighbors", Dict{String, Any}("configurationId"=>configurationId); aws_config=aws_config)
list_server_neighbors(configurationId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("ListServerNeighbors", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationId"=>configurationId), params)); aws_config=aws_config)

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

Start the continuous flow of agent's discovered data into Amazon Athena.

"""
start_continuous_export(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartContinuousExport"; aws_config=aws_config)
start_continuous_export(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartContinuousExport", params; aws_config=aws_config)

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

Instructs the specified agents or connectors to start collecting data.

# Arguments
- `agent_ids`: The IDs of the agents or connectors from which to start collecting data. If
  you send a request to an agent/connector ID that you do not have permission to contact,
  according to your AWS account, the service does not throw an exception. Instead, it returns
  the error in the Description field. If you send a request to multiple agents/connectors and
  you do not have permission to contact some of those agents/connectors, the system does not
  throw an exception. Instead, the system shows Failed in the Description field.

"""
start_data_collection_by_agent_ids(agentIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartDataCollectionByAgentIds", Dict{String, Any}("agentIds"=>agentIds); aws_config=aws_config)
start_data_collection_by_agent_ids(agentIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartDataCollectionByAgentIds", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("agentIds"=>agentIds), params)); aws_config=aws_config)

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

 Begins the export of discovered data to an S3 bucket.  If you specify agentIds in a
filter, the task exports up to 72 hours of detailed data collected by the identified
Application Discovery Agent, including network, process, and performance details. A time
range for exported agent data may be set by using startTime and endTime. Export of detailed
agent data is limited to five concurrently running exports.   If you do not include an
agentIds filter, summary data is exported that includes both AWS Agentless Discovery
Connector data and summary data from AWS Discovery Agents. Export of summary data is
limited to two exports per day.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"endTime"`: The end timestamp for exported data from the single Application Discovery
  Agent selected in the filters. If no value is specified, exported data includes the most
  recent data collected by the agent.
- `"exportDataFormat"`: The file format for the returned export data. Default value is CSV.
  Note: The GRAPHML option has been deprecated.
- `"filters"`: If a filter is present, it selects the single agentId of the Application
  Discovery Agent for which data is exported. The agentId can be found in the results of the
  DescribeAgents API or CLI. If no filter is present, startTime and endTime are ignored and
  exported data includes both Agentless Discovery Connector data and summary data from
  Application Discovery agents.
- `"startTime"`: The start timestamp for exported data from the single Application
  Discovery Agent selected in the filters. If no value is specified, data is exported
  starting from the first data collected by the agent.
"""
start_export_task(; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartExportTask"; aws_config=aws_config)
start_export_task(params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartExportTask", params; aws_config=aws_config)

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

Starts an import task, which allows you to import details of your on-premises environment
directly into AWS Migration Hub without having to use the Application Discovery Service
(ADS) tools such as the Discovery Connector or Discovery Agent. This gives you the option
to perform migration assessment and planning directly from your imported data, including
the ability to group your devices as applications and track their migration status. To
start an import request, do this:   Download the specially formatted comma separated value
(CSV) import template, which you can find here:
https://s3-us-west-2.amazonaws.com/templates-7cffcf56-bd96-4b1c-b45b-a5b42f282e46/import_tem
plate.csv.   Fill out the template with your server and application data.   Upload your
import file to an Amazon S3 bucket, and make a note of it's Object URL. Your import file
must be in the CSV format.   Use the console or the StartImportTask command with the AWS
CLI or one of the AWS SDKs to import the records from your file.   For more information,
including step-by-step procedures, see Migration Hub Import in the AWS Application
Discovery Service User Guide.  There are limits to the number of import tasks you can
create (and delete) in an AWS account. For more information, see AWS Application Discovery
Service Limits in the AWS Application Discovery Service User Guide.

# Arguments
- `import_url`: The URL for your import file that you've uploaded to Amazon S3.  If you're
  using the AWS CLI, this URL is structured as follows: s3://BucketName/ImportFileName.CSV
- `name`: A descriptive name for this request. You can use this name to filter future
  requests related to this import task, such as identifying applications and servers that
  were included in this import task. We recommend that you use a meaningful name for each
  import task.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"clientRequestToken"`: Optional. A unique token that you can provide to prevent the same
  import request from occurring more than once. If you don't provide a token, a token is
  automatically generated. Sending more than one StartImportTask request with the same client
  request token will return information about the original import task with that client
  request token.
"""
start_import_task(importUrl, name; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartImportTask", Dict{String, Any}("importUrl"=>importUrl, "name"=>name, "clientRequestToken"=>string(uuid4())); aws_config=aws_config)
start_import_task(importUrl, name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StartImportTask", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("importUrl"=>importUrl, "name"=>name, "clientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

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

Stop the continuous flow of agent's discovered data into Amazon Athena.

# Arguments
- `export_id`: The unique ID assigned to this export.

"""
stop_continuous_export(exportId; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StopContinuousExport", Dict{String, Any}("exportId"=>exportId); aws_config=aws_config)
stop_continuous_export(exportId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StopContinuousExport", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("exportId"=>exportId), params)); aws_config=aws_config)

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

Instructs the specified agents or connectors to stop collecting data.

# Arguments
- `agent_ids`: The IDs of the agents or connectors from which to stop collecting data.

"""
stop_data_collection_by_agent_ids(agentIds; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StopDataCollectionByAgentIds", Dict{String, Any}("agentIds"=>agentIds); aws_config=aws_config)
stop_data_collection_by_agent_ids(agentIds, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("StopDataCollectionByAgentIds", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("agentIds"=>agentIds), params)); aws_config=aws_config)

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

Updates metadata about an application.

# Arguments
- `configuration_id`: Configuration ID of the application to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"description"`: New description of the application to be updated.
- `"name"`: New name of the application to be updated.
"""
update_application(configurationId; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("UpdateApplication", Dict{String, Any}("configurationId"=>configurationId); aws_config=aws_config)
update_application(configurationId, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()) = application_discovery_service("UpdateApplication", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("configurationId"=>configurationId), params)); aws_config=aws_config)


text:
#1D functions
function multimodal_1D(sample_sats::Vector{Vector{Float64}},analytical_stats::Vector{Vector{Any}})
    new_analytical_stats = []
	print(analytical_stats)
	print(sample_sats)
    for i in 1:length(sample_stats)
        push!(new_analytical_stats,one_multimodal_1D(sample_stats[i],analytical_stats[i]))
    end
    return new_analytical_stats
end

function one_multimodal_1D(sample_stats::Any,analytical_stats::Any)
    new_analytical_stats = Array{Float64}(undef,length(analytical_stats))
    diff = abs.(sample_stats[1])
    if length(analytical_stats[1]) > 1
        new_analytical_stats[1] = analytical_stats[1][argmin(abs.(analytical_stats[1] .- sample_stats[1]))]
        for i in 2:length(analytical_stats)
            new_analytical_stats[i] = analytical_stats[i]
        end
        return new_analytical_stats
    else
        return analytical_stats
    end
end

function plot_diff_1D(
	bincenters::Vector{Float64},
	differences::Vector{Float64},
	err1::Vector{Float64},
	err2::Vector{Float64},
	err3::Vector{Float64},
	name::String)

    scatter(
        bincenters,
        differences,
        marker=(:x,2),
        label="Difference",
        ylim=(-maximum(vcat(err3...,abs.(differences)...)),maximum(vcat(err3...,abs.(differences)...))),
        ylabel=L"$f_{MCMC}-f_{ana}$"
    )
    nfill=0.3
    plot!(
        bincenters,
        err1,
        fillrange = 0,
        color="green",
        label="",
        fillalpha=nfill
    )
    plot!(
        bincenters,
        -err1,
        fillrange = 0,
        color="green",
        label="",
        fillalpha=nfill
    )
    plot!(
        bincenters,
        err2,
        fillrange = err1,
        color="yellow",
        label="",
        fillalpha=nfill
    )
    plot!(
        bincenters,
        -err2,
        fillrange = -err1,
        color="yellow",
        label="",
        fillalpha=nfill
    )
    plot!(
        bincenters,
        err3,
        fillrange = err2,
        color="red",
        label="",
        fillalpha=nfill
    )
    plot!(
        bincenters,
        -err3,
        fillrange = -err2,
        color="red",
        label="",
        fillalpha=nfill
    )
    savefig(string("plots1D/",name,"_diff.pdf"))
end

function plot1D(
	samples::DensitySampleVector,
	testfunctions::Dict,
	name::String,
	sample_stats::Vector{Float64})

	func = k->pdf(testfunctions[name].posterior,[k])[1]

	lo = minimum([BAT.flatview(samples.v)...])
	hi = maximum([BAT.flatview(samples.v)...])
	if(!isa(testfunctions_1D[name].posterior, BAT.FunnelDistribution))
		lo, hi = quantile.(testfunctions_1D[name].posterior, [0.001, 0.999]) #BAT Distributions do not support quantiles
	end
	if(isa(testfunctions_1D[name].posterior, Distributions.Cauchy))
		lo, hi = quantile.(testfunctions_1D[name].posterior, [0.01, 0.99]) #Cauchy needs smaller interval for useful plots however just for the plot
	end
	binning = range(lo, hi; length=200)

    hunnorm = fit(Histogram, [BAT.flatview(samples.v)...],FrequencyWeights(BAT.flatview(samples.weight)),binning)
	h = fit(Histogram, [BAT.flatview(samples.v)...],FrequencyWeights(BAT.flatview(samples.weight)),binning)
	idx = BAT.asindex(samples, 1)
	uvbd = EmpiricalDistributions.UvBinnedDist(h)
	marg = BAT.MarginalDist((idx,),uvbd,varshape(samples))
	h = StatsBase.normalize(h)

	plot(marg,1,seriestype = :smallest_intervals,normalize=true)

    edge_widths = h.edges[1][2:end]-h.edges[1][1:end-1]
    edge_mids = h.edges[1][1:end-1].+ (0.5*edge_widths)

	plot!(edge_mids,broadcast(func,edge_mids),label="Analytical")
	savefig(string("plots1D/",name,".pdf"))

	if(isa(testfunctions_1D[name].posterior, Distributions.Cauchy))
		lo, hi = quantile.(testfunctions_1D[name].posterior, [0.001, 0.999])
		binning = range(lo, hi; length=200)
	end

    nun = convert(Int64,floor(sum(hunnorm.weights)/10))
    unweighted_samples = bat_sample(samples, OrderedResampling(nsamples = nun)).result
    hunnorm = fit(Histogram, [BAT.flatview(unweighted_samples.v)...],binning)

    edges = hunnorm.edges[1]
    nbins = length(edges)-1
    bincenters = zeros(nbins)
    differences = zeros(nbins)
    err1 = zeros(nbins)
    err2 = zeros(nbins)
    err3 = zeros(nbins)
    pulls = zeros(nbins)
    norm_hist = sum(hunnorm.weights)

    ndf = nbins
    chi2 = 0

    narr = zeros(nbins)
    earr = zeros(nbins)

    for ibin in 1:nbins
        bincenters[ibin] = edges[ibin]+0.5*(edges[ibin+1]-edges[ibin])
        n = hunnorm.weights[ibin]
        e = norm_hist * quadgk(func,edges[ibin],edges[ibin+1])[1]
        narr[ibin] = n
        earr[ibin] = e
        differences[ibin] = n - e
        err1[ibin] = sqrt(e)
        err2[ibin] = 2 * err1[ibin]
        err3[ibin] = 3 * err1[ibin]
        pulls[ibin] = (n-e)/sqrt(e)
        if (e >= 10)
            chi2 += (n - e) * (n - e) / e
        else
            ndf = ndf-1
        end
    end

    plot_diff_1D(bincenters,differences,err1,err2,err3,name)

    hpull = fit(Histogram,pulls[pulls .!= 999],nbins=25)
    d = Normal(0,1)
    lo, hi = quantile.(d, [0.00001, 0.99999])
    x = range(lo, hi; length = 100)
    binlength = hpull.edges[1].step.hi
    plot(hpull,st=:step,normalize=false,label="",xlabel=L"$\Delta/\sigma$")
    plot!(x, pdf.(d, x)*sum(hpull.weights)*binlength,label="")
    savefig(string("plots1D/",name,"_pull.pdf"))

    if(testfunctions[name].chi2[1] == 9999)
		testfunctions[name].chi2[1]=ndf
	end
    length(sample_stats) != 4 ? push!(sample_stats,chi2)     : sample_stats[4] = chi2

	iid_sample = bat_sample(testfunctions[name].posterior, IIDSampling(nsamples = length([BAT.flatview(samples.v)...]))).result
	if(testfunctions[name].ks[1] > 999)
		testfunctions[name].ks[1]=bat_compare(samples,iid_sample).result.ks_p_values[1]
	end

	if(testfunctions[name].ahmi[1] > 999)
		testfunctions[name].ahmi[1]=bat_integrate(samples,AHMIntegration()).result.val
	end

	return h
end

function run1D(
	key::String,
	testfunctions::Dict,
    sample_stats::Vector{Float64},
    run_stats::Vector{Float64},
	algorithm::BAT.AbstractSamplingAlgorithm,
	n_steps::Integer,
	n_chains::Integer,
    n_runs=1
	)

    sample_stats_all = []
    samples, chains = bat_sample(testfunctions[key].posterior, MCMCSampling(sampler = algorithm, trafo = NoDensityTransform(), nchains = n_chains, nsteps = n_steps))
    for i in 1:n_runs
        time_before = time()
        samples, chains = bat_sample(testfunctions[key].posterior, MCMCSampling(sampler = algorithm, trafo = NoDensityTransform(), nchains = n_chains, nsteps = n_steps))
        time_after = time()

    	h = plot1D(samples,testfunctions,key,sample_stats)# posterior, key, analytical_stats,sample_stats)

        sample_stats[1] = mode(samples)[1]
        sample_stats[2] = mean(samples)[1]
        sample_stats[3] = var(samples)[1]
        run_stats[1] = n_steps
        run_stats[2] = n_chains
        run_stats[3] = time_after-time_before
        push!(sample_stats_all,sample_stats)
    end
	return sample_stats_all
end

function make_1D_results(
	testfunctions::Dict,
	sample_stats::Vector{Vector{Float64}})
	#analytical_stats::Vector{Vector{Any}})

	name = Vector{String}()
	analytical_stats = Vector{Vector{Any}}()
	ks_p_val = Vector{Float64}()
	ahmi_val = Vector{Float64}()
	for (k,v) in testfunctions
		push!(name,k)
		push!(analytical_stats,[v.mode,v.mean,v.var,v.chi2[1]])
		push!(ks_p_val,round(v.ks[1],digits=3))
		push!(ahmi_val,round(v.ahmi[1],digits=3))
	end
    statistics_names = ["mode","mean","var","chi2"]
    comparison = ["target","test","diff (abs)","diff (rel)"]
    analytical_stats = multimodal_1D(sample_stats,analytical_stats)
    sample_stats = round.(permutedims(reshape(vcat(sample_stats...),length(sample_stats[1]),length(sample_stats))),digits=3)
    analytical_stats = round.(permutedims(reshape(vcat(analytical_stats...),length(analytical_stats[1]),length(analytical_stats))),digits=3)
    diff_stats_abs = round.(sample_stats-analytical_stats,digits=3)
    diff_stats_rel = round.(diff_stats_abs ./ analytical_stats * 100,digits=3)
    comparison_vals = [analytical_stats,sample_stats,diff_stats_abs,diff_stats_rel]

    #final_header = Vector{Any}(undef,length(statistics_names)*length(comparison)+1)
	#final_table = Array{Any}(undef,length(name),length(statistics_names)*length(comparison)+1)
	final_header = Vector{Any}(undef,length(name)+1)
	final_table = Array{Any}(undef,length(statistics_names)*length(comparison)+1+1,length(name)+1)
    final_header[1] = "name"
    for j in 1:length(statistics_names)
        for k in 1:length(comparison)
            final_table[(j-1)*length(statistics_names)+k,1] = string(statistics_names[j]," ",comparison[k])
        end
    end
    final_header[2:end]=name
    for i in 1:length(name)
        for j in 1:length(statistics_names)
            final_table[(j-1)*length(statistics_names)+1,i+1] = analytical_stats[i,j]
            final_table[(j-1)*length(statistics_names)+2,i+1] = sample_stats[i,j]
            final_table[(j-1)*length(statistics_names)+3,i+1] = diff_stats_abs[i,j]
            final_table[(j-1)*length(statistics_names)+4,i+1] = diff_stats_rel[i,j]
        end
    end

	final_table[length(statistics_names)*length(comparison)+1,:] = ["KS test p value",ks_p_val...]
	final_table[length(statistics_names)*length(comparison)+2,:] = ["AHMI Integral",ahmi_val...]
	f = open("results/results_1D.txt","w")
    pretty_table(f,final_table,final_header)
    close(f)
	fl = open("results/results_1D.tex","w")
    pretty_table(fl,final_table,final_header,backend=:latex)
    close(fl)
end

function save_stats_1D(name::Vector{String},
	run_stats::Vector{Vector{Float64}},
	run_stats_names::Vector{String})

    run_stats_table = reshape(vcat(run_stats...),length(run_stats[1]),length(run_stats))
    table_stats = Any[]
    append!(table_stats,[name])
    for i in 1:length(run_stats_table[:,1])
        append!(table_stats,[run_stats_table[i,:]])
    end
    table_stats = reshape(vcat(table_stats...),length(table_stats[1]),length(table_stats))
	table_stats[1:end,4] = round.(table_stats[1:end,4],digits=0)
	table_stats[1:end,2:end] = Int.(table_stats[1:end,2:end])
	f = open("results/run_stats_1D.txt","w")
    pretty_table(f,table_stats,["name",run_stats_names...])
    close(f)
	fl = open("results/run_stats_1D.tex","w")
    pretty_table(fl,table_stats,["name",run_stats_names...],backend=:latex)
    close(fl)
end

#2D functions

function plot2D(
	samples::DensitySampleVector,
	testfunctions::Dict,
	name::String,
	sample_stats::Vector{Any})

    nbin = 400

	plot_bins = nothing
	if name == "funnel"
		plot_bins = (-5:0.1:5,-10:0.1:10)
	elseif name == "multi cauchy"
		plot_bins = (-200:0.4:200,-200:0.4:200)
	else
		plot_bins = (5:0.1:25,-5:0.1:25)
	end

    h = fit(Histogram, (BAT.flatview(samples.v)[1,:],BAT.flatview(samples.v)[2,:]) ,FrequencyWeights(samples.weight),plot_bins)
    hunnorm = fit(Histogram, (BAT.flatview(samples.v)[1,:],BAT.flatview(samples.v)[2,:]) ,FrequencyWeights(samples.weight),plot_bins)

    h = StatsBase.normalize(h)

    nun = convert(Int64,floor(sum(hunnorm.weights)/10))
    unweighted_samples = bat_sample(samples, OrderedResampling(nsamples = nun)).result
    hunnorm = fit(Histogram, (BAT.flatview(unweighted_samples.v)[1,:],BAT.flatview(unweighted_samples.v)[2,:]),plot_bins)
    hana = fit(Histogram,([],[]),hunnorm.edges)
    hdiff = fit(Histogram,([],[]),hunnorm.edges)

    nbinx = length(hunnorm.edges[1])-1
    nbiny = length(hunnorm.edges[2])-1
    differences = zeros(nbinx,nbiny)
    pulls = zeros(nbinx,nbiny)

    ndf = nbinx*nbiny
    chi2 = 0
	v = Vector{Array{Float64,1}}()
	logval = Vector{Float64}()
	w = Vector{Float64}()

    for i in 1:length(hunnorm.edges[1])-1
        for j in 1:length(hunnorm.edges[2])-1
            binmidx = (hunnorm.edges[1][i+1]-hunnorm.edges[1][i])/2 + hunnorm.edges[1][i]
            binmidy = (hunnorm.edges[2][j+1]-hunnorm.edges[2][j])/2 + hunnorm.edges[2][j]
            binarea = (hunnorm.edges[1][i+1]-hunnorm.edges[1][i]) * (hunnorm.edges[2][j+1]-hunnorm.edges[2][j])

			#anaval = exp(posterior.likelihood.f((x=binmidx,y=binmidy)).value)*sum(hunnorm.weights)*binarea
			anaval = pdf(testfunctions[name].posterior,[binmidx,binmidy])*sum(hunnorm.weights)*binarea

            hana.weights[i,j] = convert(Int64,floor(anaval+0.5))

            differences[i,j] = (anaval-hunnorm.weights[i,j])
            hana.weights[i,j] < 10 ?  pulls[i,j] = 999 : pulls[i,j] = differences[i,j]/sqrt(anaval)

            hdiff.weights[i,j] = convert(Int64,floor(differences[i,j]+0.5))

            if (hunnorm.weights[i,j] >= 10)
            chi2 += (hunnorm.weights[i,j] - hana.weights[i,j]) * (hunnorm.weights[i,j] - hana.weights[i,j]) / hana.weights[i,j]
            else
                ndf = ndf-1
            end

			push!(v,[binmidx,binmidy])
			push!(logval,anaval)
			push!(w,anaval)
        end
    end

	if(testfunctions[name].chi2[1] == 9999)
		testfunctions[name].chi2[1]=ndf
	end

	iid_sample = bat_sample(testfunctions[name].posterior, IIDSampling(nsamples = length([BAT.flatview(samples.v)...]))).result
	if(testfunctions[name].ks[1] > 999)
		ksres = bat_compare(samples,iid_sample).result.ks_p_values
		testfunctions[name].ks[1]=ksres[1]
		testfunctions[name].ks[2]=ksres[2]
	end

	if(testfunctions[name].ahmi[1] > 999)
		ahmires = bat_integrate(samples,AHMIntegration()).result
		testfunctions[name].ahmi[1]=ahmires.val
		testfunctions[name].ahmi[2]=ahmires.err
	end

	#plot(hunnorm,(1,2),seriestype=:smallest_intervals)

	if name == "multi cauchy"
		plot_bins = (-60:0.5:60,-60:0.5:60)
		plot(samples,bins=plot_bins,globalmode=false)
		savefig(string("plots2D/default_",name,".pdf"))
	else
		plot(samples,bins=plot_bins,globalmode=true)
		savefig(string("plots2D/default_",name,".pdf"))
	end

	plot(unweighted_samples,(1,2),seriestype=:smallest_intervals,bins=plot_bins)
    savefig(string("plots2D/",name,".pdf"))

    plot(hana)#,(1,2),seriestype=:smallest_intervals)
    savefig(string("plots2D/",name,"_analytic.pdf"))

	#plot(hdiff,(1,2))
	plot(hdiff)
    savefig(string("plots2D/",name,"_diff.pdf"))

    pulls = vcat(pulls...)

    hpull = fit(Histogram,pulls[pulls .!= 999],nbins=30)
    d = Normal(0,1)
    lo, hi = quantile.(d, [0.00001, 0.99999])
    x = range(lo, hi; length = 100)
    binlength = hpull.edges[1].step.hi
    plot(hpull,normalize=false,label="",xlabel=L"$\Delta/\sigma$",ylabel="N",xlims=(-5,5))
    plot!(x, pdf.(d, x)*sum(hpull.weights)*binlength,label="",linewidth=2)
    savefig(string("plots2D/",name,"_pull.pdf"))

    return h
end

function run2D(
	key::String,
	testfunctions::Dict,
    sample_stats::Vector{Any},
    run_stats::Vector{Any},
	algorithm::MCMCAlgorithm,
	n_steps::Integer,
	n_chains::Integer,
	n_runs=1)

    sample_stats_all = []

    samples, stats = bat_sample(testfunctions[key].posterior, MCMCSampling(sampler = algorithm, trafo = NoDensityTransform(), nchains = n_chains, nsteps = n_steps))
    for i in 1:n_runs
        time_before = time()
        samples, stats = bat_sample(testfunctions[key].posterior, MCMCSampling(sampler = algorithm, trafo = NoDensityTransform(), nchains = n_chains, nsteps = n_steps))
        time_after = time()

		h = plot2D(samples, testfunctions, key, sample_stats)

        sample_stats[1] = mode(samples).data
        sample_stats[2] = mean(samples).data
        sample_stats[3] = var(samples).data

        run_stats[1] = n_steps
        run_stats[2] = n_chains
        run_stats[3] = time_after-time_before
        push!(sample_stats_all,sample_stats)
    end

    return sample_stats_all
end

function make_2D_results(testfunctions::Dict,sample_stats2D::Vector{Vector{Any}})

	name2D = Vector{String}()
	analytical_stats2D = Vector{Vector{Any}}()
	ks_p_val = Vector{Any}()
	ahmi_val = Vector{Any}()

	for (k,v) in testfunctions
		push!(name2D,k)
		push!(analytical_stats2D,[v.mode,v.mean,v.var])
		push!(ks_p_val,round.(v.ks,digits=3))
		push!(ahmi_val,round.(v.ahmi,digits=3))
	end

    run_stats_names2D = ["nsteps","nchains","Times"]
    stats_names2D = ["mode","mean","var"]
    comparison = ["target","test","diff (abs)","diff (rel)"]
    header  = Vector{Any}(undef,length(stats_names2D)*length(comparison)+3)
    table   = Array{Any}(undef,length(name2D),length(stats_names2D)*length(comparison)+3)
    header[1] = "name"
    table[1:end,1] = name2D

    for i in 1:length(name2D)
        for j in 1:length(stats_names2D)
            header[1+(j-1)*length(comparison)+1]    = string(stats_names2D[j]," ",comparison[1])
            header[1+(j-1)*length(comparison)+2]  = string(stats_names2D[j]," ",comparison[2])
            header[1+(j-1)*length(comparison)+3]  = string(stats_names2D[j]," ",comparison[3])
            header[1+(j-1)*length(comparison)+4]  = string(stats_names2D[j]," ",comparison[4])
            sample_stat = sample_stats2D[i][j]
            analytical_stat = analytical_stats2D[i][j]
            if ((length(analytical_stat[1]) != length(sample_stat[1]) ) && stats_names2D[j] == "mode")
                diffs = Array{Float64}(undef,length(analytical_stat))
                for k in 1:length(analytical_stat)
                    diffs[k] = sum(abs.(analytical_stat[k] .- sample_stat))
                end
                analytical_stat  = analytical_stat[argmin(diffs)]
            end
            diff_abs = analytical_stat.-sample_stat
            diff_rel = diff_abs./analytical_stat
            table[i,1+(j-1)*length(comparison)+1]  =  round.(analytical_stat,digits=3)
            table[i,1+(j-1)*length(comparison)+2]  =  round.(sample_stat,digits=3)
            table[i,1+(j-1)*length(comparison)+3]  =  round.(diff_abs,digits=3)
            table[i,1+(j-1)*length(comparison)+4]  =  round.(diff_rel,digits=3)
        end
		table[i,length(stats_names2D)*length(comparison)+2] = ks_p_val[i]
		table[i,length(stats_names2D)*length(comparison)+3] = ahmi_val[i]
    end

	header[length(stats_names2D)*length(comparison)+2] = "KS test p-value"
	header[length(stats_names2D)*length(comparison)+3] = "AHMI integral"
	full_table = Array{Any}(undef,length(name2D)+1,length(stats_names2D)*length(comparison)+3)
	full_table[1,:] = header
	full_table[2:end,:] = table
	full_table = permutedims(full_table)
	table = full_table[2:end,:]
	header = full_table[1,:]
	f = open("results/results_2D.txt","w")
    pretty_table(f,table,header)
    close(f)
	fl = open("results/results_2D.tex","w")
    pretty_table(fl,table,header,backend=:latex)
    close(fl)
end

function save_stats_2D(name::Vector{String},run_stats::Vector{Vector{Any}},run_stats_names::Vector{String})
    run_stats_table = reshape(vcat(run_stats...),length(run_stats[1]),length(run_stats))
    table_stats = Any[]
    append!(table_stats,[name])
    for i in 1:length(run_stats_table[:,1])
        append!(table_stats,[run_stats_table[i,:]])
    end
    table_stats = reshape(vcat(table_stats...),length(table_stats[1]),length(table_stats))
	table_stats[1:end,4] = round.(table_stats[1:end,4],digits=0)
	table_stats[1:end,2:end] = Int.(table_stats[1:end,2:end])
	f = open("results/run_stats_2D.txt","w")
    pretty_table(f,table_stats,["name",run_stats_names...])
    close(f)
	fl = open("results/run_stats_2D.tex","w")
    pretty_table(fl,table_stats,["name",run_stats_names...],backend=:latex)
    close(fl)
end

ext: jl


text:
#=
Accurate simple zeros of polynomials in floating point arithmetic
Stef Graillat
Computers and Mathematics with Applications 56 (2008) 1114–1120

   poly has N+1 coefficients a_0,a_1,..,a_N for x^0,x^1,..,x^N

   poly = polynomial(coeffs[a_0,a_1,..,a_N])
   poly(x) = sum[i=0:1:N]( a_i * x^i )          this the the classical Horner scheme

   function classicalHorner{R<:Real}(p::Poly,x::R)
      n = length(p.a)
      s = zeros(R, n)       # one-based indexing
      s[:] = p.a[:]
      for i in (n-1):(-1):1
          s[i] += s[i+1]*x
      end
      s[1]
    end

Algorithms for Accurate, Validated and Fast Polynomial Evaluation
Stef Graillat, Philippe Langlois and Nicolas Louvet
Japan J. Indust. Appl. Math., 26 (2009), 191–214

eftHorner(p_n,x) computes
(i)  the floating point evaluation Horner(p_n,x)
(ii) two polynomials pa, pb, of degree n-1
such that
  Horner(p_n,x), pa, pb = eftHorner(p_n,x)
  p_n(x) == Horner(p_n,x) + (pa + pb)(x)
so
  eftHorner is an errorfree transformation for polynomial evaluation with the Horner algorithm
     (unless there is underflow)
=#

function eftHorner{T<:AbstractFloat}(p::Poly{T},x::T)
   deg = degree(p); n=deg+1
   cofs = coeffs(p)
   pa = zeros(T,n)
   pb = zeros(T,n)
   r = cofs[n]
   for i in deg:(-1):1
      t, pa[i] = eftMul(r, x)
      r, pb[i] = eftAdd(t, cofs[i])
    end
    r, pa, pb
end

# expects length(a) == length(b), length(a)>0
function sumHorner{T<:AbstractFloat}(a::Vector{T}, b::Vector{T}, x::T)
   n = length(a)
   if ((n == 0)|(n != length(b)))
      throw(ErrorException("vectors must be of the same nonzero length"))
   end
   r = a[n]+b[n]
   for i in (n-1): -1: 1
     r = r * x + (a[i]+b[i])
   end
   r
end

function csdHornerA{T<:AbstractFloat}(p::Poly{T}, x::T)
    r, pa, pb = eftHorner(p,x)
    c = sumHorner(pa,pb,x)
    r + c
end

# as accurate as if computed at twice the working precision and then rounded to working precision
function csdHorner{T<:AbstractFloat}(p::Poly{T}, x::T)
    r, pa, pb = eftHorner(p,x)
    pab = Poly(pa + pb)
    c = polyval(pab, x)
    r + c
end


text:
@testset "examples" begin

function find_sources(path::String, sources=String[])
    if isdir(path)
        for entry in readdir(path)
            find_sources(joinpath(path, entry), sources)
        end
    elseif endswith(path, ".jl")
        push!(sources, path)
    end
    sources
end

examples_dir = joinpath(@__DIR__, "..", "examples")
examples = find_sources(examples_dir)
filter!(file -> readline(file) != "# EXCLUDE FROM TESTING", examples)
filter!(file -> !occursin("Kaleidoscope", file), examples)

cd(examples_dir) do
    examples = relpath.(examples, Ref(examples_dir))
    @testset for example in examples
        cmd = Base.julia_cmd()
        if Base.JLOptions().project != C_NULL
            cmd = `$cmd --project=$(unsafe_string(Base.JLOptions().project))`
        end

        @test success(pipeline(`$cmd $example`, stderr=stderr))
    end
end

end


text:
using AbstractGPs
using BayesianLinearRegressors
using Bijectors
using LinearAlgebra
using Random
using Test
using WarpedRegressors
using Zygote

@testset "WarpedRegressors.jl" begin
    include("warped_regressor.jl")
    include("extras.jl")
end


text:
export BrickletLaserRangeFinderDistanceCallbackThreshold
struct BrickletLaserRangeFinderDistanceCallbackThreshold
    option::Char
    min::Integer
    max::Integer
end

export BrickletLaserRangeFinderVelocityCallbackThreshold
struct BrickletLaserRangeFinderVelocityCallbackThreshold
    option::Char
    min::Integer
    max::Integer
end

export BrickletLaserRangeFinderMovingAverage
struct BrickletLaserRangeFinderMovingAverage
    distance_average_length::Integer
    velocity_average_length::Integer
end

export BrickletLaserRangeFinderConfiguration
struct BrickletLaserRangeFinderConfiguration
    acquisition_count::Integer
    enable_quick_termination::Bool
    threshold_value::Integer
    measurement_frequency::Integer
end

export BrickletLaserRangeFinderIdentity
struct BrickletLaserRangeFinderIdentity
    uid::String
    connected_uid::String
    position::Char
    hardware_version::Vector{Integer}
    firmware_version::Vector{Integer}
    device_identifier::Integer
end

export BrickletLaserRangeFinder
"""
Measures distance up to 40m with laser light
"""
mutable struct BrickletLaserRangeFinder <: TinkerforgeDevice
    ipcon::IPConnection
    deviceInternal::PyObject

    """
    Creates an object with the unique device ID *uid* and adds it to
    the IP Connection *ipcon*.
    """
    function BrickletLaserRangeFinder(uid::String, ipcon::IPConnection)
        package = pyimport("tinkerforge.bricklet_laser_range_finder")
        deviceInternal = package.BrickletLaserRangeFinder(uid, ipcon.ipconInternal)

        return new(ipcon, deviceInternal)
    end
end

export get_distance
"""
    $(SIGNATURES)

Returns the measured distance.

Sensor hardware version 1 (see :func:`Get Sensor Hardware Version`) cannot
measure distance and velocity at the same time. Therefore, the distance mode
has to be enabled using :func:`Set Mode`.
Sensor hardware version 3 can measure distance and velocity at the same
time. Also the laser has to be enabled, see :func:`Enable Laser`.

If you want to get the distance periodically, it is recommended to
use the :cb:`Distance` callback and set the period with
:func:`Set Distance Callback Period`.
"""
function get_distance(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_distance()
end

export get_velocity
"""
    $(SIGNATURES)

Returns the measured velocity.

Sensor hardware version 1 (see :func:`Get Sensor Hardware Version`) cannot
measure distance and velocity at the same time. Therefore, the velocity mode
has to be enabled using :func:`Set Mode`.
Sensor hardware version 3 can measure distance and velocity at the same
time, but the velocity measurement only produces stables results if a fixed
measurement rate (see :func:`Set Configuration`) is configured. Also the laser
has to be enabled, see :func:`Enable Laser`.

If you want to get the velocity periodically, it is recommended to
use the :cb:`Velocity` callback and set the period with
:func:`Set Velocity Callback Period`.
"""
function get_velocity(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_velocity()
end

export set_distance_callback_period
"""
    $(SIGNATURES)

Sets the period with which the :cb:`Distance` callback is triggered
periodically. A value of 0 turns the callback off.

The :cb:`Distance` callback is only triggered if the distance value has
changed since the last triggering.
"""
function set_distance_callback_period(device::BrickletLaserRangeFinder, period)
    device.deviceInternal.set_distance_callback_period(period)
end

export get_distance_callback_period
"""
    $(SIGNATURES)

Returns the period as set by :func:`Set Distance Callback Period`.
"""
function get_distance_callback_period(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_distance_callback_period()
end

export set_velocity_callback_period
"""
    $(SIGNATURES)

Sets the period with which the :cb:`Velocity` callback is triggered
periodically. A value of 0 turns the callback off.

The :cb:`Velocity` callback is only triggered if the velocity value has
changed since the last triggering.
"""
function set_velocity_callback_period(device::BrickletLaserRangeFinder, period)
    device.deviceInternal.set_velocity_callback_period(period)
end

export get_velocity_callback_period
"""
    $(SIGNATURES)

Returns the period as set by :func:`Set Velocity Callback Period`.
"""
function get_velocity_callback_period(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_velocity_callback_period()
end

export set_distance_callback_threshold
"""
    $(SIGNATURES)

Sets the thresholds for the :cb:`Distance Reached` callback.

The following options are possible:

.. csv-table::
 :header: "Option", "Description"
 :widths: 10, 100

 "'x'",    "Callback is turned off"
 "'o'",    "Callback is triggered when the distance value is *outside* the min and max values"
 "'i'",    "Callback is triggered when the distance value is *inside* the min and max values"
 "'<'",    "Callback is triggered when the distance value is smaller than the min value (max is ignored)"
 "'>'",    "Callback is triggered when the distance value is greater than the min value (max is ignored)"
"""
function set_distance_callback_threshold(device::BrickletLaserRangeFinder, option, min, max)
    device.deviceInternal.set_distance_callback_threshold(option, min, max)
end

export get_distance_callback_threshold
"""
    $(SIGNATURES)

Returns the threshold as set by :func:`Set Distance Callback Threshold`.
"""
function get_distance_callback_threshold(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_distance_callback_threshold()
end

export set_velocity_callback_threshold
"""
    $(SIGNATURES)

Sets the thresholds for the :cb:`Velocity Reached` callback.

The following options are possible:

.. csv-table::
 :header: "Option", "Description"
 :widths: 10, 100

 "'x'",    "Callback is turned off"
 "'o'",    "Callback is triggered when the velocity is *outside* the min and max values"
 "'i'",    "Callback is triggered when the velocity is *inside* the min and max values"
 "'<'",    "Callback is triggered when the velocity is smaller than the min value (max is ignored)"
 "'>'",    "Callback is triggered when the velocity is greater than the min value (max is ignored)"
"""
function set_velocity_callback_threshold(device::BrickletLaserRangeFinder, option, min, max)
    device.deviceInternal.set_velocity_callback_threshold(option, min, max)
end

export get_velocity_callback_threshold
"""
    $(SIGNATURES)

Returns the threshold as set by :func:`Set Velocity Callback Threshold`.
"""
function get_velocity_callback_threshold(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_velocity_callback_threshold()
end

export set_debounce_period
"""
    $(SIGNATURES)

Sets the period with which the threshold callbacks

* :cb:`Distance Reached`,
* :cb:`Velocity Reached`,

are triggered, if the thresholds

* :func:`Set Distance Callback Threshold`,
* :func:`Set Velocity Callback Threshold`,

keep being reached.
"""
function set_debounce_period(device::BrickletLaserRangeFinder, debounce)
    device.deviceInternal.set_debounce_period(debounce)
end

export get_debounce_period
"""
    $(SIGNATURES)

Returns the debounce period as set by :func:`Set Debounce Period`.
"""
function get_debounce_period(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_debounce_period()
end

export set_moving_average
"""
    $(SIGNATURES)

Sets the length of a `moving averaging <https://en.wikipedia.org/wiki/Moving_average>`__
for the distance and velocity.

Setting the length to 0 will turn the averaging completely off. With less
averaging, there is more noise on the data.
"""
function set_moving_average(device::BrickletLaserRangeFinder, distance_average_length, velocity_average_length)
    device.deviceInternal.set_moving_average(distance_average_length, velocity_average_length)
end

export get_moving_average
"""
    $(SIGNATURES)

Returns the length moving average as set by :func:`Set Moving Average`.
"""
function get_moving_average(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_moving_average()
end

export set_mode
"""
    $(SIGNATURES)

.. note::
 This function is only available if you have a LIDAR-Lite sensor with hardware
 version 1. Use :func:`Set Configuration` for hardware version 3. You can check
 the sensor hardware version using :func:`Get Sensor Hardware Version`.

The LIDAR-Lite sensor (hardware version 1) has five different modes. One mode is
for distance measurements and four modes are for velocity measurements with
different ranges.

The following modes are available:

* 0: Distance is measured with resolution 1.0 cm and range 0-4000 cm
* 1: Velocity is measured with resolution 0.1 m/s and range is 0-12.7 m/s
* 2: Velocity is measured with resolution 0.25 m/s and range is 0-31.75 m/s
* 3: Velocity is measured with resolution 0.5 m/s and range is 0-63.5 m/s
* 4: Velocity is measured with resolution 1.0 m/s and range is 0-127 m/s
"""
function set_mode(device::BrickletLaserRangeFinder, mode)
    device.deviceInternal.set_mode(mode)
end

export get_mode
"""
    $(SIGNATURES)

Returns the mode as set by :func:`Set Mode`.
"""
function get_mode(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_mode()
end

export enable_laser
"""
    $(SIGNATURES)

Activates the laser of the LIDAR.

We recommend that you wait 250ms after enabling the laser before
the first call of :func:`Get Distance` to ensure stable measurements.
"""
function enable_laser(device::BrickletLaserRangeFinder)
    device.deviceInternal.enable_laser()
end

export disable_laser
"""
    $(SIGNATURES)

Deactivates the laser of the LIDAR.
"""
function disable_laser(device::BrickletLaserRangeFinder)
    device.deviceInternal.disable_laser()
end

export is_laser_enabled
"""
    $(SIGNATURES)

Returns *true* if the laser is enabled, *false* otherwise.
"""
function is_laser_enabled(device::BrickletLaserRangeFinder)
    return device.deviceInternal.is_laser_enabled()
end

export get_sensor_hardware_version
"""
    $(SIGNATURES)

Returns the LIDAR-Lite hardware version.

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function get_sensor_hardware_version(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_sensor_hardware_version()
end

export set_configuration
"""
    $(SIGNATURES)

.. note::
 This function is only available if you have a LIDAR-Lite sensor with hardware
 version 3. Use :func:`Set Mode` for hardware version 1. You can check
 the sensor hardware version using :func:`Get Sensor Hardware Version`.

The **Acquisition Count** defines the number of times the Laser Range Finder Bricklet
will integrate acquisitions to find a correlation record peak. With a higher count,
the Bricklet can measure longer distances. With a lower count, the rate increases. The
allowed values are 1-255.

If you set **Enable Quick Termination** to true, the distance measurement will be terminated
early if a high peak was already detected. This means that a higher measurement rate can be achieved
and long distances can be measured at the same time. However, the chance of false-positive
distance measurements increases.

Normally the distance is calculated with a detection algorithm that uses peak value,
signal strength and noise. You can however also define a fixed **Threshold Value**.
Set this to a low value if you want to measure the distance to something that has
very little reflection (e.g. glass) and set it to a high value if you want to measure
the distance to something with a very high reflection (e.g. mirror). Set this to 0 to
use the default algorithm. The other allowed values are 1-255.

Set the **Measurement Frequency** to force a fixed measurement rate. If set to 0,
the Laser Range Finder Bricklet will use the optimal frequency according to the other
configurations and the actual measured distance. Since the rate is not fixed in this case,
the velocity measurement is not stable. For a stable velocity measurement you should
set a fixed measurement frequency. The lower the frequency, the higher is the resolution
of the calculated velocity. The allowed values are 10Hz-500Hz (and 0 to turn the fixed
frequency off).

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function set_configuration(device::BrickletLaserRangeFinder, acquisition_count, enable_quick_termination, threshold_value, measurement_frequency)
    device.deviceInternal.set_configuration(acquisition_count, enable_quick_termination, threshold_value, measurement_frequency)
end

export get_configuration
"""
    $(SIGNATURES)

Returns the configuration as set by :func:`Set Configuration`.

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function get_configuration(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_configuration()
end

export get_identity
"""
    $(SIGNATURES)

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port).
A Bricklet connected to an :ref:`Isolator Bricklet <isolator_bricklet>` is always at
position 'z'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|
"""
function get_identity(device::BrickletLaserRangeFinder)
    return device.deviceInternal.get_identity()
end

export register_callback
"""
Registers the given *function* with the given *callback_id*.
"""
function register_callback(device::BrickletLaserRangeFinder, callback_id, function_)
    if isnothing(function_)
        device.registered_callbacks.pop(callback_id, None)
    else
        device.registered_callbacks[callback_id] = function_
    end
end


text:
function NormCtx(O::NfAbsOrd, nb::Int, normal::Bool = false)
  if normal
    return NormCtx_split(O, nb)
  else
    return NormCtx_gen(O, nb)
  end
end

function norm(m::fmpz_mat, NC::NormCtx_split, div::fmpz = fmpz(1))
  l = Array{fmpz, 1}()
  for i = 1:length(NC.lp)
    ccall((:fmpz_mat_get_nmod_mat, libflint), Cvoid, (Ref{gfp_mat}, Ref{fmpz_mat}), NC.mp[i], m)
    mul!(NC.np[i], NC.mp[i], NC.lC[i])
    n = NC.np[i]
    p = n[1,1]
    for j=2:ncols(m)
      p *= n[1,j]
    end
    push!(l, lift(inv(NC.lR[i](div))*p))
  end
  no = crt_signed(l, NC.e)
  if nbits(no) > NC.nb - 10
    return nothing
  else
    return no
  end
end

#not used.
function norms(a::fmpz_mat, NC::NormCtx_split, div::fmpz = fmpz(1))
  no = Array{fmpz, 1}()
  nr = Array{gfp_mat, 1}()
  for i=1:length(NC.lp)
    n = matrix(NC.lR[i], a)*NC.lC[i]
    m = zero_matrix(NC.lR[i], nrows(a), 1)
    for k=1:nrows(n)
      for j=2:ncols(n)
        n[k, 1] *= n[k, j]
      end
      m[k, 1] = n[k, 1]*inv(NC.lR[i](div))
    end
    push!(nr, m)
  end
  mm = induce_crt(nr, NC.e, true)
end

function norm(a::fmpz_mat, NC::NormCtx_gen, div::fmpz = fmpz(1))
  O = NC.O
  k = nf(O)
  no = numerator(norm_div(O(fmpz[a[1, i] for i = 1:degree(k)]).elem_in_nf, div, NC.nb))
  if nbits(no) > NC.nb-10
    return nothing
  else
    return no
  end
end

function class_group_small_lll_elements_relation_start(clg::ClassGrpCtx{T},
                O::NfOrd; prec::Int = 200, val::Int = 0,
                limit::Int = 0) where {T}
  return class_group_small_lll_elements_relation_start(clg, ideal(O, parent(basis_matrix(O).num)(1)), prec = prec)
end

function class_group_small_lll_elements_relation_start(clg::ClassGrpCtx{T},
                A::NfOrdIdl; prec::Int = 200, val::Int = 0,
                limit::Int = 0) where {T}
  global _start = A
  O = order(A)
  n = degree(O)
  L, Tr = lll(A, prec = prec)
  I = SmallLLLRelationsCtx(zero_matrix(FlintZZ, 1, 1))
  S = Tr*basis_matrix(A, copy = false)
  bd = abs(discriminant(O))*norm(A)^2
  bd = root(bd, n)
  bd *= L.den
  f = Int[i for i = 1:n if compare_index(L.num, i, i, bd) < 0]
  m = div(n, 4)
  if m < 2
    m = n
  end
  while length(f) < m
    f = Int[i for i = 1:n if compare_index(L.num, i, i, bd) < 0]
    bd *= 2
  end
  I.b = Vector{fmpz_mat}(undef, length(f))
  for j = 1:length(f)
    I.b[j] = view(S, f[j]:f[j], 1:ncols(S))
  end
  #println([Float64(numerator(L)[i,i]//denominator(L)*1.0) for i=1:degree(K)])
  #now select a subset that can yield "small" relations, where
  #small means of effective norm <= sqrt(disc)
  I.A = A
  I.elt = zero_matrix(FlintZZ, 1, n)
  return I
end

function class_group_small_lll_elements_relation_next(I::SmallLLLRelationsCtx)
  #the 1st n relations are the basis elements
  if I.cnt < length(I.b)
    I.cnt += 1
    I.elt = I.b[I.cnt]
    return deepcopy(I.b[I.cnt])
  end
  #the next 2*n*(n-1)/2 relations are the ones of weight 2
  #1st b[i] + b[j], all combinations, then b[i]-b[j]
  #it may (or may not) be helpful
  if I.cnt <= length(I.b)^2
    n = length(I.b)
    c = I.cnt - n # we already did n relations in the 1st if
    if c > n*(n-1)/2
      c -= div(n*(n-1), 2)
      s = -1
    else
      s = 1
    end
    i = 1
    while c+i-1 >= n
      c -= (n-i)
      i += 1
    end
    I.cnt += 1
    I.elt = I.b[i] + s*I.b[c+i]
    return I.elt
  end

  if I.cnt > (2*I.bd+1)^div(length(I.b), 2)
    I.bd += 1
  end

  I.cnt += 1
  while true
    # Before: rand!(I.elt, I.b, -I.bd:I.bd, min(length(I.b), 5))
    # TODO: make this non-allocating
    I.elt = rand(-I.bd:I.bd) * rand(I.b)
    for i in 2:min(length(I.b), 5)
      I.elt = I.elt + rand(-I.bd:I.bd) * rand(I.b)
    end
    if !iszero(I.elt)
      return deepcopy(I.elt)
    end
  end
end


text:
using ToroidalHarmonics
using Base.Test


text:
# code reflection entry-points

# TODO: get and disassemble the native binary using oneL0

#
# code_* replacements
#

for method in (:code_typed, :code_warntype, :code_llvm, :code_native)
    # only code_typed doesn't take a io argument
    args = method == :code_typed ? (:job,) : (:io, :job)

    @eval begin
        function $method(io::IO, @nospecialize(func), @nospecialize(types);
                         kernel::Bool=false, kwargs...)
            source = FunctionSpec(func, Base.to_tuple_type(types), kernel)
            target = SPIRVCompilerTarget()
            params = oneAPICompilerParams()
            job = CompilerJob(target, source, params)
            GPUCompiler.$method($(args...); kwargs...)
        end
        $method(@nospecialize(func), @nospecialize(types); kwargs...) =
            $method(stdout, func, types; kwargs...)
    end
end

const code_spirv = code_native

#
# @device_code_* functions
#

export @device_code_lowered, @device_code_typed, @device_code_warntype,
       @device_code_llvm, @device_code_spirv,
       @device_code

@eval $(Symbol("@device_code_lowered")) = $(getfield(GPUCompiler, Symbol("@device_code_lowered")))
@eval $(Symbol("@device_code_typed")) = $(getfield(GPUCompiler, Symbol("@device_code_typed")))
@eval $(Symbol("@device_code_warntype")) = $(getfield(GPUCompiler, Symbol("@device_code_warntype")))
@eval $(Symbol("@device_code_llvm")) = $(getfield(GPUCompiler, Symbol("@device_code_llvm")))
@eval $(Symbol("@device_code_spirv")) = $(getfield(GPUCompiler, Symbol("@device_code_native")))
@eval $(Symbol("@device_code")) = $(getfield(GPUCompiler, Symbol("@device_code")))


text:
struct DeferredProcess
    proc
    filedeps
    fileproducts
end


text:
struct RLBaseEnv{E} <: RLBase.AbstractEnv
    env::E
end

Base.show(io::IO, mime::MIME"text/plain", env::RLBaseEnv{E}) where {E <: AbstractGridWorld} = show(io, mime, env.env)

play!(terminal::REPL.Terminals.UnixTerminal, env::RLBaseEnv{E}, file_name::Union{Nothing, AbstractString}, frame_start_delimiter::Union{Nothing, AbstractString}) where {E <: AbstractGridWorld} = play!(terminal, env.env, file_name, frame_start_delimiter)
play!(env::RLBaseEnv{E}; file_name = nothing, frame_start_delimiter = nothing) where {E <: AbstractGridWorld} = play!(REPL.TerminalMenus.terminal, env.env, file_name, frame_start_delimiter)


text:
using MultipleViewGeometry, Test
using MultipleViewGeometry.ModuleCostFunction
using MultipleViewGeometry.ModuleTypes
using LinearAlgebra
using StaticArrays
using GeometryTypes

# Test for cost functions.

# Test cost function on Fundamental matrix estimation.

𝒳 = [Point3D(x,y,rand(50:100)) for x = -100:5:100 for y = -100:5:100]
𝒳 = 𝒳[1:50:end]

# Specify the coordinate systems of the world, the camera frame and the picture
# plane.
world_basis = (Vec(1.0, 0.0, 0.0), Vec(0.0, 1.0, 0.0), Vec(0.0, 0.0, 1.0))
camera_basis = (Point(0.0, 0.0, 0.0), Vec(-1.0, 0.0, 0.0), Vec(0.0, -1.0, 0.0), Vec(0.0, 0.0, 1.0))
picture_basis = (Point(0.0, 0.0), Vec(-1.0, 0.0), Vec(0.0, -1.0))

# The focal length for both cameras is one.
f = 1
image_width = 640 / 10
image_height = 480 / 10

camera₁ = Pinhole(image_width, image_height, f, camera_basis..., picture_basis...)
camera₂ = Pinhole(image_width, image_height, f, camera_basis..., picture_basis...)

# Rotate and translate camera one.
𝐑₁ = Matrix{Float64}(I,3,3)
𝐭₁ = [-50.0, -2.0, 0.0]
relocate!(camera₁, 𝐑₁, 𝐭₁)

# Rotate and translate camera two.
𝐑₂ = Matrix{Float64}(I,3,3)
𝐭₂ = [50.0, 2.0, 0.0]
relocate!(camera₂, 𝐑₂, 𝐭₂)

𝐑₁′, 𝐭₁′ = ascertain_pose(camera₁, world_basis... )
𝐊₁′ = obtain_intrinsics(camera₁, CartesianSystem())
𝐑₂′, 𝐭₂′ = ascertain_pose(camera₂, world_basis... )
𝐊₂′ = obtain_intrinsics(camera₂, CartesianSystem())

# Camera projection matrices.
𝐏₁ = construct(ProjectionMatrix(),𝐊₁′,𝐑₁′,𝐭₁′)
𝐏₂ = construct(ProjectionMatrix(),𝐊₂′,𝐑₂′,𝐭₂′)

# Set of corresponding points.
ℳ = project(camera₁,𝐏₁,𝒳)
ℳʹ = project(camera₂,𝐏₂,𝒳)

# Construct fundamental matrix from projection matrices.
𝐅 = construct(FundamentalMatrix(), 𝐏₁, 𝐏₂)

# Ensure the estimated and true matrix have the same scale and sign.
𝐅 = 𝐅 / norm(𝐅)
𝐅 = 𝐅 / sign(𝐅[3,1])
𝐟 = vec(𝐅)

Λ₁ =  [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(ℳ)]
Λ₂ =  [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(ℳ)]
Jₐₘₗ =  cost(AML(),FundamentalMatrix(), SVector{9}(𝐟), (Λ₁,Λ₂), (ℳ, ℳʹ))

@test isapprox(Jₐₘₗ, 0.0; atol = 1e-14)

# Verify that the vectorised fundamental matrix is in the null space of X
𝐗 = X(AML(),FundamentalMatrix(), vec(𝐅), (Λ₁,Λ₂), (ℳ, ℳʹ))

# The true parameters should lie in the null space of the matrix X.
@test all(isapprox.(𝐗 * 𝐟, 0.0; atol = 1e-9))

# Verify that the the vectorised fundamental matrix is in the null space of H.
# H represents the Hessian matrix of the AML cost function.
𝐇 = H(AML(),FundamentalMatrix(), vec(𝐅), (Λ₁,Λ₂), (ℳ, ℳʹ))

# The true parameters should lie in the null space of the matrix H.
@test all(isapprox.(𝐇 * 𝐟, 0.0; atol = 1e-9))


text:
"""
    parse_and_number(treestring::S)::Node where S<:AbstractString

--- INTERNAL ---
Parse a newick string and then call set_binary and number_nodes! on the
resulting tree
"""
function parse_and_number(treestring::S)::Node where S<:AbstractString
    p_tree2 = parsing_newick_string(string(treestring))
    set_binary!(p_tree2)
    number_nodes!(p_tree2)
    p_tree2
end # parse_and_number

"""
    nodnums2names(bps::Vector{Tuple}, tree::Node)
        ::Vector{Tuple{Set{String}, Set{String}}}

--- INTERNAL ---
"""
function nodnums2names(bps::Vector{Tuple}, tree::Node
                      )::Vector{Tuple{Set{String}, Set{String}}}

    treebps = Vector{Tuple{Set{String}, Set{String}}}()
    for bipart in bps
        bipartition = []
        for part in bipart
            myset = Set{String}()
            for num in part
                no = find_num(tree, num)
                if no.nchild == 0
                    push!(myset, no.name)
                end # if
            end # for
            push!(bipartition, myset)
        end # for
        push!(treebps, Tuple(bipartition))
    end # for
    treebps
end # nodnums2names

"""
    ASDSF(args::String...; freq::Int64=1, check_leaves::Bool=true,
          min_splits::Float64=0.1, show_progress::Bool=true)::Vector{Float64}

Calculate the average standard deviation of split frequencies for two or more
files containing newick representations of trees. Default frequency is 1 and
by default only trees with the same leafsets are supported. The default minimal
splits threshold is 0.1. The progress bar is activated by default.
"""
function ASDSF(args::String...; freq::Int64=1, check_leaves::Bool=true,
               min_splits::Float64=0.1, show_progress::Bool=true
               )::Vector{Float64}

    length(args) < 2 && throw(ArgumentError("At least two input files are needed."))
    splitsQueue = Accumulator{Tuple{Set{String}, Set{String}}, Int64}()
    splitsQueues = Vector{Accumulator{Tuple{Set{String}, Set{String}}, Int64}}()
    for arg in args
        push!(splitsQueues, Accumulator{Tuple{Set{String}, Set{String}}, Int64}())
    end # for
    iter = zip([eachline(arg) for arg in args]...)
    ASDF_vals = zeros(Int(countlines(args[1]) / freq))
    asdsf_int(splitsQueue, splitsQueues, iter, ASDF_vals, freq, check_leaves,
              min_splits, show_progress)
end # ASDSF

"""
    ASDSF(args::Vector{String}...; freq::Int64=1, check_leaves::Bool=true,
          min_splits::Float64=0.1, show_progress::Bool=true)::Vector{Float64}

Calculate the average standard deviation of split frequencies for two or more
Vectors containing newick representations of trees. Default frequency is 1 and
by default only trees with the same leafsets are supported. The default minimal
splits threshold is 0.1. The progress bar is activated by default.
"""
function ASDSF(args::Vector{String}...; freq::Int64=1, check_leaves::Bool=true,
               min_splits::Float64=0.1, show_progress::Bool=true
               )::Vector{Float64}

    length(args) < 2 && throw(ArgumentError("At least two input arrays are needed."))
    splitsQueue = Accumulator{Tuple{Set{String}, Set{String}}, Int64}()
    splitsQueues = Vector{Accumulator{Tuple{Set{String}, Set{String}}, Int64}}()
    for arg in args
        push!(splitsQueues, Accumulator{Tuple{Set{String}, Set{String}}, Int64}())
    end # for
    iter = zip(args...)
    ASDF_vals = zeros(Int(length(iter) / freq))
    asdsf_int(splitsQueue, splitsQueues, iter, ASDF_vals, freq, check_leaves,
              min_splits, show_progress)
end # ASDSF

"""
    ASDSF(model::ModelChains; freq::Int64=1, check_leaves::Bool=true,
          min_splits::Float64=0.1, show_progress::Bool=true)::Vector{Float64}

Calculate the average standard deviation of split frequencies for the trees in
different chains in a ModelChains object. Default frequency is 1 and by default
only trees with the same leafsets are supported. The default minimal splits
threshold is 0.1. The progress bar is activated by default.
"""
function ASDSF(model::ModelChains; freq::Int64=1, check_leaves::Bool=true,
               min_splits::Float64=0.1, show_progress::Bool=true
               )::Vector{Float64}

    splitsQueue = Accumulator{Tuple{Set{String}, Set{String}}, Int64}()
    splitsQueues = Vector{Accumulator{Tuple{Set{String}, Set{String}}, Int64}}()
    l = size(model.trees, 3)
    for i in 1:l
        push!(splitsQueues, Accumulator{Tuple{Set{String}, Set{String}}, Int64}())
    end # for
    iter = zip([model.trees[:,:,i] for i in 1:l]...)
    ASDF_vals = zeros(Int(length(iter) / freq))
    asdsf_int(splitsQueue, splitsQueues, iter, ASDF_vals, freq, check_leaves,
              min_splits, show_progress)
end # ASDSF

"""
    asdsf_int(splitsQueue, splitsQueues, iter, ASDF_vals, freq, check_leaves
              min_splits)::Vector{Float64}

--- INTERNAL ---
Handles the computation of the Average Standard Deviation of Split Frequencies.
"""
function asdsf_int(splitsQueue, splitsQueues, iter, ASDF_vals, freq,
                   check_leaves, min_splits, show_progress)::Vector{Float64}

    all_keys = Set{Tuple{Set{String},Set{String}}}()
    if show_progress
        p = Progress(length(ASDF_vals))
    end # if
    run::Int64 = 1
    outer::Float64 = 0.0
    inner::Float64 = 0.0
    for (i, lines) in enumerate(iter)
        if mod(i, freq) == 0
            trees = [parse_and_number(tree_string) for tree_string in lines]
            check_leaves && check_leafsets(trees)
            bps = [get_bipartitions(tree) for tree in trees]
            named_bps = [nodnums2names(bp, tree) for (bp, tree) in zip(bps, trees)]
            cmds = [countmap(named_bp) for named_bp in named_bps]
            outer = 0.0
            new_splits = union([keys(cmds[t]) for t in 1:length(trees)]...)
            all_keys = union(all_keys, new_splits)
            for split in new_splits
                inc!(splitsQueue, split)
            end # for
            for split in all_keys
                inner = 0.0
                for (t, tree) in enumerate(trees)
                    if haskey(cmds[t], split)
                        inner += 1 / length(trees)
                        inc!(splitsQueues[t], split)
                    end # if
                end # for
                if any([x[split] / run > min_splits for x in splitsQueues])
                    fre = splitsQueue[split] / run
                    inner += (inner - fre) ^ 2
                    outer += sqrt(inner)
                end # if
            end # for
            ASDF_vals[run] = outer / length(splitsQueue)
            run += 1
            show_progress && ProgressMeter.next!(p)
        end # if
    end # for
    ASDF_vals
end # asdsf_int


text:
abstract type TransitionOperator end

args(r::TransitionOperator) = ()
kwargs(r::TransitionOperator) = NamedTuple()

import Base.==
==(op1::TransitionOperator, op2::TransitionOperator) =
    typeof(op1) == typeof(op2) && args(op1) == args(op2) && kwargs(op1) == kwargs(op2)

is_possible(t, cfg) = false

struct NoArc <: TransitionOperator end
(::NoArc)(cfg) = noarc(cfg)

struct Reduce <: TransitionOperator end
(::Reduce)(cfg) = reduce(cfg)

struct Shift  <: TransitionOperator end
(::Shift)(cfg) = shift(cfg)

struct LeftArc{A<:Tuple,K<:NamedTuple} <: TransitionOperator
    args::A
    kwargs::K

    function LeftArc(args...; kwargs...)
        A = typeof(args)
        ks = kwargs.data
        K = typeof(ks)
        new{A,K}(args, ks)
    end
end

(op::LeftArc)(cfg) = leftarc(cfg, op.args...; op.kwargs...)

name(::LeftArc) = "LeftArc"
args(op::LeftArc) = op.args
kwargs(op::LeftArc) = op.kwargs

struct RightArc{A<:Tuple,K<:NamedTuple} <: TransitionOperator
    args::A
    kwargs::K

    function RightArc(args...; kwargs...)
        A = typeof(args)
        ks = kwargs.data
        K = typeof(ks)
        new{A,K}(args, ks)
    end
end

(op::RightArc)(cfg) = rightarc(cfg, op.args...; op.kwargs...)

name(::RightArc) = "RightArc"
args(op::RightArc) = op.args
kwargs(op::RightArc) = op.kwargs

function Base.show(io::IO, op::Union{LeftArc,RightArc})
    as = join(args(op), ", ")
    kwas = join(["$a=$v" for (a, v) in Dict(zip(keys(op.kwargs), values(op.kwargs)))], ", ")
    T = name(op)
    if length(op.kwargs) > 0
        print(io, "$T($as; $kwas)")
    else
        print(io, "$T($as)")
    end
end

abstract type AbstractParserConfiguration end

deptree(cfg::AbstractParserConfiguration) = deptree(tokens(cfg))

"""
    stacktoken(cfg, i)

Return the token at stack index `i` (starting at 1).
"""
function stacktoken(cfg, i=1)
    stk = stack(cfg)
    stklen = length(stk)
    s_index = stacklength(cfg) - i + 1
    if 1 <= s_index <= stacklength(cfg)
        a_index = stk[s_index]
        a_index == 0 ? ROOT : token(cfg, a_index)
    else
        Token()
    end
end

"""
    buffertoken(cfg, i)

Return the token at buffer index `i` (starting at 1).
"""
buffertoken(cfg, i) =
    1 <= i <= bufferlength(cfg) ? token(cfg, buffer(cfg)[i]) : Token()

function stack_buffer_config(T, words)
    σ = [0]
    β = collect(1:length(words))
    A = Token.(words)
    return T(σ, β, A)
end

function stack_buffer_config(T, gold::DependencyTree)
    σ = [0]
    β = collect(1:length(gold))
    A = [Token(t; head=-1) for t in gold]
    T(σ, β, A)
end

stacklength(cfg) = length(stack(cfg))
bufferlength(cfg) = length(buffer(cfg))

token(cfg::AbstractParserConfiguration, i) =
    iszero(i) ? ROOT : i == -1   ? Token(nothing) : tokens(cfg)[i]

tokens(cfg::AbstractParserConfiguration, is) =
    [token(cfg, i) for i in is if 0 <= i <= length(tokens(cfg))]

popstack(cfg::AbstractParserConfiguration, n=1) = popstack(stack(cfg), n)

function popstack(stk::Vector, n=1)
    sh = [s for s in stk[end-n+1:end]]
    st = length(stk) >= n ? [s for s in stk[1:end-n]] : Int[]
    return (st, sh...)
end

shiftbuffer(cfg::AbstractParserConfiguration, n=1) = shiftbuffer(buffer(cfg), n)

function shiftbuffer(buf, n=1)
    @assert length(buf) >= n "can't shift $n from buffer $buf"
    bh = [b for b in buf[1:n]]
    be = length(buf) > n ? [b for b in buf[n+1:end]] : Int[]
    return (bh..., be)
end

# LeftArc in ArcStandard
# function leftarc_reduce2(cfg, args...; kwargs...)
function leftarc_reduce2(stack, buffer, A, args...; kwargs...)
    # assert a head-dependent relation between the word at the top of
    # the stack and the word directly beneath it; remove the lower
    # word from the stack
    @assert length(stack) >= 2
    σ, s1, s0 = stack[1:end-2], stack[end-1], stack[end]
    A = copy(A)
    A[s1] = Token(A[s1]; head=s0, kwargs...)
    return [σ ; s0], buffer, A
end

# LeftArc in ArcEager
# LeftArc in ArcHybrid
function leftarc_reduce(stack, buffer, A, args...; kwargs...)
    # Assert a head-dependent relation between the word at the front
    # of the input buffer and the word at the top of the stack; pop
    # the stack.
    (σ, s) = popstack(stack)
    A = copy(A)
    if s > 0
        b, β = shiftbuffer(buffer)
        A[s] = Token(A[s]; head=b, kwargs...)
    end
    return σ, buffer, A
end

# RightArc in ArcHybrid
# RightArc in ArcStandard
function rightarc_reduce(stack, buffer, A, args...; kwargs...)
    # assert a head-dependent relation btwn the 2nd word on the stack
    # and the word on top; remove the word at the top of the stack
    # σ, s1, s0 = σs1s0(cfg)
    σ, s1, s0 = popstack(stack, 2)
    A = copy(A)
    if s0 > 0
        A[s0] = Token(A[s0]; head=s1, kwargs...)
    end
    return [σ ; s1], buffer, A
end

# RightArc in ArcEager
function rightarc_shift(stack, buffer, A, args...; kwargs...)
    # Assert a head-dependent relation between the word on the top of
    # the σ and the word at front of the input buffer; shift the
    # word at the front of the input buffer to the stack.
    (σ, s), (b, β) = popstack(stack), shiftbuffer(buffer)
    A = copy(A)
    A[b] = Token(A[b]; head=s, kwargs...)
    return [stack ; b], β, A
end

# Reduce in ArcEager
function Base.reduce(stack::Vector, buffer::Vector, A::Vector)
    # Pop the stack.
    return stack[1:end-1], buffer, A
end

# Shift in ArcEager
# Shift in ArcHybrid
# Shift in ArcStandard
function shift(stack, buffer, A)
    # Remove the word from the front of the input buffer and push it
    # onto the stack.
    b, β = shiftbuffer(buffer)
    return [stack ; b], β, A
end


text:
#!/usr/bin/julia

# FIXME I AM VERY SLOW

include("read.jl")

function extract(paths = ARGS[1:end])
	headers = []
	samples = []
	for fl in paths
		println("Extracting ... ... ... ", basename(fl))
		vcf = read(fl)
		for line in vcf
			if ismatch(r"^#|CHROM", line)
				push!(headers, line)
			else
				push!(samples, line)
			end
		end
	end
	return headers, samples
end

function merge_names(headers)
	def = []
	sampl = []
	for entry in headers
		line  = split(entry, "\t")
		push!(def, line[1:4])
		push!(sampl,line[5:end])
	end
	def = unique(def)
	sampl = unique(sampl)
	names = (def, sampl)
	return names
end

function merge_vcf(samples)
	out = []
	def = []
	for entry in samples
		line  = split(entry, "\t")
		push!(def, line[1:4])
		end
	def = unique(def)

	 for item in def
		push!(out, item)
		for ln in samples
			cols = split(ln, "\t")
			if item == cols[1:4]
				push!(out, cols[5:end])
			end
		end
		push!(out, "\n")
	end
	return out
end

headers, samples = extract()
head = merge_names(headers)
data = merge_vcf(samples)

println(head)
ln = 1
while ln <=lenght(data)
	println(data[ln])
	ln += 1
end


text:
using ExportNURBS
using Test
using Random
using BasicBSpline
using Colors

@testset "ExportNURBS.jl" begin
    Random.seed!(42)

    @testset "1d2d" begin
        p = 3 # degree of polynomial
        k = Knots(1:12) # knot vector
        P = FastBSplineSpace(p, k) # B-spline space
        rand_a = [randn(2) for i in 1:dim(P)] / 2
        a = [[2 * i - 6, 0] for i in 1:dim(P)] + rand_a # random generated control points
        M = BSplineCurve([P], a) # Define B-spline manifold

        @testset "luxor-svg" begin
            save_svg("1d2d.svg",M)
            @test isfile("1d2d.svg")
        end
        @testset "luxor-png" begin
            save_png("1d2d.png",M)
            @test isfile("1d2d.png")
        end
    end

    @testset "2d2d" begin
        p = 3 # degree of polynomial
        k = Knots(1:12) # knot vector
        P = FastBSplineSpace(p, k) # B-spline space
        rand_a = [randn(2) for i in 1:dim(P), j in 1:dim(P)] / 2
        a = [[2 * i - 6, 2 * j - 6] for i in 1:dim(P), j in 1:dim(P)] + rand_a # random generated control points
        M = BSplineSurface([P, P], a) # Define B-spline manifold

        @testset "luxor-svg" begin
            save_svg("2d2d.svg",M)
            @test isfile("2d2d.svg")
        end
        @testset "luxor-png" begin
            save_png("2d2d.png",M)
            @test isfile("2d2d.png")
        end
        @testset "luxor-color-png" begin
            color(u) = rand(RGB)
            save_png("2d2d_color.png", M, color)
            @test isfile("2d2d_color.png")
        end
    end

    @testset "1d3d" begin
        p = 4 # degree of polynomial
        k = Knots(rand(12)) # knot vector
        P = FastBSplineSpace(p, k) # B-spline space
        rand_a = [randn(3) for i in 1:dim(P)]
        a = [[2 * i - 6, 2 * i - 6, 0] for i in 1:dim(P)] + rand_a # random generated control points
        M = BSplineCurve([P], a) # Define B-spline manifold

        @testset "povray" begin
            save_pov("1d3d.inc",M)
            @test isfile("1d3d.inc")
        end
    end

    @testset "2d3d" begin
        p = 3 # degree of polynomial
        k = Knots(1:12) # knot vector
        P = FastBSplineSpace(p, k) # B-spline space
        rand_a = [randn(3) for i in 1:dim(P), j in 1:dim(P)] / 2
        a = [[2 * i - 6, 2 * j - 6, 0] for i in 1:dim(P), j in 1:dim(P)] + rand_a # random generated control points
        M = BSplineSurface([P, P], a) # Define B-spline manifold

        @testset "povray" begin
            save_pov("2d3d.inc", M , maincolor=RGBA(0,1,1,0.95))
            @test isfile("2d3d.inc")
        end
    end

    @testset "3d3d" begin
        p = 3 # degree of polynomial
        k = Knots(1:12) # knot vector
        P = FastBSplineSpace(p, k) # B-spline space
        rand_a = [randn(3) for i in 1:dim(P), j in 1:dim(P), k in 1:dim(P)] / 4
        a = [[2 * i - 6, 2 * j - 6, 2 * k - 6] for i in 1:dim(P), j in 1:dim(P), k in 1:dim(P)] + rand_a # random generated control points
        M = BSplineSolid([P, P, P], a) # Define B-spline manifold

        @testset "povray" begin
            save_pov("3d3d.inc", M, maincolor=RGBA(0.5,0.1,1,0.8))
            @test isfile("3d3d.inc")
        end
    end
end


text:
using WiltonInts84

using Base.Test
using FixedSizeArrays

include("num_quad.jl")

if !isdefined(:record)
    record = false
end

N = 2
T = Float64

J = Vector{Vec{N+3,T}}()
I = similar(J)
L = Vector{Vec{N+3,Vec{3,T}}}()
K = similar(L)

v1 = Vec(1.0, 0.0, 0.0)
v2 = Vec(0.0, 1.0, 0.0)
v3 = Vec(0.0, 0.0, 0.0)
n = normalize(cross(v1-v3,v2-v3))

X = [
    (v1 + v2 + v3)/3               + 20n, # h > 0, inside
    (1-1.5)v1 + 1.5v3              + 20n, # h > 0, on extension [v1,v3]
    0.5v1 + 0.5v3                  + 20n, # h > 0, on the interior of [v1,v3]
    -0.5v1 + 0.5v2 + (1-0.5-0.5)v3 + 20n, # h > 0, outside
    v2                             + 20n, # h > 0, on top of v2

    (v1 + v2 + v3)/3               - 20n, # h < 0, inside
    (1-1.5)v1 + 1.5v3              - 20n, # h < 0, on extension [v1,v3]
    0.5v1 + 0.5v3                  - 20n, # h < 0, on the interior of [v1,v3]
    -0.5v1 + 0.5v2 + (1-0.5-0.5)v3 - 20n, # h < 0, outside
    v2                             - 20n, # h < 0, on top of v2

    (1-1.5)v1 + 1.5v3              - 0n,  # h = 0, on extension [v1,v3]
    -0.5v1 + 0.5v2 + (1-0.5-0.5)v3 - 0n,  # h = 0, outside

    (v1 + v2 + v3)/3               - 0n,  # h = 0, inside
]

for (i,x) in enumerate(X)
    A, B = wiltonints(v1,v2,v3,x,Val{N})
    push!(I,A); push!(K,B);
    if record
        P, Q = dblquadints1(v1,v2,v3,x,Val{N})
        push!(J,P); push!(L,Q);
    end
end

using JLD
fn = joinpath(dirname(@__FILE__),"triangleints.jld")

# convert data to arrays to avoid JLD bug
I = T[I[m][n] for m in eachindex(I), n in 1:length(eltype(I))]
K = T[K[m][n][p] for m in eachindex(K), n in 1:length(eltype(K)), p in 1:3]

if record == true
    J = T[J[m][n] for m in eachindex(J), n in 1:length(eltype(J))]
    L = T[L[m][n][p] for m in eachindex(L), n in 1:length(eltype(L)), p in 1:3]
    jldopen(fn,"w") do file
        write(file, "J", J)
        write(file, "L", L)
    end
else
    J,L = jldopen(fn,"r") do file
        read(file, "J"), read(file, "L")
    end
end

# The actual tests
ϵ = 1.0e-4
for i = 1 : size(I,1)-1
    for j = 1 : size(I,2)
        @test !isnan(I[i,j])
        @test !isnan(K[i,j,1])
        @test !isnan(K[i,j,2])
        @test !isnan(K[i,j,3])
        @test !isinf(I[i,j])
        @test !isinf(K[i,j,1])
        @test !isinf(K[i,j,2])
        @test !isinf(K[i,j,3])
        @test nearlyequal(I[i,j], J[i,j], ϵ)
        @test nearlyequal(vec(K[i,j,:]), vec(L[i,j,:]), ϵ)
    end
end

ϵ = 2.0e-4
for i = size(I,1)
    for j = 1 : size(I,2)
        @test !isnan(I[i,j])
        @test !isnan(K[i,j,1])
        @test !isnan(K[i,j,2])
        @test !isnan(K[i,j,3])
        @test !isinf(I[i,j])
        @test !isinf(K[i,j,1])
        @test !isinf(K[i,j,2])
        @test !isinf(K[i,j,3])
        @test nearlyequal(I[i,j], J[i,j], ϵ)
        j == 1 && continue # dblquad cannot approx Cauchy principal values...
        @test nearlyequal(vec(K[i,j,:]), vec(L[i,j,:]), ϵ)
    end
end