text:
#==============================================================================#
# DAX.jl
#
# This file is generated from:
# https://github.com/aws/aws-sdk-js/blob/master/apis/dax-2017-04-19.normal.json
#==============================================================================#

__precompile__()

module DAX

using AWSCore

"""
    using AWSSDK.DAX.create_cluster
    create_cluster([::AWSConfig], arguments::Dict)
    create_cluster([::AWSConfig]; ClusterName=, NodeType=, ReplicationFactor=, IamRoleArn=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "CreateCluster", arguments::Dict)
    dax([::AWSConfig], "CreateCluster", ClusterName=, NodeType=, ReplicationFactor=, IamRoleArn=, <keyword arguments>)

# CreateCluster Operation

Creates a DAX cluster. All nodes in the cluster run the same DAX caching software.

# Arguments

## `ClusterName = ::String` -- *Required*
The cluster identifier. This parameter is stored as a lowercase string.

**Constraints:**

*   A name must contain from 1 to 20 alphanumeric characters or hyphens.

*   The first character must be a letter.

*   A name cannot end with a hyphen or contain two consecutive hyphens.

## `NodeType = ::String` -- *Required*
The compute and memory capacity of the nodes in the cluster.

## `Description = ::String`
A description of the cluster.

## `ReplicationFactor = ::Int` -- *Required*
The number of nodes in the DAX cluster. A replication factor of 1 will create a single-node cluster, without any read replicas. For additional fault tolerance, you can create a multiple node cluster with one or more read replicas. To do this, set *ReplicationFactor* to 2 or more.

**Note**
> AWS recommends that you have at least two read replicas per cluster.

## `AvailabilityZones = [::String, ...]`
The Availability Zones (AZs) in which the cluster nodes will be created. All nodes belonging to the cluster are placed in these Availability Zones. Use this parameter if you want to distribute the nodes across multiple AZs.

## `SubnetGroupName = ::String`
The name of the subnet group to be used for the replication group.

**Important**
> DAX clusters can only run in an Amazon VPC environment. All of the subnets that you specify in a subnet group must exist in the same VPC.

## `SecurityGroupIds = [::String, ...]`
A list of security group IDs to be assigned to each node in the DAX cluster. (Each of the security group ID is system-generated.)

If this parameter is not specified, DAX assigns the default VPC security group to each node.

## `PreferredMaintenanceWindow = ::String`
Specifies the weekly time range during which maintenance on the DAX cluster is performed. It is specified as a range in the format ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). The minimum maintenance window is a 60 minute period. Valid values for `ddd` are:

*   `sun`

*   `mon`

*   `tue`

*   `wed`

*   `thu`

*   `fri`

*   `sat`

Example: `sun:05:00-sun:09:00`

**Note**
> If you don't specify a preferred maintenance window when you create or modify a cache cluster, DAX assigns a 60-minute maintenance window on a randomly selected day of the week.

## `NotificationTopicArn = ::String`
The Amazon Resource Name (ARN) of the Amazon SNS topic to which notifications will be sent.

**Note**
> The Amazon SNS topic owner must be same as the DAX cluster owner.

## `IamRoleArn = ::String` -- *Required*
A valid Amazon Resource Name (ARN) that identifies an IAM role. At runtime, DAX will assume this role and use the role's permissions to access DynamoDB on your behalf.

## `ParameterGroupName = ::String`
The parameter group to be associated with the DAX cluster.

## `Tags = [[ ... ], ...]`
A set of tags to associate with the DAX cluster.
```
 Tags = [[
        "Key" =>  ::String,
        "Value" =>  ::String
    ], ...]
```

# Returns

`CreateClusterResponse`

# Exceptions

`ClusterAlreadyExistsFault`, `InvalidClusterStateFault`, `InsufficientClusterCapacityFault`, `SubnetGroupNotFoundFault`, `InvalidParameterGroupStateFault`, `ParameterGroupNotFoundFault`, `ClusterQuotaForCustomerExceededFault`, `NodeQuotaForClusterExceededFault`, `NodeQuotaForCustomerExceededFault`, `InvalidVPCNetworkStateFault`, `TagQuotaPerResourceExceeded`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/CreateCluster)
"""
@inline create_cluster(aws::AWSConfig=default_aws_config(); args...) = create_cluster(aws, args)

@inline create_cluster(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "CreateCluster", args)

@inline create_cluster(args) = create_cluster(default_aws_config(), args)

"""
    using AWSSDK.DAX.create_parameter_group
    create_parameter_group([::AWSConfig], arguments::Dict)
    create_parameter_group([::AWSConfig]; ParameterGroupName=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "CreateParameterGroup", arguments::Dict)
    dax([::AWSConfig], "CreateParameterGroup", ParameterGroupName=, <keyword arguments>)

# CreateParameterGroup Operation

Creates a new parameter group. A parameter group is a collection of parameters that you apply to all of the nodes in a DAX cluster.

# Arguments

## `ParameterGroupName = ::String` -- *Required*
The name of the parameter group to apply to all of the clusters in this replication group.

## `Description = ::String`
A description of the parameter group.

# Returns

`CreateParameterGroupResponse`

# Exceptions

`ParameterGroupQuotaExceededFault`, `ParameterGroupAlreadyExistsFault`, `InvalidParameterGroupStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/CreateParameterGroup)
"""
@inline create_parameter_group(aws::AWSConfig=default_aws_config(); args...) = create_parameter_group(aws, args)

@inline create_parameter_group(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "CreateParameterGroup", args)

@inline create_parameter_group(args) = create_parameter_group(default_aws_config(), args)

"""
    using AWSSDK.DAX.create_subnet_group
    create_subnet_group([::AWSConfig], arguments::Dict)
    create_subnet_group([::AWSConfig]; SubnetGroupName=, SubnetIds=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "CreateSubnetGroup", arguments::Dict)
    dax([::AWSConfig], "CreateSubnetGroup", SubnetGroupName=, SubnetIds=, <keyword arguments>)

# CreateSubnetGroup Operation

Creates a new subnet group.

# Arguments

## `SubnetGroupName = ::String` -- *Required*
A name for the subnet group. This value is stored as a lowercase string.

## `Description = ::String`
A description for the subnet group

## `SubnetIds = [::String, ...]` -- *Required*
A list of VPC subnet IDs for the subnet group.

# Returns

`CreateSubnetGroupResponse`

# Exceptions

`SubnetGroupAlreadyExistsFault`, `SubnetGroupQuotaExceededFault`, `SubnetQuotaExceededFault` or `InvalidSubnet`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/CreateSubnetGroup)
"""
@inline create_subnet_group(aws::AWSConfig=default_aws_config(); args...) = create_subnet_group(aws, args)

@inline create_subnet_group(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "CreateSubnetGroup", args)

@inline create_subnet_group(args) = create_subnet_group(default_aws_config(), args)

"""
    using AWSSDK.DAX.decrease_replication_factor
    decrease_replication_factor([::AWSConfig], arguments::Dict)
    decrease_replication_factor([::AWSConfig]; ClusterName=, NewReplicationFactor=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DecreaseReplicationFactor", arguments::Dict)
    dax([::AWSConfig], "DecreaseReplicationFactor", ClusterName=, NewReplicationFactor=, <keyword arguments>)

# DecreaseReplicationFactor Operation

Removes one or more nodes from a DAX cluster.

**Note**
> You cannot use `DecreaseReplicationFactor` to remove the last node in a DAX cluster. If you need to do this, use `DeleteCluster` instead.

# Arguments

## `ClusterName = ::String` -- *Required*
The name of the DAX cluster from which you want to remove nodes.

## `NewReplicationFactor = ::Int` -- *Required*
The new number of nodes for the DAX cluster.

## `AvailabilityZones = [::String, ...]`
The Availability Zone(s) from which to remove nodes.

## `NodeIdsToRemove = [::String, ...]`
The unique identifiers of the nodes to be removed from the cluster.

# Returns

`DecreaseReplicationFactorResponse`

# Exceptions

`ClusterNotFoundFault`, `NodeNotFoundFault`, `InvalidClusterStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DecreaseReplicationFactor)
"""
@inline decrease_replication_factor(aws::AWSConfig=default_aws_config(); args...) = decrease_replication_factor(aws, args)

@inline decrease_replication_factor(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DecreaseReplicationFactor", args)

@inline decrease_replication_factor(args) = decrease_replication_factor(default_aws_config(), args)

"""
    using AWSSDK.DAX.delete_cluster
    delete_cluster([::AWSConfig], arguments::Dict)
    delete_cluster([::AWSConfig]; ClusterName=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DeleteCluster", arguments::Dict)
    dax([::AWSConfig], "DeleteCluster", ClusterName=)

# DeleteCluster Operation

Deletes a previously provisioned DAX cluster. *DeleteCluster* deletes all associated nodes, node endpoints and the DAX cluster itself. When you receive a successful response from this action, DAX immediately begins deleting the cluster; you cannot cancel or revert this action.

# Arguments

## `ClusterName = ::String` -- *Required*
The name of the cluster to be deleted.

# Returns

`DeleteClusterResponse`

# Exceptions

`ClusterNotFoundFault`, `InvalidClusterStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DeleteCluster)
"""
@inline delete_cluster(aws::AWSConfig=default_aws_config(); args...) = delete_cluster(aws, args)

@inline delete_cluster(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DeleteCluster", args)

@inline delete_cluster(args) = delete_cluster(default_aws_config(), args)

"""
    using AWSSDK.DAX.delete_parameter_group
    delete_parameter_group([::AWSConfig], arguments::Dict)
    delete_parameter_group([::AWSConfig]; ParameterGroupName=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DeleteParameterGroup", arguments::Dict)
    dax([::AWSConfig], "DeleteParameterGroup", ParameterGroupName=)

# DeleteParameterGroup Operation

Deletes the specified parameter group. You cannot delete a parameter group if it is associated with any DAX clusters.

# Arguments

## `ParameterGroupName = ::String` -- *Required*
The name of the parameter group to delete.

# Returns

`DeleteParameterGroupResponse`

# Exceptions

`InvalidParameterGroupStateFault`, `ParameterGroupNotFoundFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DeleteParameterGroup)
"""
@inline delete_parameter_group(aws::AWSConfig=default_aws_config(); args...) = delete_parameter_group(aws, args)

@inline delete_parameter_group(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DeleteParameterGroup", args)

@inline delete_parameter_group(args) = delete_parameter_group(default_aws_config(), args)

"""
    using AWSSDK.DAX.delete_subnet_group
    delete_subnet_group([::AWSConfig], arguments::Dict)
    delete_subnet_group([::AWSConfig]; SubnetGroupName=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DeleteSubnetGroup", arguments::Dict)
    dax([::AWSConfig], "DeleteSubnetGroup", SubnetGroupName=)

# DeleteSubnetGroup Operation

Deletes a subnet group.

**Note**
> You cannot delete a subnet group if it is associated with any DAX clusters.

# Arguments

## `SubnetGroupName = ::String` -- *Required*
The name of the subnet group to delete.

# Returns

`DeleteSubnetGroupResponse`

# Exceptions

`SubnetGroupInUseFault` or `SubnetGroupNotFoundFault`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DeleteSubnetGroup)
"""
@inline delete_subnet_group(aws::AWSConfig=default_aws_config(); args...) = delete_subnet_group(aws, args)

@inline delete_subnet_group(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DeleteSubnetGroup", args)

@inline delete_subnet_group(args) = delete_subnet_group(default_aws_config(), args)

"""
    using AWSSDK.DAX.describe_clusters
    describe_clusters([::AWSConfig], arguments::Dict)
    describe_clusters([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DescribeClusters", arguments::Dict)
    dax([::AWSConfig], "DescribeClusters", <keyword arguments>)

# DescribeClusters Operation

Returns information about all provisioned DAX clusters if no cluster identifier is specified, or about a specific DAX cluster if a cluster identifier is supplied.

If the cluster is in the CREATING state, only cluster level information will be displayed until all of the nodes are successfully provisioned.

If the cluster is in the DELETING state, only cluster level information will be displayed.

If nodes are currently being added to the DAX cluster, node endpoint information and creation time for the additional nodes will not be displayed until they are completely provisioned. When the DAX cluster state is *available*, the cluster is ready for use.

If nodes are currently being removed from the DAX cluster, no endpoint information for the removed nodes is displayed.

# Arguments

## `ClusterNames = [::String, ...]`
The names of the DAX clusters being described.

## `MaxResults = ::Int`
The maximum number of results to include in the response. If more results exist than the specified `MaxResults` value, a token is included in the response so that the remaining results can be retrieved.

The value for `MaxResults` must be between 20 and 100.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token, up to the value specified by `MaxResults`.

# Returns

`DescribeClustersResponse`

# Exceptions

`ClusterNotFoundFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DescribeClusters)
"""
@inline describe_clusters(aws::AWSConfig=default_aws_config(); args...) = describe_clusters(aws, args)

@inline describe_clusters(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DescribeClusters", args)

@inline describe_clusters(args) = describe_clusters(default_aws_config(), args)

"""
    using AWSSDK.DAX.describe_default_parameters
    describe_default_parameters([::AWSConfig], arguments::Dict)
    describe_default_parameters([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DescribeDefaultParameters", arguments::Dict)
    dax([::AWSConfig], "DescribeDefaultParameters", <keyword arguments>)

# DescribeDefaultParameters Operation

Returns the default system parameter information for the DAX caching software.

# Arguments

## `MaxResults = ::Int`
The maximum number of results to include in the response. If more results exist than the specified `MaxResults` value, a token is included in the response so that the remaining results can be retrieved.

The value for `MaxResults` must be between 20 and 100.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token, up to the value specified by `MaxResults`.

# Returns

`DescribeDefaultParametersResponse`

# Exceptions

`InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DescribeDefaultParameters)
"""
@inline describe_default_parameters(aws::AWSConfig=default_aws_config(); args...) = describe_default_parameters(aws, args)

@inline describe_default_parameters(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DescribeDefaultParameters", args)

@inline describe_default_parameters(args) = describe_default_parameters(default_aws_config(), args)

"""
    using AWSSDK.DAX.describe_events
    describe_events([::AWSConfig], arguments::Dict)
    describe_events([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DescribeEvents", arguments::Dict)
    dax([::AWSConfig], "DescribeEvents", <keyword arguments>)

# DescribeEvents Operation

Returns events related to DAX clusters and parameter groups. You can obtain events specific to a particular DAX cluster or parameter group by providing the name as a parameter.

By default, only the events occurring within the last hour are returned; however, you can retrieve up to 14 days' worth of events if necessary.

# Arguments

## `SourceName = ::String`
The identifier of the event source for which events will be returned. If not specified, then all sources are included in the response.

## `SourceType = "CLUSTER", "PARAMETER_GROUP" or "SUBNET_GROUP"`
The event source to retrieve events for. If no value is specified, all events are returned.

## `StartTime = timestamp`
The beginning of the time interval to retrieve events for, specified in ISO 8601 format.

## `EndTime = timestamp`
The end of the time interval for which to retrieve events, specified in ISO 8601 format.

## `Duration = ::Int`
The number of minutes' worth of events to retrieve.

## `MaxResults = ::Int`
The maximum number of results to include in the response. If more results exist than the specified `MaxResults` value, a token is included in the response so that the remaining results can be retrieved.

The value for `MaxResults` must be between 20 and 100.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token, up to the value specified by `MaxResults`.

# Returns

`DescribeEventsResponse`

# Exceptions

`InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DescribeEvents)
"""
@inline describe_events(aws::AWSConfig=default_aws_config(); args...) = describe_events(aws, args)

@inline describe_events(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DescribeEvents", args)

@inline describe_events(args) = describe_events(default_aws_config(), args)

"""
    using AWSSDK.DAX.describe_parameter_groups
    describe_parameter_groups([::AWSConfig], arguments::Dict)
    describe_parameter_groups([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DescribeParameterGroups", arguments::Dict)
    dax([::AWSConfig], "DescribeParameterGroups", <keyword arguments>)

# DescribeParameterGroups Operation

Returns a list of parameter group descriptions. If a parameter group name is specified, the list will contain only the descriptions for that group.

# Arguments

## `ParameterGroupNames = [::String, ...]`
The names of the parameter groups.

## `MaxResults = ::Int`
The maximum number of results to include in the response. If more results exist than the specified `MaxResults` value, a token is included in the response so that the remaining results can be retrieved.

The value for `MaxResults` must be between 20 and 100.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token, up to the value specified by `MaxResults`.

# Returns

`DescribeParameterGroupsResponse`

# Exceptions

`ParameterGroupNotFoundFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DescribeParameterGroups)
"""
@inline describe_parameter_groups(aws::AWSConfig=default_aws_config(); args...) = describe_parameter_groups(aws, args)

@inline describe_parameter_groups(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DescribeParameterGroups", args)

@inline describe_parameter_groups(args) = describe_parameter_groups(default_aws_config(), args)

"""
    using AWSSDK.DAX.describe_parameters
    describe_parameters([::AWSConfig], arguments::Dict)
    describe_parameters([::AWSConfig]; ParameterGroupName=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DescribeParameters", arguments::Dict)
    dax([::AWSConfig], "DescribeParameters", ParameterGroupName=, <keyword arguments>)

# DescribeParameters Operation

Returns the detailed parameter list for a particular parameter group.

# Arguments

## `ParameterGroupName = ::String` -- *Required*
The name of the parameter group.

## `Source = ::String`
How the parameter is defined. For example, `system` denotes a system-defined parameter.

## `MaxResults = ::Int`
The maximum number of results to include in the response. If more results exist than the specified `MaxResults` value, a token is included in the response so that the remaining results can be retrieved.

The value for `MaxResults` must be between 20 and 100.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token, up to the value specified by `MaxResults`.

# Returns

`DescribeParametersResponse`

# Exceptions

`ParameterGroupNotFoundFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DescribeParameters)
"""
@inline describe_parameters(aws::AWSConfig=default_aws_config(); args...) = describe_parameters(aws, args)

@inline describe_parameters(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DescribeParameters", args)

@inline describe_parameters(args) = describe_parameters(default_aws_config(), args)

"""
    using AWSSDK.DAX.describe_subnet_groups
    describe_subnet_groups([::AWSConfig], arguments::Dict)
    describe_subnet_groups([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "DescribeSubnetGroups", arguments::Dict)
    dax([::AWSConfig], "DescribeSubnetGroups", <keyword arguments>)

# DescribeSubnetGroups Operation

Returns a list of subnet group descriptions. If a subnet group name is specified, the list will contain only the description of that group.

# Arguments

## `SubnetGroupNames = [::String, ...]`
The name of the subnet group.

## `MaxResults = ::Int`
The maximum number of results to include in the response. If more results exist than the specified `MaxResults` value, a token is included in the response so that the remaining results can be retrieved.

The value for `MaxResults` must be between 20 and 100.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token, up to the value specified by `MaxResults`.

# Returns

`DescribeSubnetGroupsResponse`

# Exceptions

`SubnetGroupNotFoundFault`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/DescribeSubnetGroups)
"""
@inline describe_subnet_groups(aws::AWSConfig=default_aws_config(); args...) = describe_subnet_groups(aws, args)

@inline describe_subnet_groups(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "DescribeSubnetGroups", args)

@inline describe_subnet_groups(args) = describe_subnet_groups(default_aws_config(), args)

"""
    using AWSSDK.DAX.increase_replication_factor
    increase_replication_factor([::AWSConfig], arguments::Dict)
    increase_replication_factor([::AWSConfig]; ClusterName=, NewReplicationFactor=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "IncreaseReplicationFactor", arguments::Dict)
    dax([::AWSConfig], "IncreaseReplicationFactor", ClusterName=, NewReplicationFactor=, <keyword arguments>)

# IncreaseReplicationFactor Operation

Adds one or more nodes to a DAX cluster.

# Arguments

## `ClusterName = ::String` -- *Required*
The name of the DAX cluster that will receive additional nodes.

## `NewReplicationFactor = ::Int` -- *Required*
The new number of nodes for the DAX cluster.

## `AvailabilityZones = [::String, ...]`
The Availability Zones (AZs) in which the cluster nodes will be created. All nodes belonging to the cluster are placed in these Availability Zones. Use this parameter if you want to distribute the nodes across multiple AZs.

# Returns

`IncreaseReplicationFactorResponse`

# Exceptions

`ClusterNotFoundFault`, `InvalidClusterStateFault`, `InsufficientClusterCapacityFault`, `InvalidVPCNetworkStateFault`, `NodeQuotaForClusterExceededFault`, `NodeQuotaForCustomerExceededFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/IncreaseReplicationFactor)
"""
@inline increase_replication_factor(aws::AWSConfig=default_aws_config(); args...) = increase_replication_factor(aws, args)

@inline increase_replication_factor(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "IncreaseReplicationFactor", args)

@inline increase_replication_factor(args) = increase_replication_factor(default_aws_config(), args)

"""
    using AWSSDK.DAX.list_tags
    list_tags([::AWSConfig], arguments::Dict)
    list_tags([::AWSConfig]; ResourceName=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "ListTags", arguments::Dict)
    dax([::AWSConfig], "ListTags", ResourceName=, <keyword arguments>)

# ListTags Operation

List all of the tags for a DAX cluster. You can call `ListTags` up to 10 times per second, per account.

# Arguments

## `ResourceName = ::String` -- *Required*
The name of the DAX resource to which the tags belong.

## `NextToken = ::String`
An optional token returned from a prior request. Use this token for pagination of results from this action. If this parameter is specified, the response includes only results beyond the token.

# Returns

`ListTagsResponse`

# Exceptions

`ClusterNotFoundFault`, `InvalidARNFault`, `InvalidClusterStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/ListTags)
"""
@inline list_tags(aws::AWSConfig=default_aws_config(); args...) = list_tags(aws, args)

@inline list_tags(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "ListTags", args)

@inline list_tags(args) = list_tags(default_aws_config(), args)

"""
    using AWSSDK.DAX.reboot_node
    reboot_node([::AWSConfig], arguments::Dict)
    reboot_node([::AWSConfig]; ClusterName=, NodeId=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "RebootNode", arguments::Dict)
    dax([::AWSConfig], "RebootNode", ClusterName=, NodeId=)

# RebootNode Operation

Reboots a single node of a DAX cluster. The reboot action takes place as soon as possible. During the reboot, the node status is set to REBOOTING.

# Arguments

## `ClusterName = ::String` -- *Required*
The name of the DAX cluster containing the node to be rebooted.

## `NodeId = ::String` -- *Required*
The system-assigned ID of the node to be rebooted.

# Returns

`RebootNodeResponse`

# Exceptions

`ClusterNotFoundFault`, `NodeNotFoundFault`, `InvalidClusterStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/RebootNode)
"""
@inline reboot_node(aws::AWSConfig=default_aws_config(); args...) = reboot_node(aws, args)

@inline reboot_node(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "RebootNode", args)

@inline reboot_node(args) = reboot_node(default_aws_config(), args)

"""
    using AWSSDK.DAX.tag_resource
    tag_resource([::AWSConfig], arguments::Dict)
    tag_resource([::AWSConfig]; ResourceName=, Tags=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "TagResource", arguments::Dict)
    dax([::AWSConfig], "TagResource", ResourceName=, Tags=)

# TagResource Operation

Associates a set of tags with a DAX resource. You can call `TagResource` up to 5 times per second, per account.

# Arguments

## `ResourceName = ::String` -- *Required*
The name of the DAX resource to which tags should be added.

## `Tags = [[ ... ], ...]` -- *Required*
The tags to be assigned to the DAX resource.
```
 Tags = [[
        "Key" =>  ::String,
        "Value" =>  ::String
    ], ...]
```

# Returns

`TagResourceResponse`

# Exceptions

`ClusterNotFoundFault`, `TagQuotaPerResourceExceeded`, `InvalidARNFault`, `InvalidClusterStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/TagResource)
"""
@inline tag_resource(aws::AWSConfig=default_aws_config(); args...) = tag_resource(aws, args)

@inline tag_resource(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "TagResource", args)

@inline tag_resource(args) = tag_resource(default_aws_config(), args)

"""
    using AWSSDK.DAX.untag_resource
    untag_resource([::AWSConfig], arguments::Dict)
    untag_resource([::AWSConfig]; ResourceName=, TagKeys=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "UntagResource", arguments::Dict)
    dax([::AWSConfig], "UntagResource", ResourceName=, TagKeys=)

# UntagResource Operation

Removes the association of tags from a DAX resource. You can call `UntagResource` up to 5 times per second, per account.

# Arguments

## `ResourceName = ::String` -- *Required*
The name of the DAX resource from which the tags should be removed.

## `TagKeys = [::String, ...]` -- *Required*
A list of tag keys. If the DAX cluster has any tags with these keys, then the tags are removed from the cluster.

# Returns

`UntagResourceResponse`

# Exceptions

`ClusterNotFoundFault`, `InvalidARNFault`, `TagNotFoundFault`, `InvalidClusterStateFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/UntagResource)
"""
@inline untag_resource(aws::AWSConfig=default_aws_config(); args...) = untag_resource(aws, args)

@inline untag_resource(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "UntagResource", args)

@inline untag_resource(args) = untag_resource(default_aws_config(), args)

"""
    using AWSSDK.DAX.update_cluster
    update_cluster([::AWSConfig], arguments::Dict)
    update_cluster([::AWSConfig]; ClusterName=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "UpdateCluster", arguments::Dict)
    dax([::AWSConfig], "UpdateCluster", ClusterName=, <keyword arguments>)

# UpdateCluster Operation

Modifies the settings for a DAX cluster. You can use this action to change one or more cluster configuration parameters by specifying the parameters and the new values.

# Arguments

## `ClusterName = ::String` -- *Required*
The name of the DAX cluster to be modified.

## `Description = ::String`
A description of the changes being made to the cluster.

## `PreferredMaintenanceWindow = ::String`
A range of time when maintenance of DAX cluster software will be performed. For example: `sun:01:00-sun:09:00`. Cluster maintenance normally takes less than 30 minutes, and is performed automatically within the maintenance window.

## `NotificationTopicArn = ::String`
The Amazon Resource Name (ARN) that identifies the topic.

## `NotificationTopicStatus = ::String`
The current state of the topic.

## `ParameterGroupName = ::String`
The name of a parameter group for this cluster.

## `SecurityGroupIds = [::String, ...]`
A list of user-specified security group IDs to be assigned to each node in the DAX cluster. If this parameter is not specified, DAX assigns the default VPC security group to each node.

# Returns

`UpdateClusterResponse`

# Exceptions

`InvalidClusterStateFault`, `ClusterNotFoundFault`, `InvalidParameterGroupStateFault`, `ParameterGroupNotFoundFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/UpdateCluster)
"""
@inline update_cluster(aws::AWSConfig=default_aws_config(); args...) = update_cluster(aws, args)

@inline update_cluster(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "UpdateCluster", args)

@inline update_cluster(args) = update_cluster(default_aws_config(), args)

"""
    using AWSSDK.DAX.update_parameter_group
    update_parameter_group([::AWSConfig], arguments::Dict)
    update_parameter_group([::AWSConfig]; ParameterGroupName=, ParameterNameValues=)

    using AWSCore.Services.dax
    dax([::AWSConfig], "UpdateParameterGroup", arguments::Dict)
    dax([::AWSConfig], "UpdateParameterGroup", ParameterGroupName=, ParameterNameValues=)

# UpdateParameterGroup Operation

Modifies the parameters of a parameter group. You can modify up to 20 parameters in a single request by submitting a list parameter name and value pairs.

# Arguments

## `ParameterGroupName = ::String` -- *Required*
The name of the parameter group.

## `ParameterNameValues = [[ ... ], ...]` -- *Required*
An array of name-value pairs for the parameters in the group. Each element in the array represents a single parameter.
```
 ParameterNameValues = [[
        "ParameterName" =>  ::String,
        "ParameterValue" =>  ::String
    ], ...]
```

# Returns

`UpdateParameterGroupResponse`

# Exceptions

`InvalidParameterGroupStateFault`, `ParameterGroupNotFoundFault`, `InvalidParameterValueException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/UpdateParameterGroup)
"""
@inline update_parameter_group(aws::AWSConfig=default_aws_config(); args...) = update_parameter_group(aws, args)

@inline update_parameter_group(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "UpdateParameterGroup", args)

@inline update_parameter_group(args) = update_parameter_group(default_aws_config(), args)

"""
    using AWSSDK.DAX.update_subnet_group
    update_subnet_group([::AWSConfig], arguments::Dict)
    update_subnet_group([::AWSConfig]; SubnetGroupName=, <keyword arguments>)

    using AWSCore.Services.dax
    dax([::AWSConfig], "UpdateSubnetGroup", arguments::Dict)
    dax([::AWSConfig], "UpdateSubnetGroup", SubnetGroupName=, <keyword arguments>)

# UpdateSubnetGroup Operation

Modifies an existing subnet group.

# Arguments

## `SubnetGroupName = ::String` -- *Required*
The name of the subnet group.

## `Description = ::String`
A description of the subnet group.

## `SubnetIds = [::String, ...]`
A list of subnet IDs in the subnet group.

# Returns

`UpdateSubnetGroupResponse`

# Exceptions

`SubnetGroupNotFoundFault`, `SubnetQuotaExceededFault`, `SubnetInUse` or `InvalidSubnet`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/dax-2017-04-19/UpdateSubnetGroup)
"""
@inline update_subnet_group(aws::AWSConfig=default_aws_config(); args...) = update_subnet_group(aws, args)

@inline update_subnet_group(aws::AWSConfig, args) = AWSCore.Services.dax(aws, "UpdateSubnetGroup", args)

@inline update_subnet_group(args) = update_subnet_group(default_aws_config(), args)

end # module DAX

#==============================================================================#
# End of file
#==============================================================================#


text:
"""
Bridges `CP.IfThenElse` to `CP.Implication`.
"""
struct IfThenElse2ImplicationBridge{T} <: MOIBC.AbstractBridge
    con_if::MOI.ConstraintIndex # {MOI.VectorAffineFunction{T}, CP.Implication}
    con_else::MOI.ConstraintIndex # {MOI.VectorAffineFunction{T}, CP.Implication}

    # TODO: why is it necessary?
    function IfThenElse2ImplicationBridge(
            con_if::MOI.ConstraintIndex{MOI.VectorAffineFunction{T}, CP.Implication{S1, S2}},
            con_else::MOI.ConstraintIndex{MOI.VectorAffineFunction{T}, CP.Implication{S1, S3}},
        ) where {T, S1, S2, S3}
        return new{T}(con_if, con_else)
    end
end

function MOIBC.bridge_constraint(
    ::Type{IfThenElse2ImplicationBridge{T}},
    model,
    f::MOI.VectorOfVariables,
    s::CP.IfThenElse{S1, S2, S3},
) where {T, S1, S2, S3}
    return MOIBC.bridge_constraint(
        IfThenElse2ImplicationBridge{T},
        model,
        MOI.VectorAffineFunction{T}(f),
        s,
    )
end

function MOIBC.bridge_constraint(
    ::Type{IfThenElse2ImplicationBridge{T}},
    model,
    f::MOI.VectorAffineFunction{T},
    s::CP.IfThenElse{S1, S2, S3},
) where {T, S1, S2, S3}
    f_scalars = MOIU.scalarize(f)
    f_condition = f_scalars[1 : MOI.dimension(s.condition)]
    f_true = f_scalars[(MOI.dimension(s.condition) + 1) : (MOI.dimension(s.condition) + MOI.dimension(s.true_constraint))]
    f_false = f_scalars[(MOI.dimension(s.condition) + MOI.dimension(s.true_constraint) + 1) : (MOI.dimension(s.condition) + MOI.dimension(s.true_constraint) + MOI.dimension(s.false_constraint))]

    con_if = MOI.add_constraint(
        model,
        MOIU.vectorize(
            [
                f_condition...,
                f_true...,
            ]
        ),
        CP.Implication(s.condition, s.true_constraint)
    )

    con_else = MOI.add_constraint(
        model,
        MOIU.vectorize(
            [
                f_condition...,
                f_false...,
            ]
        ),
        CP.Implication(s.condition, s.false_constraint)
    )

    return IfThenElse2ImplicationBridge(con_if, con_else)
end

function MOI.supports_constraint(
    ::Type{IfThenElse2ImplicationBridge{T}},
    ::Union{Type{MOI.VectorOfVariables}, Type{MOI.VectorAffineFunction{T}}},
    ::Type{CP.IfThenElse{S1, S2, S3}},
) where {T, S1, S2, S3}
    return true
end

function MOIB.added_constrained_variable_types(::Type{IfThenElse2ImplicationBridge{T}}) where {T}
    return Tuple{Type}[]
end

function MOIB.added_constraint_types(::Type{IfThenElse2ImplicationBridge{T}}) where {T}
    return [
        (MOI.VectorAffineFunction{T}, CP.Implication), # TODO: how to be more precise?
    ]
end

function MOI.get(
    ::IfThenElse2ImplicationBridge{T},
    ::MOI.NumberOfConstraints{
        MOI.VectorAffineFunction{T}, CP.Implication,
    },
) where {T}
    return 2
end

function MOI.get(
    b::IfThenElse2ImplicationBridge{T},
    ::MOI.ListOfConstraintIndices{
        MOI.VectorAffineFunction{T}, CP.Implication,
    },
) where {T}
    return [b.con_if, b.con_else]
end

ext: jl


text:
using JuMP

# Inspired from Joey Huchette's test in ConvexHull.jl
function infeasibletest(lib::Polyhedra.Library, n)
    m = Model()
    @variable(m, 0 ≤ x[1:n] ≤ 1)
    @constraint(m, x[1] ≥ 2)

    poly = polyhedron(m, lib)

    @test !haspoints(poly)
    @test !haslines(poly)
    @test !hasrays(poly)
    @test !hasallrays(poly)

    @test !hashyperplanes(poly)
    @test hashalfspaces(poly)
    @test hasallhalfspaces(poly)

    # haseqs has triggered a detection of linearity but
    # it shouldn't affect the V-representation
    @test !haspoints(poly)
    @test !haslines(poly)
    @test !hasrays(poly)
    @test !hasallrays(poly)
end


text:
using jInv.Mesh
using Multigrid

println("************************************************* Example 2D ******************************************************");

domain = [0.0, 1.0, 0.0, 1.0];
n = [512,512];
Mr = getRegularMesh(domain,n)
G = getNodalGradientMatrix(Mr);
Ar = G'*G;
Ar = Ar + 1e-4*norm(Ar,1)*speye(size(Ar,2));

levels      = 4;
numCores 	= 8;
maxIter     = 10;
relativeTol = 1e-10;
relaxType   = "Jac";
relaxParam  = 0.75;
relaxPre 	= 1;
relaxPost   = 1;
cycleType   ='V';
coarseSolveType = "NoMUMPS";

MG = getMGparam(levels,numCores,maxIter,relativeTol,relaxType,relaxParam,relaxPre,relaxPost,cycleType,coarseSolveType,0.5,0.0);

N = size(Ar,2);

b = Ar*rand(N,2);
x = zeros(N,2);

MGsetup(Ar,Mr,MG,Float64,size(b,2),true);

println("****************************** Stand-alone GMG: ******************************")
solveMG(MG,b,x,true);

println("****************************** Stand-alone GMG: iterative coarsest ***********************")
coarseSolveType = "BiCGSTAB"
MG = getMGparam(levels,numCores,maxIter,relativeTol,relaxType,relaxParam,relaxPre,relaxPost,cycleType,coarseSolveType,0.5,0.0);
MGsetup(Ar,Mr,MG,Float64,size(b,2),true);
solveMG(MG,b,x,true);
println("****************************** GMRES preconditioned with GMG: (only one rhs...) ******************************")
x[:] = 0.0
b = vec(b[:,1]);
x = vec(x[:,1]);
relaxType   = "Jac-GMRES";
coarseSolveType = "NoMUMPS"
MG = getMGparam(levels,numCores,maxIter,relativeTol,relaxType,relaxParam,relaxPre,relaxPost,cycleType,coarseSolveType,0.5,0.0);
MGsetup(Ar,Mr,MG,Float64,size(b,2),true);
solveGMRES_MG(Ar,MG,b,x,true,2)

Ar = 0;
b = 0;
x = 0;
Mr = 0;

copySolver(MG);
destroyCoarsestLU(MG);
clear!(MG)


text:
using POMDPs
using POMDPPolicies
using POMDPModels
using BeliefUpdaters
using Random
using Test
using POMDPSimulators
import POMDPLinter

@testset "rollout" begin
    include("test_rollout.jl")
end
@testset "sim" begin
    include("test_sim.jl")
end
@testset "stepthrough" begin
    include("test_stepthrough.jl")
end
@testset "history_recorder" begin
    include("test_history_recorder.jl")
end
@testset "parallel" begin
    include("test_parallel.jl")
end
@testset "display" begin
    include("test_display.jl")
end


text:
using Documenter, PETSc

makedocs(;
    modules=[PETSc],
    sitename="PETSc.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
    ),
    pages=[
        "Home" => "index.md",
        "Getting Started" => "man/getting_started.md",
        "PETSc" => Any[
            "Mat" =>  "man/mat.md",
        ],
        "List of functions"  => "man/listfunctions.md"
    ],
)

deploydocs(;
    repo="github.com/JuliaParallel/PETSc.jl.git",
    branch = "gh-pages",
    target = "build",
    devbranch = "master",
    devurl = "dev",
    forcepush=true,
    push_preview = true
)


text:
using CSV, DataFrames
using Dates
using TimeSeries
using Setfield
using Plots

function prep_data()
    spydf = CSV.File("SPY.csv") |> DataFrame
    aggdf = CSV.File("AGG.csv") |> DataFrame

    # filter(:Date => x -> x >= Date("2003-09-30"), spydf)
    # filter(:Date => x -> x >= Date("2003-09-30"), aggdf)

    DataFrames.rename!(spydf, "Adj Close" => :adj_close)
    ratio = spydf.adj_close ./ spydf.Close
    spydf.adj_open = spydf.Open .* ratio

    spyta = TimeArray(spydf.Date, Matrix(spydf[!, [:adj_open, :adj_close]]), [:adj_open, :adj_close])

    DataFrames.rename!(aggdf, "Adj Close" => :adj_close)
    ratio = aggdf.adj_close ./ aggdf.Close
    aggdf.adj_open = aggdf.Open .* ratio

    aggta = TimeArray(aggdf.Date, Matrix(aggdf[!, [:adj_open, :adj_close]]), [:adj_open, :adj_close])

    spyta = spyta[Date(2003, 9, 30):Day(1):Date(2019, 12, 31)]
    aggta = aggta[Date(2003, 9, 30):Day(1):Date(2019, 12, 31)]

    return (;spy = spyta, agg = aggta)
end

########################################
# Simple signals
########################################

"""
Generate `begininng of the month` signal. First day of the timeseries is considered to be non-start, if this assumption is wrong value should be overwritten manually
"""
function generate_bom_signal(ta)
    ts = timestamp(ta)
    res = Vector{Bool}(undef, length(ts))

    for (i, t) in pairs(ts)
        if i == 1
            res[1] = false
            continue
        end
        res[i] = month(ts[i - 1]) != month(ts[i])
    end

    return TimeArray(ts, res)
end

function generate_buyandhold_signal(ta, k = 1)
    ts = timestamp(ta)
    res = zeros(Bool, length(ts))
    res[k] = true

    return TimeArray(ts, res)
end

struct Broker{T1, T2}
    cash::Float64
    shares::T1
    ratios::T2 # do not like this, should be part of the strategy, not broker
end

"""
Estimate our assets, taking into account our cash, and current prices. `k` define which price we are using, `open` or `close`
"""
function assets(broker, prices, i, k = 2)
    res = broker.cash
    for j in 1:length(prices)
        res += values(prices[j])[i, k] * broker.shares[j]
    end
    return res
end

"""
Everything is hardcoded, sorriamba. Prices should be an array or tuple of
TimeArrays and they should be in 1 to 1 relation with `ratios` field of `Broker`.
Signal should be the result of `generate_bom_signal`. All series should have the same size, no date checking is made.
"""
function run(broker::Broker, prices, signal)
    ts = timestamp(signal)
    signal = values(signal)
    equity_curve = Vector{Float64}(undef, length(signal))
    for i in 1:length(signal)
        if signal[i]
            # We've got rebalance signal, it's action time
            a = assets(broker, prices, i, 1) # we are buying in the morning, so we are using `open` price to estimate our current situation.
            for j in 1:length(prices)
                p = values(prices[j])[i, 1] # current price
                delta = floor(Int, a / p * broker.ratios[j]) - broker.shares[j]
                @set! broker.cash -= delta * p
                @set! broker.shares[j] += delta
            end
        end

        equity_curve[i] = assets(broker, prices, i)
    end

    return TimeArray(ts, equity_curve)
end

########################################
# Calculations
########################################

broker = Broker(1_000_000.0, (0, 0), (0.6, 0.4))
spyta, aggta = prep_data();
signal = generate_bom_signal(spyta);

sim1 = run(broker, (spyta, aggta), signal);

broker2 = Broker(1_000_000.0, (0, ), (1.0, ))

buyandhold = generate_buyandhold_signal(spyta, 2)
sim2 = run(broker2, (spyta, ), buyandhold);

plot(sim1, label = "Rebalance 60/40", legend = :topleft)
plot!(sim2, label = "SPY buy&hold")


text:
@test yyyymmdd2yyyy(19240401) == 1924
@test yyyymmdd2yyyy(20240401) == 2024

@test yyyymmdd2yyyymm(19240401) == 192404
@test yyyymmdd2yyyymm(20240401) == 202404

@test yyyymmdd2date(19240401) == Date(1924,4,1)
@test yyyymmdd2date(20240401) == Date(2024,4,1)

@test dduuuyyyy2date("01Apr1924") == Date(1924,4,1)
@test dduuuyyyy2date("01Apr2024") == Date(2024,4,1)

@test uuuddyyyy2date("Apr 1, 1924") == Date(1924,4,1)
@test uuuddyyyy2date("Apr 1, 2024") == Date(2024,4,1)

@test datetimestring2datetime("Apr 1, 1924", dateformat"uuu dd, yyyy") == Date(1924,4,1)
@test datetimestring2datetime("Apr 1, 2024", dateformat"uuu dd, yyyy") == Date(2024,4,1)
@test datetimestring2datetime("Apr 1, 24", dateformat"uuu dd, yyyy") == Date(2024,4,1)
@test datetimestring2datetime("Apr 1, 71", dateformat"uuu dd, yyyy") == Date(1971,4,1)
@test ismissing(datetimestring2datetime("Bla 1, 71", dateformat"uuu dd, yyyy"))

@test date2yyyymmdd(Date(1924,4,1)) == 19240401
@test date2yyyymmdd(Date(2024,4,1)) == 20240401

@test date2yyyymm(Date(1924,4,1)) == 192404
@test date2yyyymm(Date(2024,4,1)) == 202404

@test date2yyyy(Date(1924,4,1)) == 1924
@test date2yyyy(Date(2024,4,1)) == 2024

@test datetimestring2yyyymmdd("Apr 1, 1924", dateformat"uuu dd, yyyy") == 19240401
@test datetimestring2yyyymmdd("Apr 1, 2024", dateformat"uuu dd, yyyy") == 20240401
@test datetimestring2yyyymmdd("Apr 1, 24", dateformat"uuu dd, yyyy") == 20240401
@test datetimestring2yyyymmdd("Apr 1, 71", dateformat"uuu dd, yyyy") == 19710401
@test ismissing(datetimestring2yyyymmdd("Bla 1, 71", dateformat"uuu dd, yyyy"))

@test date2yyyyqq(Date(1924,4,1)) == 192402
@test date2yyyyqq(Date(1924,3,31)) == 192401
@test date2yyyyqq(Date(1924,9,30)) == 192403
@test date2yyyyqq(Date(1924,10,01)) == 192404
@test date2yyyyqq(Date(1924,12,31)) == 192404
@test date2yyyyqq(Date(2024,4,1)) == 202402

@test yearqtr2date("1924-Q1") == Date(1924,3,31)
@test yearqtr2date("1924-Q2") == Date(1924,6,30)
@test yearqtr2date("1924-Q3") == Date(1924,9,30)
@test yearqtr2date("1924-Q4") == Date(1924,12,31)


text:
struct Piece
    name::Symbol
    char::Char
    icon_symbol::String
    icon_color::String
    icon_size::Integer
    icon_offset::Tuple{<:Integer, <:Integer}
    icon_file::Union{Nothing,String}
    function Piece(
        name,
        char;
        icon_symbol=char,
        icon_color="#333",
        icon_size=20,
        icon_offset=(0,0),
        icon_file=nothing)
        new(
            name,
            char[1],
            icon_symbol,
            icon_color,
            icon_size,
            icon_offset,
            icon_file,
        )
    end
end

plaintext(x::Piece) = string(x.char)


text:
using MDP

# This example describes a Markov decision process where
#
# S = {1, 2}
# A = {1, 2}

# r = S x A matrix

r = [ 5  10
     -1  -Inf ]

# Transition matrix
# P(1) = [0.5, 0.5; 0.0, 1.0]
# P(2) = [0.0, 1.0; 0.5, 0.5]
P = Matrix[ [ 0.5  0.5
              0.0  1.0 ],
            [ 0.0  1.0
              0.5  0.5 ] ]

# Construct the MDP model
model = ProbModel(r, P; objective=:Max)

# Compute the optimal solution
(v,g) = valueIteration(model; discount=0.95, tolerance=1e-4)

println(g) # [1, 1]
println(v) # [-8.571401228528563,-19.999971289954992]

# For this model, the optimal solution is:

# g = [1, 1]
# w[1] = (5 - 5.5 β) / (1 -0.5 β)(1 - β)
# w[2] = -1/(1-β)

# which equals
#
# w = [ -8.57142857142855, -19.999999999999982]

# As expected, v is within 1e-4 of w (in sup-norm).


text:
using Base.Test
import GenSPGL

@testset "oneprojector Tests" begin

    @testset "oneprojectorMex Tests" begin

    end

end


text:
function vector_identities(c)
    A = ContravariantVector(2*randn(3),c,unit_basis=true)
    B = ContravariantVector(2*randn(3),c,unit_basis=true)
    C = CovariantVector(2*randn(3),c,unit_basis=true)
    D = CovariantVector(2*randn(3),c,unit_basis=true)

    ϵ = eps(abs(float(one(eltype(A)))))

    A_BxC = dot(A,cross(B,C))
    @test isapprox(A_BxC, dot(cross(A,B),C), atol=500ϵ)
    @test isapprox(A_BxC, dot(B,cross(C,A)), atol=500ϵ)
    @test isapprox(A_BxC, dot(cross(B,C),A), atol=500ϵ)
    @test isapprox(A_BxC, dot(C,cross(A,B)), atol=500ϵ)
    @test isapprox(A_BxC, dot(cross(C,A),B), atol=500ϵ)

    AxBxC = cross(A,cross(B,C))
    @test isapprox(AxBxC, cross(cross(C,B),A), atol=500ϵ)
    @test isapprox(AxBxC, dot(A,C)*B - dot(A,B)*C, atol=500ϵ)

    @test isapprox(cross(A,cross(B,C)) + cross(B,cross(C,A)) + cross(C,cross(A,B)), zeros(eltype(A),length(A)), atol=2000ϵ)

    AxB_CxD = dot(cross(A,B),cross(C,D))
    @test isapprox(AxB_CxD, dot(A,C)*dot(B,D) - dot(A,D)*dot(B,C), atol=2000ϵ)

    AxBxCxD = cross(cross(A,B),cross(C,D))
    @test isapprox(AxBxCxD, (dot(cross(A,B),D)*C - dot(cross(A,B),C)*D), atol=2000ϵ)
end

@testset "Algebraic Operations" begin
    println("Testing Algebraic Identities...")
    @testset "Algebraic Identities" begin
        @testset "Cartesian" begin
            c = CartesianCoordinate([2.0,-1.0,1.5])
            vector_identities(c);
        end
        @testset "Cylindrical" begin
            c = CylindricalCoordinate([2.0,pi/4,-1.0])
            vector_identities(c);
        end
        @testset "Spherical" begin
            c = SphericalCoordinate([2.0,pi/4,pi/3])
            vector_identities(c);
        end
    end
end


text:
# borrowed from KernelDensity.jl
# https://github.com/JuliaStats/KernelDensity.jl/blob/master/src/univariate.jl

"""
optimize(f, x_lower, x_upper; iterations=1000, rel_tol=nothing, abs_tol=nothing)

Minimize the function `f` in the interval `x_lower..x_upper`, using the
[golden-section search](https://en.wikipedia.org/wiki/Golden-section_search).
Return an approximate minimum `x̃` or error if such approximate minimum cannot be found.

This algorithm assumes that `-f` is unimodal on the interval `x_lower..x_upper`,
that is to say, there exists a unique `x` in `x_lower..x_upper` such that `f` is
decreasing on `x_lower..x` and increasing on `x..x_upper`.

`rel_tol` and `abs_tol` determine the relative and absolute tolerance, that is
to say, the returned value `x̃` should differ from the actual minimum `x` at most
`abs_tol + rel_tol * abs(x̃)`.
If not manually specified, `rel_tol` and `abs_tol` default to `sqrt(eps(T))` and
`eps(T)` respectively, where `T` is the floating point type of `x_lower` and `x_upper`.

`iterations` determines the maximum number of iterations allowed before convergence.

This is a private, unexported function, used internally to select the optimal bandwidth
automatically.
"""
function optimize(f, x_lower, x_upper; iterations=1000, rel_tol=nothing, abs_tol=nothing)

if x_lower > x_upper
    error("x_lower must be less than x_upper")
end

T = promote_type(typeof(x_lower/1), typeof(x_upper/1))
rtol = something(rel_tol, sqrt(eps(T)))
atol = something(abs_tol, eps(T))

function midpoint_and_convergence(lower, upper)
    midpoint = (lower + upper) / 2
    tol = atol + rtol * midpoint
    midpoint, (upper - lower) <= 2tol
end

invphi::T = 0.5 * (sqrt(5) - 1)
invphisq::T = 0.5 * (3 - sqrt(5))

a::T, b::T = x_lower, x_upper
h = b - a
c = a + invphisq * h
d = a + invphi * h

fc, fd = f(c), f(d)

for _ in 1:iterations
    h *= invphi
    if fc < fd
        m, converged = midpoint_and_convergence(a, d)
        converged && return m
        b = d
        d, fd = c, fc
        c = a + invphisq * h
        fc = f(c)
    else
        m, converged = midpoint_and_convergence(c, b)
        converged && return m
        a = c
        c, fc = d, fd
        d = a + invphi * h
        fd = f(d)
    end
end

error("Reached maximum number of iterations without convergence.")
end


text:
export Agent

type Agent
    handle :: hsa_agent_t

    function Agent(h :: hsa_agent_t)
        assert_runtime_alive()

        new(h)
    end
end

import Base.convert

convert(::Type{hsa_agent_t}, a :: Agent) = a.handle

# Iterate Agents
const iterate_agents_cb_ptr = cfunction(iterate_cb, hsa_status_t, (hsa_agent_t, Ptr{Void}))

function iterate_agents(callback::Function)
    state = IterState(
        function(x)
            a = Agent(x)
            callback(a)
        end)

    state_ptr = pointer_from_objref(state)

    err = ccall((:hsa_iterate_agents, libhsa), hsa_status_t, (Ptr{Void}, Ptr{Void}),
        iterate_agents_cb_ptr, state_ptr)

    if !isnull(state.err)
        rethrow(state.err.value)
    end

    test_status(err)
end

function all_agents(;
    feat :: hsa_agent_feature_t = (hsa_agent_feature_t)(0),
    dev :: hsa_device_type_t = (hsa_device_type_t)(0))
    agents = Array(HSA.Agent,0)

    filter_agents = a -> begin
        a_feat = agent_info_feature(a)
        a_dev = agent_info_device(a)
        if a_feat & feat == feat &&
            a_dev & dev == dev
            push!(agents, a)
        end
        true # continue
    end

    iterate_agents(filter_agents)

    return agents
end

type AgentInfo
    agent :: Agent
    name :: AbstractString
    vendor_name :: AbstractString
    feature :: hsa_agent_feature_t
    wavefront_size :: UInt32
    workgroup_max_dim :: Tuple{UInt16, UInt16, UInt16}
    workgroup_max_size :: UInt32
    grid_max_dim :: hsa_dim3_t
    grid_max_size :: UInt32
    fbarrier_max_size :: UInt32
    queues_max :: UInt32
    queue_max_size :: UInt32
    queue_type :: hsa_queue_type_t
    node :: UInt32
    device :: hsa_device_type_t
    cache_size :: Tuple{UInt32,UInt32,UInt32,UInt32}
end

function AgentInfo(a :: Agent)
    AgentInfo(
        a,
        agent_info_name(a),
        agent_info_vendor_name(a),
        agent_info_feature(a),
        agent_info_wavefront_size(a),
        agent_info_workgroup_max_dim(a),
        agent_info_workgroup_max_size(a),
        agent_info_grid_max_dim(a),
        agent_info_grid_max_size(a),
        agent_info_fbarrier_max_size(a),
        agent_info_queues_max(a),
        agent_info_queue_max_size(a),
        agent_info_queue_type(a),
        agent_info_node(a),
        agent_info_device(a),
        agent_info_cache_size(a)
    )
end

agent_info(a::Agent) = AgentInfo(a)


text:
# MIT License
#
# Copyright (c) 2020 Joshua E Gentry

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# =================================================================================================
# Test reading/writing a J2K image.
@testset "pix_write_jp2k/pix_read_jp2k" begin

    local pix = pix_with()

    @suppress begin
        # -----------------------------------------------------------------------------------------
        # To/From a file.
        local filename = safe_tmp_file()
        @test pix_write_jp2k(filename, pix) == true
        @test pix_read_jp2k(filename) != nothing
        rm(filename)

        # -----------------------------------------------------------------------------------------
        # To/From an IOBuffer.
        local buffer = IOBuffer()
        @test pix_write_jp2k(buffer, pix) == true
        @test position(buffer) != 0
        seekstart(buffer)
        @test pix_read_jp2k(buffer) != nothing

        # -----------------------------------------------------------------------------------------
        # To/From an IOStream
        local filename = safe_tmp_file()
        local file     = open(filename, read=true, write=true)
        @test pix_write_jp2k(file, pix) == true
        @test position(file) != 0
        seekstart(file)
        @test pix_read_jp2k(file) != nothing
        close(file)
        rm(filename)

        # -----------------------------------------------------------------------------------------
        # To/From a byte array.
        local data  = jp2k_with()
        local bytes = pix_write_jp2k(pix)
        @test length(bytes) > 0
        @test pix_read_jp2k(bytes) != nothing
    end
end

# =================================================================================================
# Test writing a J2K image at various qualities.
@testset "pix_write_jp2k | quality = $quality, levels = $levels" for
        quality in [ 20, 34, 40, 45, 100  ], levels in [ 1, 5, 8 ]

    local pix = pix_with()
    @suppress begin
        # -----------------------------------------------------------------------------------------
        # To/From a file.
        local filename = safe_tmp_file()
        @test pix_write_jp2k(filename, pix; quality = quality, levels = levels) == true
        for i in 1:levels
            @test pix_read_jp2k(filename; reduction = 2^(i-1)) != nothing
        end
        rm(filename)

        # -----------------------------------------------------------------------------------------
        # To/From an IOBuffer.
        local buffer = IOBuffer()
        @test pix_write_jp2k(buffer, pix; quality = quality, levels = levels) == true
        @test position(buffer) != 0
        for i in 1:levels
            seekstart(buffer)
            @test pix_read_jp2k(buffer; reduction = 2^(i-1)) != nothing
        end

        # -----------------------------------------------------------------------------------------
        # To/From a byte array.
        local data  = jp2k_with()
        local bytes = pix_write_jp2k(pix; quality = quality, levels = levels)
        @test length(bytes) > 0
        for i in 1:levels
            @test pix_read_jp2k(bytes; reduction = 2^(i-1)) != nothing
        end
    end
end

# =================================================================================================
# Test reading a J2K image /w box
@testset "pix_read_jp2k /w box | level = $level" for level in [1, 3 ]

    local pix = pix_with()
    local box = PixBox(
        IMAGE_WIDTH / 2,
        IMAGE_HEIGHT / 2,
        IMAGE_WIDTH / 2,
        IMAGE_HEIGHT / 2
    )
    @suppress begin
        # -----------------------------------------------------------------------------------------
        # To/From a file.
        local filename = safe_tmp_file()
        @test pix_write_jp2k(filename, pix) == true
        @test pix_read_jp2k(filename; box = box, reduction=2^(level-1)) != nothing
        rm(filename)

        # -----------------------------------------------------------------------------------------
        # To/From an IOBuffer.
        local buffer = IOBuffer()
        @test pix_write_jp2k(buffer, pix) == true
        @test position(buffer) != 0
        seekstart(buffer)
        @test pix_read_jp2k(buffer; box = box, reduction=2^(level-1)) != nothing

        # -----------------------------------------------------------------------------------------
        # To/From a byte array.
        local data  = jp2k_with()
        local bytes = pix_write_jp2k(pix)
        @test length(bytes) > 0
        @test pix_read_jp2k(bytes; box = box, reduction=2^(level-1)) != nothing
    end

end

# =================================================================================================
# Test reading empty data.
@testset "pix_read_jp2k empty data" begin
    @suppress begin
        # -----------------------------------------------------------------------------------------
        # From a file.
        local filename = safe_tmp_file()
        @test pix_read_jp2k(filename) == nothing
        @test pix_read_jp2k("xyzzy.j2k") == nothing
        rm(filename)

        # -----------------------------------------------------------------------------------------
        # From an IOBuffer.
        local buffer = IOBuffer()
        @test pix_read_jp2k(buffer) == nothing

        # -----------------------------------------------------------------------------------------
        # To/From a byte array.
        local bytes = Vector{UInt8}()
        @test pix_read_jp2k(bytes) == nothing
    end
end

# =================================================================================================
# Test pix_write_jp2k supplying bad quality.
@testset "pix_write_jp2k /w invalid quality" begin
    local pix = pix_with()
    local err = "Quality must be between 1 and 100 inclusive."

    # ---------------------------------------------------------------------------------------------
    # To a file.
    local filename = safe_tmp_file()
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix; quality = -1)) == false
    @test filesize(filename) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix; quality = 0)) == false
    @test filesize(filename) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix; quality = 101)) == false
    @test filesize(filename) == 0
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To an IOBuffer.
    local buffer = IOBuffer()
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix; quality = -1)) == false
    @test length(take!(buffer)) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix; quality = 0)) == false
    @test length(take!(buffer)) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix; quality = 101)) == false
    @test length(take!(buffer)) == 0
    # ---------------------------------------------------------------------------------------------
    # To a byte array.
    @test (@test_logs (:error, err) pix_write_jp2k(pix; quality = -1)) == nothing
    @test (@test_logs (:error, err) pix_write_jp2k(pix; quality = 0)) == nothing
    @test (@test_logs (:error, err) pix_write_jp2k(pix; quality = 101)) == nothing
end

# =================================================================================================
# Test pix_write_jp2k supplying bad level.
@testset "pix_write_jp2k /w invalid level" begin
    local pix = pix_with()
    local err = "Levels must be between 1 and 10 inclusive."

    # ---------------------------------------------------------------------------------------------
    # To a file.
    local filename = safe_tmp_file()
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix; levels = -1)) == false
    @test filesize(filename) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix; levels = 0)) == false
    @test filesize(filename) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix; levels = 11)) == false
    @test filesize(filename) == 0
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To an IOBuffer.
    local buffer = IOBuffer()
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix; levels = -1)) == false
    @test length(take!(buffer)) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix; levels = 0)) == false
    @test length(take!(buffer)) == 0
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix; levels = 11)) == false
    @test length(take!(buffer)) == 0
    # ---------------------------------------------------------------------------------------------
    # To a byte array.
    @test (@test_logs (:error, err) pix_write_jp2k(pix; levels = -1)) == nothing
    @test (@test_logs (:error, err) pix_write_jp2k(pix; levels = 0)) == nothing
    @test (@test_logs (:error, err) pix_write_jp2k(pix; levels = 11)) == nothing
end

# =================================================================================================
# Test reading with invalid values.
@testset "pix_read_jp2k /w invalid values" begin
    local pix = pix_with()
    local err = "Reduction must be a factor of 2."

    # ---------------------------------------------------------------------------------------------
    # To/From a file.
    local filename = safe_tmp_file()
    @test pix_write_jp2k(filename, pix) == true
    @test (@test_logs (:error, err) pix_read_jp2k(filename; reduction = -1)) == nothing
    @test (@test_logs (:error, err) pix_read_jp2k(filename; reduction = 0)) == nothing
    @test (@test_logs (:error, err) pix_read_jp2k(filename; reduction = 13)) == nothing
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To/From an IOBuffer.
    local buffer = IOBuffer()
    @test pix_write_jp2k(buffer, pix) == true
    @test position(buffer) != 0
    seekstart(buffer)
    @test (@test_logs (:error, err) pix_read_jp2k(buffer; reduction = -1)) == nothing
    seekstart(buffer)
    @test (@test_logs (:error, err) pix_read_jp2k(buffer; reduction = 0)) == nothing
    seekstart(buffer)
    @test (@test_logs (:error, err) pix_read_jp2k(buffer; reduction = 9)) == nothing

    # ---------------------------------------------------------------------------------------------
    # To/From a byte array.
    local bytes = pix_write_jp2k(pix)
    @test length(bytes) > 0
    @test (@test_logs (:error, err) pix_read_jp2k(bytes; reduction = -1)) == nothing
    @test (@test_logs (:error, err) pix_read_jp2k(bytes; reduction = 0)) == nothing
    @test (@test_logs (:error, err) pix_read_jp2k(bytes; reduction = 6)) == nothing
end

# =================================================================================================
# Test writing a Pix that has been freed.
@testset "pix_write_jp2k /w freed Pix" begin
    local err = "Pix has been freed."
    local pix = pix_with()
    pix_delete!(pix)

    # ---------------------------------------------------------------------------------------------
    # To a file.
    local filename = safe_tmp_file()
    @test (@test_logs (:error, err) pix_write_jp2k(filename, pix)) == false
    rm(filename)

    # ---------------------------------------------------------------------------------------------
    # To an IOBuffer.
    local buffer = IOBuffer()
    @test (@test_logs (:error, err) pix_write_jp2k(buffer, pix)) == false

    # ---------------------------------------------------------------------------------------------
    # To a byte array.
    @test (@test_logs (:error, err) pix_write_jp2k(pix)) == nothing
end


text:
using WeakRefStrings, Missings, Test, Random

@testset "WeakRefString{UInt8}" begin
    data = codeunits("hey there sailor")

    str = WeakRefStrings.WeakRefString(pointer(data), 3)

    @test str.len == 3
    @test string(str) == "hey"
    @test String(str) == "hey"
    @test str[1] === 'h'
    @test str[2] === 'e'
    @test str[3] === 'y'

    io = IOBuffer()
    show(io, str)
    @test String(take!(io)) == "\"hey\""

    show(io, typeof(str))
    @test String(take!(io)) == "WeakRefString{UInt8}"
end

@testset "WeakRefString{UInt16}" begin
    # simulate UTF16 data
    data = [0x0068, 0x0065, 0x0079]

    str = WeakRefStrings.WeakRefString(pointer(data), 3)
    @test typeof(str) == WeakRefStrings.WeakRefString{UInt16}
    @test String(str) == "hey"
    @test str.len == 3
end

@testset "WeakRefString{UInt32}" begin
    # simulate UTF32 data with trailing null byte
    data = [0x00000068, 0x00000065, 0x00000079, 0x00000000]

    str = WeakRefStrings.WeakRefString(pointer(data), 4)
    @test typeof(str) == WeakRefStrings.WeakRefString{UInt32}
    @test String(str) == "hey"
    @test str.len == 4
end

@testset "WeakRefArray" begin
    data = codeunits("hey there sailor")
    str = WeakRefStrings.WeakRefString(pointer(data), 3)
    C = WeakRefStringArray(data, [str])
    @test length(C) == 1
    @test eltype(C) === String

    A = WeakRefStringArray(UInt8[], WeakRefStrings.WeakRefString{UInt8}, 10)
    @test size(A) == (10,)
    @test A[1] == ""
    @test A[1, 1] == ""
    A[1] = "hey"
    A[1, 1] = "hey"
    @test A[1] == "hey"
    resize!(A, 5)
    @test size(A) == (5,)
    push!(A, str)
    @test size(A) == (6,)
    @test A[end] == str
    push!(A, "hi")
    @test length(A) == 7
    @test A[end] == convert(WeakRefStrings.WeakRefString{UInt8}, "hi")

    B = WeakRefStringArray(UInt8[], WeakRefStrings.WeakRefString{UInt8}, 10)
    B[1] = "ho"
    append!(A, B)
    @test size(A) == (17,)

    D = WeakRefStringArray(UInt8[], Union{Missing, WeakRefStrings.WeakRefString{UInt8}}, 0)
    push!(D, "hey")
    push!(D, str)
    push!(D, missing)
    @test length(D) == 3
    @test eltype(D) == Union{Missing, String}
    @test D[2] == str
    @test D[3] === missing
    D[2] = missing
    @test D[2] === missing
    deleteat!(D, 1)
    @test length(D) == 2
    @test D[1] === missing

    E = WeakRefStringArray(data, [str missing])
    @test size(E) == (1, 2)
    @test eltype(E) === Union{String, Missing}

end

@testset "StringVector" begin
    s = "Julia is a name without special letters such as æ, ø, and å. Such letters require more than a single byte when encoded in UTF8"
    @testset "split on $splits" for splits in (['.'], [',', '.', ' '])
        sa     = split(s, splits)
        svinit = StringVector{WeakRefString{UInt8}}(sa)
        @testset "version" for sv in (copy(svinit),
                                   copyto!(similar(svinit), svinit),
                                   StringVector{WeakRefString{UInt8}}(sa))
            @test sa == sv
            @test sort(sa) == sort(sv)
            @test copy(sv) == sv

            @testset "setindex with WeakRefString" begin
                # important to start with end because of special branch when
                # lengths are empty and setting last element
                tmp = sv[end]
                sv[end] = "🍕"
                @test sv[end] == "🍕"

                sv[end] = tmp
                sv[1]   = sv[1]
                @test sa == sv
            end

            @testset "setindex with String" begin
                sv[1]   = sa[1]
                sv[end] = sa[end]
                @test sa == sv
            end

            push!(sv, "🍕") == push!(copy(sa), "🍕")
            @test length(empty!(sv)) == 0
        end
    end

    @testset "resize+index" begin
        sv = StringVector(["JuliaDB", "TextParse"])
        @test resize!(sv, 1)[1] == "JuliaDB"
    end

    @testset "filter regex" begin
        sv = StringVector(["TextParse", "TextParse", "JuliaDB", "TextParse", "TextParse", "TextParse", "TextParse", "JuliaDB", "JuliaDB"])
        sv[end] = "Dagger"
        sv[1] = "Dagger"
        @test length(filter(x->occursin(r"JuliaDB", x), sv)) == 2
    end

    @testset "test WeakRefString element type constructor" begin
        @test eltype(StringVector{WeakRefString}(1)) <: WeakRefString
    end

    @testset "test permute!" begin
        sv = StringVector(["TextParse", "TextParse", "JuliaDB", "TextParse", "TextParse", "TextParse", "TextParse", "JuliaDB", "JuliaDB"])
        permute!(sv, reverse!([1:length(sv);]))
        @test sv[1] == "JuliaDB"
        @test sv[end] == "TextParse"
    end

    @testset "vcat" begin
        sv1 = StringVector{String}(unsafe_wrap(Array, pointer(randstring(1024)), 1024), UInt64[1:10:1000;], ones(UInt32,100)*9);
        sv2 = StringVector{String}(unsafe_wrap(Array, pointer(randstring(1024)), 1024), UInt64[1:10:1000;], ones(UInt32,100)*9);
        sv3 = vcat(sv1, sv2)
        @test length(sv3) == length(sv1) + length(sv2)
    end

    @testset "append!" begin
        sv1 = StringVector{WeakRefString{UInt8}}(["foo", "bar"])
        sv2 = StringVector{String}(["baz", "qux"])

        append!(sv1, sv2)
        @test sv1 == ["foo", "bar", "baz", "qux"]

        append!(sv2, ["yep", "nope"])
        @test sv2 == ["baz", "qux", "yep", "nope"]
    end
end


text:
module LACosmic

using Statistics
using StaticArrays
using StaticKernels
using StaticKernels: ExtensionConstant, ExtensionReplicate

export lacosmic

include("subsample.jl")
include("core.jl")

end


text:
@testset "Binary Variables" begin
    lfp = LinearFractionalModel(with_optimizer(Cbc.Optimizer))

    x1 = @variable(lfp, lower_bound=0, base_name="x1", binary=true)
    x2 = @variable(lfp, lower_bound=0, binary=true, base_name="x2")
    @constraint(lfp, -x1 + x2 <= 4)
    @constraint(lfp, 2x1 + x2 <= 14)

    LinearFractional.set_objective(lfp, MOI.MIN_SENSE,
              @expression(lfp, -2x1 + x2 + 2),
              @expression(lfp, x1 + -x2 + 4))

    optimize!(lfp)

    @test termination_status(lfp) === MOI.OPTIMAL
    @test value(x1) ≈ 1.0
    @test value(x2) ≈ 0.0

    @testset "Set the binary big-M" begin
        lfp = LinearFractionalModel(with_optimizer(Cbc.Optimizer); binary_M = 100.0)
        @test lfp.options.binary_M == 100.0

        lfp = LinearFractionalModel(with_optimizer(Cbc.Optimizer); binary_M = 10.0)
        @test lfp.options.binary_M == 10.0
    end
end


text:
using ThArrays
using Test

@testset "Tensor Array interface (except indexing)" begin

    @testset "Tensor array interface" begin
        ary = rand(2, 3)
        ten = Tensor(ary)
        @test eltype(ten) == eltype(ary)
        @test ndims(ten) == ndims(ary)
        @test size(ten) == size(ary)
    end

    @testset "Tensor iteration" begin
        ary = rand(2, 3)
        ten = Tensor(ary)
        for (idx, val) in enumerate(ten)
            @test ary[idx] == val[]
        end
    end

    @testset "Tensor concatenation" begin
        a1 = rand(2, 3)
        a2 = rand(2, 3)
        a3 = rand(2, 3)
        a4 = rand(2, 3)

        t1 = Tensor(a1)
        t2 = Tensor(a2)
        t3 = Tensor(a3)
        t4 = Tensor(a4)

        @test [t1 t2] == Tensor([a1 a2])
        @test [t1; t2] == Tensor([a1; a2])
        @test [t1 t2; t3 t4] == Tensor([a1 a2; a3 a4])
    end
end


text:
using Test
using PWS
using DiffEqJump
using Catalyst
using StaticArrays

traj = PWS.Trajectory([SA[1,4], SA[2,5], SA[3,6]], [1.0, 2.0, 3.0], [1, 1])
traj_mat = PWS.Trajectory([1 2 3; 4 5 6], [1.0, 2.0, 3.0], [1, 1])

@test traj == traj_mat

@test length(traj) == 3 == length(traj.t)
@test collect(traj) == [([1, 4], 1.0, 1), ([2,5], 2.0, 1), ([3, 6], 3.0, 0)]
@test traj(0.0) == [1,4]
@test traj(1.5) == [2,5]
@test traj(2.9) == [3,6]

sn = @reaction_network begin
    0.005, S --> ∅
    0.25, ∅ --> S
end

u0 = [50.0]
tspan = (0., 100.)
discrete_prob = DiscreteProblem(u0, tspan, [])
jump_prob = JumpProblem(sn, discrete_prob, Direct())

integrator = init(jump_prob, SSAStepper(), tstops=Float64[])
ssa_iter = PWS.SSAIter(integrator)

traj_sol = PWS.collect_trajectory(ssa_iter)
@test length(traj_sol.i) == length(traj_sol)
sol = integrator.sol
for i in eachindex(sol)
    if i > 1
        @test sol.t[i] == traj_sol.t[i-1]
        @test sol[i] == traj_sol.u[i]
    end
end

for i in 1:length(traj_sol)-1
    if traj_sol.i[i] == 1
        @test (traj_sol.u[i+1] - traj_sol.u[i]) == [-1]
    else
        @test (traj_sol.u[i+1] - traj_sol.u[i]) == [1]
    end
end

# test SSAIterator
iterator = PWS.SSAIter(init(jump_prob, SSAStepper()))
collected_values = collect(iterator)
times = getindex.(collected_values, 2)
@test times[begin] > 0.0
@test times[end] == 100.0
@test issorted(times)
@test allunique(times)

traj2 = PWS.Trajectory([[1,4], [2,5], [3,6]], [0.5, 1.5, 2.5], [2, 4, 0])

joint = traj |> PWS.MergeWith(traj2)

@test collect(joint) == [
    ([1,4,1,4], 0.5, 2),
    ([1,4,2,5], 1.0, 1),
    ([2,5,2,5], 1.5, 4),
    ([2,5,3,6], 2.0, 1),
    ([3,6,3,6], 2.5, 0),
]

rn = @reaction_network begin
    0.01, S --> X + S
    0.01, X --> ∅
end

network = merge(sn, rn)

u0 = [50.0,50.0]
tspan = (0., 100.)
discrete_prob = DiscreteProblem(u0, tspan, [])
jump_prob = JumpProblem(network, discrete_prob, Direct())
integrator = init(jump_prob, SSAStepper())

partial = PWS.sub_trajectory(PWS.SSAIter(integrator), [1])
sol = integrator.sol
for i in eachindex(partial.t)
    @test partial.t[i] ∈ vcat(sol.t, 100.0)
    @test partial.u[i][1] ∈ sol[[1],:]
end

# test driven jump problem
import PWS
using PWS: DrivenJumpProblem

cb_traj = PWS.Trajectory([[50], [110], [120], [130]], [30.0, 60.0, 90.0, 100.0])
u0 = [50.0]
tspan = (0., 100.)
discrete_prob = DiscreteProblem(sn, u0, tspan, [])
jump_prob = JumpProblem(sn, discrete_prob, Direct())
djp = DrivenJumpProblem(jump_prob, cb_traj)
integrator = init(djp)
iter = PWS.SSAIter(integrator)
traj = iter |> PWS.collect_trajectory

for t in [30, 60, 90] @test t ∈ traj.t end
@test traj(30.0)[1] == 110
@test traj(60.0)[1] == 120
@test traj(90.0)[1] == 130

# sol = integrator.sol
# events = map((u,t,i)::Tuple -> (u=u,t=t), traj) |> collect

# for i in eachindex(events)
#     if i == length(events) continue end
#     @test events[i].t ∈ sol.t
#     j = searchsortedlast(sol.t, events[i].t)
#     @test events[i+1].u == sol.u[j]
# end


text:
module Omniscape
using ArchGDAL
using Base.Threads
using Circuitscape
using DelimitedFiles
using LinearAlgebra.BLAS
using ProgressMeter
using StatsBase
using Statistics

include("config.jl")
include("io.jl")
include("consts.jl")
include("functions.jl")
include("run_omniscape.jl")
include("errors_warnings.jl")
export run_omniscape

end


text:
const MIGRATIONS_FOLDER_NAME = "migrations"

const CONFIG_PATH     = "config"
const ENV_PATH        = joinpath(CONFIG_PATH, "env")
const LOG_PATH        = "log"
const APP_PATH        = "app"
const RESOURCES_PATH  = joinpath(APP_PATH, "resources")
const TEST_PATH       = "test"
const DB_PATH         = "db"
const MIGRATIONS_PATH = joinpath(DB_PATH, MIGRATIONS_FOLDER_NAME)

const SEARCHLIGHT_MODEL_FILE_NAME             = "model.jl"
const SEARCHLIGHT_VALIDATOR_FILE_NAME         = "validator.jl"
const SEARCHLIGHT_DB_CONFIG_FILE_NAME         = "connection.yml"
const SEARCHLIGHT_MIGRATIONS_TABLE_NAME       = "schema_migrations"

const TEST_FILE_IDENTIFIER = "_test.jl"

const SEARCHLIGHT_VALIDATOR_FILE_POSTFIX      = "Validator.jl"

const LAST_INSERT_ID_LABEL = "LAST_INSERT_ID"
const LAST_UPDATE_ID_LABEL = "LAST_UPDATE_ID"


text:
using CUDA
using Adapt

include("../util/Matrix_utils.jl")
include("Drawables.jl")
include("../util/Enum_Collection.jl")

function get_button(name::Enum,buttons::All_Buttons)
    if name === app_close_button
        return buttons.app_close_button
    elseif name === dropdown_button_datei
        return buttons.dropdown_button_datei
    elseif name === dropdown_button_anzeigen
        return buttons.dropdown_button_anzeigen
    elseif name === dropdown_button_navigation
        return buttons.dropdown_button_navigation
    elseif name === dropdown_button_tools
        return buttons.dropdown_button_tools
    elseif name === dropdown_button_hilfe
        return buttons.dropdown_button_hilfe
    elseif name === small_button_1
        return buttons.small_button_1
    elseif name === small_button_2
        return buttons.small_button_2
    elseif name === small_button_3
        return buttons.small_button_3
    elseif name === small_button_4
        return buttons.small_button_4
    elseif name === small_button_5
        return buttons.small_button_5
    elseif name === small_button_6
        return buttons.small_button_6
    elseif name === small_button_7
        return buttons.small_button_7
    elseif name === small_button_8
        return buttons.small_button_8
    elseif name === small_button_9
        return buttons.small_button_9
    elseif name === small_button_10
        return buttons.small_button_10
    elseif name === small_button_11
        return buttons.small_button_11
    elseif name === small_button_12
        return buttons.small_button_12
    elseif name === small_button_13
        return buttons.small_button_13

    else return buttons.app_close_button
    end
end

function draw_self(button::Button,parent_matrix)
    if !(button.status[1,blockIdx().x])
        kernel_blit_image_inplace(threadIdx().x,blockIdx().x,parent_matrix,button.matrix_unclicked,button.x_Coord,button.y_Coord)
    else
        kernel_blit_image_inplace(threadIdx().x,blockIdx().x,parent_matrix,button.matrix_clicked,button.x_Coord,button.y_Coord)
    end

end

function placeholder_function()
    #@cuprintln("Hello!",threadIdx().x)
end

function listen(func::On_click_listeners)
    if func === placeholder
        placeholder_function()
    else
        return
    end
    return
end

function select_matrix(button::Button)
    if button.status[1,blockIdx().x]
        return button.matrix_clicked
    else
        return button.matrix_unclicked
    end
end

function click(x,y,button,parents_x,parents_y)

    if includes_point(x,y,(button.x_Coord+parents_x),(button.y_Coord+parents_y),button.width,button.height)
        if threadIdx().x == 1
            button.status[1,blockIdx().x] = !(button.status[1,blockIdx().x])
        end
        sync_threads()
        if button.status[2,blockIdx().x]
            reward = 0
        else
            button.status[2,blockIdx().x] = true
            reward = button.reward
        end
        listen(button.on_click_listener)
        return reward,true,button.x_Coord,button.y_Coord

    end

    return 0,false,0,0
end


text:
# tests with the 5 taxon networks parameters: net.ht, net.numht
# Claudia January 2015

# test functions

#tests = ["C","F","G","H","J","D","E","I"];
tests = ["F","G","H","J","I"];
wrong = AbstractString[];

function whichtree(t::String)
    if(t == "tree")
        tree = "(((6:0.1,4:1.5)1:0.2,7:0.2)5:0.1,8:0.1,10:0.1);" # normal tree
    elseif(t == "C")
        tree = "((((6:0.1,4:1.5),(7:0.2)11#H1),11#H1),8:0.1,10:0.1);" # Case C: bad triangle II
    elseif(t == "F")
        tree = "(((6:0.1,(4)11#H1)1:0.2,(11#H1,7))5:0.1,8:0.1,10:0.1);" # Case F: bad diamond I
    elseif(t == "G")
        tree = "((((6:0.1,4:1.5)1:0.2,(7)11#H1)5:0.1,(11#H1,8)),10:0.1);" # Case G
    elseif(t == "H")
        tree = "((((6,4),#H1),7),(8)#H1,10);" # Case H
    elseif(t == "J")
        tree = "((((6)#H1,4),7),8,(#H1,10));" # Case J
    elseif(t == "D")
        tree = "((((6,4))#H1,(#H1,7)),8,10);" # Case D Bad triangle I
    elseif(t == "E")
        tree = "(((((8,10))#H1,7),#H1),6,4);" # Case E Bad triangle I
    elseif(t == "I")
        tree = "((((8,10))#H1,7),6,(4,#H1));" # Case I Bad diamond II
    else
        error("not a known 5 taxon network case")
    end
    return tree
end

function whichtest(t::String,net::HybridNetwork)
    if(t == "C")
        all(map(approxEq,net.ht,[exp(-1.)*(1-0.1*(1-exp(-1.))),exp(-1.)*(1-0.9*(1-exp(-1.))),-exp(-2.)*(0.1*(1-exp(-1.))*(1-exp(-1.))*0.9)])) || error("net.ht wrong in case $(t)")
        net.numht == [41,42,43] || error("net.numht wrong in case $(t)")
    elseif(t == "F")
        all(map(approxEq,net.ht,[0.1,0.9*(1-exp(-.2)),0.1*(1-exp(-1.))])) || error("net.ht wrong in case $(t)")
        net.numht == [8,31,32] || error("net.numht wrong in case $(t)")
        net.index == [8,4,6] || error("net.index wrong in case $(t)")
    elseif(t == "G")
        all(map(approxEq,net.ht,[0.1,0.2,0.1,1.0])) || error("net.ht wrong in case $(t)")
        net.numht == [7,3,6,9] || error("net.numht wrong in case $(t)")
        net.index == [7,3,6,9] || error("net.index wrong in case $(t)")
    elseif(t == "H")
        all(map(approxEq,net.ht,[0.1,1.0,1.0,1.0])) || error("net.ht wrong in case $(t)")
        net.numht == [4,3,5,7] || error("net.numht wrong in case $(t)")
        net.index == [4,3,5,7] || error("net.index wrong in case $(t)")
    elseif(t == "J")
        all(map(approxEq,net.ht,[0.1,1.0,1.0,1.0])) || error("net.ht wrong in case $(t)")
        net.numht == [8,4,6,10] || error("net.numht wrong in case $(t)")
        net.index == [8,4,6,10] || error("net.index wrong in case $(t)")
    elseif(t == "D")
        all(map(approxEq,net.ht,[1.0,0.1*(1-exp(-1.)),0.1*0.1*(1-exp(-1.))+0.9*0.9*(1-exp(-2.))])) || error("net.ht wrong in case $(t)")
        net.numht == [8,31,32] || error("net.numht wrong in case $(t)")
    elseif(t == "E")
        all(map(approxEq,net.ht,[1.0,0.9*(1-exp(-1.)),0.1*0.1*(1-exp(-2.))+0.9*0.9*(1-exp(-1.))])) || error("net.ht wrong in case $(t)")
        net.numht == [8,31,32] || error("net.numht wrong in case $(t)")
    elseif(t == "I")
        all(map(approxEq,net.ht,[0.1,2.0,1.0,1.0,1.0])) || error("net.ht wrong in case $(t)")
        net.numht == [9,4,6,9,10] || error("net.numht wrong in case $(t)")
        net.index == [9,4,6,9,10] || error("net.index wrong in case $(t)")
    else
        error("not a known 5 taxon network case")
    end
end

for t in tests
    #println("running $(t)")
    global net = readTopologyLevel1(whichtree(t));
    try
        whichtest(t,net)
    catch
        println("error in $(t)")
        push!(wrong,t);
    end
end

@test isempty(wrong)


text:
"""
	assemble!(model, P, q, constraint(s); [settings, x0, y0, s0])

Assembles a `COSMO.Model` with a cost function defind by `P` and `q`, and a number of `constraints`.

The positive semidefinite matrix `P` and vector `q` are used to specify the cost function of the optimization problem:

```
min   1/2 x'Px + q'x
s.t.  Ax + b ∈ C
```
`constraints` is a `COSMO.Constraint` or an array of `COSMO.Constraint` objects that are used to describe the constraints on `x`.

---
The optional keyword argument `settings` can be used to pass custom solver settings:

```julia
custom_settings = COSMO.Settings(verbose = true);
assemble!(model, P, q, constraints, settings = custom_settings)
```
---
The optional keyword arguments `x0`, `s0`, and `y0` can be used to provide the solver with warm starting values for the primal variable `x`, the primal slack variable `s` and the dual variable `y`.

```julia
x_0 = [1.0; 5.0; 3.0]
COSMO.assemble!(model, P, q, constraints, x0 = x_0)
```

"""
function assemble!(model::Model{T},
	P::AbstractMatrix,
	q::AbstractVector,
	constraints::Union{Constraint{T},Vector{Constraint{T}}}; settings::COSMO.Settings = COSMO.Settings(),
	x0::Union{Vector{T}, Nothing} = nothing, y0::Union{Vector{T}, Nothing} = nothing, s0::Union{Vector{T}, Nothing} = nothing) where{ T<: AbstractFloat}

	!isa(constraints, Array) && (constraints = [constraints])

	merge_constraints!(constraints)
	model.p.P = issparse(P) ? P : sparse(P)
	model.p.q = q
	n = length(q)
	m = sum(map( x-> x.dim, map( x-> x.convex_set, constraints)))

	model.p.model_size = [m; n]

	model.p.A = spzeros(Float64, m, n)
	model.p.b = spzeros(Float64, m)

	check_dimensions(model.p.P, model.p.q, model.p.A, model.p.b)

	# merge and sort the constraint sets
	sort!(constraints, by = x-> sort_sets(x.convex_set))
	row_num = 1
	for con in constraints
		process_constraint!(model.p, row_num, con.A, con.b, con.convex_set, n)
		row_num += con.convex_set.dim
	end

	# save the convex sets inside the model as a composite set
	model.p.C = CompositeConvexSet(map( x-> x.convex_set, constraints))
	model.settings = settings

	# the size of the temporary variables might change if the problem is decomposed
	# only allocate if it's not a cd problem
	pre_allocate_variables!(model)

	# if user provided (full) warm starting variables, update model
	x0 != nothing && warm_start_primal!(model, x0)
	s0 != nothing && warm_start_slack!(model, s0)
	y0 != nothing && warm_start_dual!(model, y0)
	nothing
end

# Handle case where q is a 2-dimensional array instead of a 1-dimensional array
assemble!(model::COSMO.Model{T}, P::AbstractMatrix{T}, q::AbstractMatrix{T}, args...; kwargs...) where {T} = assemble!(model, P, vec(q), args...; kwargs...)
assemble!(model::COSMO.Model{T}, P::AbstractVector, q::AbstractArray, args...; kwargs...) where {T} = assemble!(model, P[:, :], q, args...; kwargs...)
# Handle 1-D cases
assemble!(model::COSMO.Model{T}, P::Real, q::Real, args...; kwargs...) where {T} = assemble!(model, [P], [q], args...; kwargs...)
assemble!(model::COSMO.Model{T}, P::Real, q::Union{AbstractMatrix, AbstractVector}, args...; kwargs...) where {T} = assemble!(model, [P], q, args...; kwargs...)
assemble!(model::COSMO.Model{T}, P::Union{AbstractMatrix, AbstractVector}, q::Real, args...; kwargs...) where {T} = assemble!(model, P, [q], args...; kwargs...)

"""
	empty_model!(model)

Resets all the fields of `model` to that of a model created with `COSMO.Model()` (apart from the settings).
"""
function empty_model!(model::COSMO.Model{T}) where {T}
	model.p = ProblemData{T}()
	model.sm = ScaleMatrices{T}()
	model.vars = Variables{T}(1, 1, model.p.C)
	model.utility_vars = UtilityVariables{T}(1, 1)
	model.ρ = zero(T)
	model.ρvec = T[]
	model.kkt_solver = nothing
	model.flags = Flags()
	model.Info = Info([zero(T)])
	model.times = ResultTimes()
	nothing
end

function _warm_start!(z::Vector{T}, z0::Vector{T}, ind::Union{UnitRange{Int64}, Nothing}) where {T}
		ind == nothing && (ind = 1:length(z))
		length(ind) != length(z0) && throw(DimensionMismatch("Dimension of warm starting vector doesn't match the length of index range ind."))
		z[ind] = z0
end

"""
	warm_start_primal!(model, x0, [ind])

Provides the `COSMO.Model` with warm starting values for the primal variable `x`. `ind` can be used to warm start certain components of `x`.
"""
warm_start_primal!(model::COSMO.Model{T}, x0::Vector{T}, ind::Union{UnitRange{Int64}, Nothing}) where {T} = _warm_start!(model.vars.x, x0, ind)
warm_start_primal!(model::COSMO.Model{T}, x0::Vector{T}) where {T} = warm_start_primal!(model, x0, nothing)
warm_start_primal!(model::COSMO.Model{T}, x0::Real, ind::Int64) where {T} = (model.vars.x[ind] = x0)

"""
	warm_start_slack!(model, s0, [ind])

Provides the `COSMO.Model` with warm starting values for the primal slack variable `s`. `ind` can be used to warm start certain components of `s`.
"""
warm_start_slack!(model::COSMO.Model{T}, s0::Vector{T}, ind::Union{UnitRange{Int64}, Nothing}) where {T} = _warm_start!(model.vars.s.data, s0, ind)
warm_start_slack!(model::COSMO.Model{T}, s0::Vector{T}) where {T} = warm_start_slack!(model, s0, nothing)
warm_start_slack!(model::COSMO.Model{T}, s0::Real, ind::Int64) where {T} = (model.vars.s.data[ind] = s0)

# Notice that the sign of the dual variable y is inverted here, since internally the dual variable μ = -y is used
"""
	warm_start_dual!(model, y0, [ind])

Provides the `COSMO.Model` with warm starting values for the dual variable `y`. `ind` can be used to warm start certain components of `y`.
"""
warm_start_dual!(model::COSMO.Model{T}, y0::Vector{T}, ind::Union{UnitRange{Int64}, Nothing}) where {T} = _warm_start!(model.vars.μ, -y0, ind)
warm_start_dual!(model::COSMO.Model{T}, y0::Vector{T}) where {T} = warm_start_dual!(model, y0, nothing)
warm_start_dual!(model::COSMO.Model{T}, y0::Real, ind::Int64) where {T} = (model.vars.μ[ind] = -y0)

"""
	set!(model, P, q, A, b, convex_sets, [settings])

Sets model data directly based on provided fields.
"""
function set!(model::COSMO.Model,
	P::AbstractMatrix{<:Real},
	q::AbstractVector{<:Real},
	A::AbstractMatrix{<:Real},
	b::AbstractVector{<:Real},
	convex_sets::Vector{<: COSMO.AbstractConvexSet{T}}, settings::COSMO.Settings = COSMO.Settings()) where{T}

	check_dimensions(P, q, A, b)

	# convert inputs and copy them
	P_c = convert_copy(P, SparseMatrixCSC{Float64, Int64})
	A_c = convert_copy(A, SparseMatrixCSC{Float64, Int64})
	q_c = convert_copy(q, Vector{Float64})
	b_c = convert_copy(b, Vector{Float64})

	n = length(q)
	m = length(b)
	model.p.P = P_c
	model.p.q = q_c
	model.p.A = A_c
	model.p.b = b_c
	model.p.model_size = [m; n]
	model.p.C = CompositeConvexSet(deepcopy(convex_sets))

	pre_allocate_variables!(model)
 	model.settings = settings
	nothing
end

function check_dimensions(P, q, A, b)
	size(A, 1) != length(b) && throw(DimensionMismatch("The dimensions of matrix A and vector b don't match."))
	size(A, 2) != length(q) && throw(DimensionMismatch("The dimensions of matrix A and vector q don't match."))
	size(b, 2) != 1 && throw(DimensionMismatch("Input b must be a vector or a scalar."))
	size(P, 1) != length(q) && throw(DimensionMismatch("The dimensions of matrix P and vector q don't match."))
	nothing
end

function check_A_dim(A::Union{AbstractVector{<:Real},AbstractMatrix{<:Real}}, n::Int64)
	size(A, 2) != n && throw(DimensionMismatch("The dimensions of a matrix A (m x $(size(A, 2))) in one of the constraints is inconsistent with the dimension of P ($(n))."))
end

# convert x into type (which creates a copy) or copy x if type coincides
function convert_copy(x::AbstractArray, type::Type)
	if typeof(x) == type
		x_c = copy(x)
	else
		x_c = convert(type, x)
	end
	return x_c
end

# merge zeros sets and nonnegative sets
function merge_constraints!(constraints::Array{COSMO.Constraint{T}}) where{T}
	# handle zeros sets
	ind = findall(set->typeof(set) == ZeroSet{T}, map(x -> x.convex_set, constraints))
	if length(ind) > 1
		M = merge_zeros(constraints[ind])
		deleteat!(constraints, ind)
		push!(constraints, M)
	end

	# handle nonnegative sets
	ind = findall(set->typeof(set) == Nonnegatives{T},map(x->x.convex_set,constraints))
	if length(ind) > 1
		M = merge_nonnegatives(constraints[ind])
		deleteat!(constraints, ind)
		push!(constraints, M)
	end
	nothing
end

function merge_zeros(constraints::Array{COSMO.Constraint{T}}) where{T}
	m = sum(x -> x.dim, map(x -> x.convex_set, constraints))
	n = size(constraints[1].A, 2)
	A = spzeros(m, n)
	b = zeros(m)
	s = 1
	e = 0
	for cons in constraints
		e = s + cons.convex_set.dim - 1
		A[s:e, :] = cons.A
		b[s:e, :] = cons.b
		s = e + 1
	end
	return M = COSMO.Constraint(A, b, ZeroSet)
end

function merge_nonnegatives(constraints::Array{COSMO.Constraint{T}}) where{T}
	m = sum(x -> x.dim, map(x -> x.convex_set, constraints))
	n = size(constraints[1].A, 2)
	A = spzeros(m, n)
	b = zeros(m)

	s = 1
	e = 0
	for cons in constraints
		e = s + cons.convex_set.dim - 1
		A[s:e, :] = cons.A
		b[s:e, :] = cons.b
		s = e + 1
	end

	return M = COSMO.Constraint(A, b, Nonnegatives)
end

function sort_sets(C::AbstractConvexSet)
  C = typeof(C)
  (C <: ZeroSet) && return 1
  (C <: Nonnegatives) && return 2
  (C <: Box) && return 3
  (C <: SecondOrderCone) && return 4
  (C <: PsdCone) && return 5
  (C <: PsdConeTriangle) && return 6
  return 6
end

# transform A*x + b in {0}, to A*x + s == b, s in {0}
function process_constraint!(p::COSMO.ProblemData, row_num::Int64, A::Union{AbstractVector{<:Real},AbstractMatrix{<:Real}}, b::AbstractVector{<:Real}, C::AbstractConvexSet, n::Int64)
	check_A_dim(A, n)
	s = row_num
	e = row_num + C.dim - 1
	p.A[s:e, :] = -A
	p.b[s:e, :] = b
end

function pre_allocate_variables!(ws::Workspace{T}) where{T}
	m, n = ws.p.model_size
  ws.vars = Variables{T}(m, n, ws.p.C)
  ws.utility_vars = UtilityVariables{T}(m, n)
end