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

"""
    create_budget(account_id, budget)
    create_budget(account_id, budget, params::Dict{String,<:Any})

Creates a budget and, if included, notifications and subscribers.   Only one of BudgetLimit
or PlannedBudgetLimits can be present in the syntax at one time. Use the syntax that
matches your case. The Request Syntax section shows the BudgetLimit syntax. For
PlannedBudgetLimits, see the Examples section.

# Arguments
- `account_id`: The accountId that is associated with the budget.
- `budget`: The budget object that you want to create.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"NotificationsWithSubscribers"`: A notification that you want to associate with a
  budget. A budget can have up to five notifications, and each notification can have one SNS
  subscriber and up to 10 email subscribers. If you include notifications and subscribers in
  your CreateBudget call, AWS creates the notifications and subscribers for you.
"""
function create_budget(AccountId, Budget; aws_config::AbstractAWSConfig=global_aws_config())
    return budgets(
        "CreateBudget",
        Dict{String,Any}("AccountId" => AccountId, "Budget" => Budget);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_budget(
    AccountId,
    Budget,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateBudget",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "Budget" => Budget),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_budget_action(account_id, action_threshold, action_type, approval_model, budget_name, definition, execution_role_arn, notification_type, subscribers)
    create_budget_action(account_id, action_threshold, action_type, approval_model, budget_name, definition, execution_role_arn, notification_type, subscribers, params::Dict{String,<:Any})

 Creates a budget action.

# Arguments
- `account_id`:
- `action_threshold`:
- `action_type`:  The type of action. This defines the type of tasks that can be carried
  out by this action. This field also determines the format for definition.
- `approval_model`:  This specifies if the action needs manual or automatic approval.
- `budget_name`:
- `definition`:
- `execution_role_arn`:  The role passed for action execution and reversion. Roles and
  actions must be in the same account.
- `notification_type`:
- `subscribers`:

"""
function create_budget_action(
    AccountId,
    ActionThreshold,
    ActionType,
    ApprovalModel,
    BudgetName,
    Definition,
    ExecutionRoleArn,
    NotificationType,
    Subscribers;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateBudgetAction",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "ActionThreshold" => ActionThreshold,
            "ActionType" => ActionType,
            "ApprovalModel" => ApprovalModel,
            "BudgetName" => BudgetName,
            "Definition" => Definition,
            "ExecutionRoleArn" => ExecutionRoleArn,
            "NotificationType" => NotificationType,
            "Subscribers" => Subscribers,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_budget_action(
    AccountId,
    ActionThreshold,
    ActionType,
    ApprovalModel,
    BudgetName,
    Definition,
    ExecutionRoleArn,
    NotificationType,
    Subscribers,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateBudgetAction",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "ActionThreshold" => ActionThreshold,
                    "ActionType" => ActionType,
                    "ApprovalModel" => ApprovalModel,
                    "BudgetName" => BudgetName,
                    "Definition" => Definition,
                    "ExecutionRoleArn" => ExecutionRoleArn,
                    "NotificationType" => NotificationType,
                    "Subscribers" => Subscribers,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_notification(account_id, budget_name, notification, subscribers)
    create_notification(account_id, budget_name, notification, subscribers, params::Dict{String,<:Any})

Creates a notification. You must create the budget before you create the associated
notification.

# Arguments
- `account_id`: The accountId that is associated with the budget that you want to create a
  notification for.
- `budget_name`: The name of the budget that you want AWS to notify you about. Budget names
  must be unique within an account.
- `notification`: The notification that you want to create.
- `subscribers`: A list of subscribers that you want to associate with the notification.
  Each notification can have one SNS subscriber and up to 10 email subscribers.

"""
function create_notification(
    AccountId,
    BudgetName,
    Notification,
    Subscribers;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateNotification",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "Notification" => Notification,
            "Subscribers" => Subscribers,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_notification(
    AccountId,
    BudgetName,
    Notification,
    Subscribers,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateNotification",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "Notification" => Notification,
                    "Subscribers" => Subscribers,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_subscriber(account_id, budget_name, notification, subscriber)
    create_subscriber(account_id, budget_name, notification, subscriber, params::Dict{String,<:Any})

Creates a subscriber. You must create the associated budget and notification before you
create the subscriber.

# Arguments
- `account_id`: The accountId that is associated with the budget that you want to create a
  subscriber for.
- `budget_name`: The name of the budget that you want to subscribe to. Budget names must be
  unique within an account.
- `notification`: The notification that you want to create a subscriber for.
- `subscriber`: The subscriber that you want to associate with a budget notification.

"""
function create_subscriber(
    AccountId,
    BudgetName,
    Notification,
    Subscriber;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateSubscriber",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "Notification" => Notification,
            "Subscriber" => Subscriber,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_subscriber(
    AccountId,
    BudgetName,
    Notification,
    Subscriber,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "CreateSubscriber",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "Notification" => Notification,
                    "Subscriber" => Subscriber,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_budget(account_id, budget_name)
    delete_budget(account_id, budget_name, params::Dict{String,<:Any})

Deletes a budget. You can delete your budget at any time.  Deleting a budget also deletes
the notifications and subscribers that are associated with that budget.

# Arguments
- `account_id`: The accountId that is associated with the budget that you want to delete.
- `budget_name`: The name of the budget that you want to delete.

"""
function delete_budget(
    AccountId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DeleteBudget",
        Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_budget(
    AccountId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DeleteBudget",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_budget_action(account_id, action_id, budget_name)
    delete_budget_action(account_id, action_id, budget_name, params::Dict{String,<:Any})

 Deletes a budget action.

# Arguments
- `account_id`:
- `action_id`:  A system-generated universally unique identifier (UUID) for the action.
- `budget_name`:

"""
function delete_budget_action(
    AccountId, ActionId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DeleteBudgetAction",
        Dict{String,Any}(
            "AccountId" => AccountId, "ActionId" => ActionId, "BudgetName" => BudgetName
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_budget_action(
    AccountId,
    ActionId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DeleteBudgetAction",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "ActionId" => ActionId,
                    "BudgetName" => BudgetName,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_notification(account_id, budget_name, notification)
    delete_notification(account_id, budget_name, notification, params::Dict{String,<:Any})

Deletes a notification.  Deleting a notification also deletes the subscribers that are
associated with the notification.

# Arguments
- `account_id`: The accountId that is associated with the budget whose notification you
  want to delete.
- `budget_name`: The name of the budget whose notification you want to delete.
- `notification`: The notification that you want to delete.

"""
function delete_notification(
    AccountId, BudgetName, Notification; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DeleteNotification",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "Notification" => Notification,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_notification(
    AccountId,
    BudgetName,
    Notification,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DeleteNotification",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "Notification" => Notification,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_subscriber(account_id, budget_name, notification, subscriber)
    delete_subscriber(account_id, budget_name, notification, subscriber, params::Dict{String,<:Any})

Deletes a subscriber.  Deleting the last subscriber to a notification also deletes the
notification.

# Arguments
- `account_id`: The accountId that is associated with the budget whose subscriber you want
  to delete.
- `budget_name`: The name of the budget whose subscriber you want to delete.
- `notification`: The notification whose subscriber you want to delete.
- `subscriber`: The subscriber that you want to delete.

"""
function delete_subscriber(
    AccountId,
    BudgetName,
    Notification,
    Subscriber;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DeleteSubscriber",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "Notification" => Notification,
            "Subscriber" => Subscriber,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_subscriber(
    AccountId,
    BudgetName,
    Notification,
    Subscriber,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DeleteSubscriber",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "Notification" => Notification,
                    "Subscriber" => Subscriber,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_budget(account_id, budget_name)
    describe_budget(account_id, budget_name, params::Dict{String,<:Any})

Describes a budget.  The Request Syntax section shows the BudgetLimit syntax. For
PlannedBudgetLimits, see the Examples section.

# Arguments
- `account_id`: The accountId that is associated with the budget that you want a
  description of.
- `budget_name`: The name of the budget that you want a description of.

"""
function describe_budget(
    AccountId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeBudget",
        Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budget(
    AccountId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudget",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_budget_action(account_id, action_id, budget_name)
    describe_budget_action(account_id, action_id, budget_name, params::Dict{String,<:Any})

 Describes a budget action detail.

# Arguments
- `account_id`:
- `action_id`:  A system-generated universally unique identifier (UUID) for the action.
- `budget_name`:

"""
function describe_budget_action(
    AccountId, ActionId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeBudgetAction",
        Dict{String,Any}(
            "AccountId" => AccountId, "ActionId" => ActionId, "BudgetName" => BudgetName
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budget_action(
    AccountId,
    ActionId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudgetAction",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "ActionId" => ActionId,
                    "BudgetName" => BudgetName,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_budget_action_histories(account_id, action_id, budget_name)
    describe_budget_action_histories(account_id, action_id, budget_name, params::Dict{String,<:Any})

 Describes a budget action history detail.

# Arguments
- `account_id`:
- `action_id`:  A system-generated universally unique identifier (UUID) for the action.
- `budget_name`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`:
- `"NextToken"`:
- `"TimePeriod"`:
"""
function describe_budget_action_histories(
    AccountId, ActionId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeBudgetActionHistories",
        Dict{String,Any}(
            "AccountId" => AccountId, "ActionId" => ActionId, "BudgetName" => BudgetName
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budget_action_histories(
    AccountId,
    ActionId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudgetActionHistories",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "ActionId" => ActionId,
                    "BudgetName" => BudgetName,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

 Describes all of the budget actions for an account.

# Arguments
- `account_id`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`:
- `"NextToken"`:
"""
function describe_budget_actions_for_account(
    AccountId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeBudgetActionsForAccount",
        Dict{String,Any}("AccountId" => AccountId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budget_actions_for_account(
    AccountId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudgetActionsForAccount",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("AccountId" => AccountId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_budget_actions_for_budget(account_id, budget_name)
    describe_budget_actions_for_budget(account_id, budget_name, params::Dict{String,<:Any})

 Describes all of the budget actions for a budget.

# Arguments
- `account_id`:
- `budget_name`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`:
- `"NextToken"`:
"""
function describe_budget_actions_for_budget(
    AccountId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeBudgetActionsForBudget",
        Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budget_actions_for_budget(
    AccountId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudgetActionsForBudget",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_budget_performance_history(account_id, budget_name)
    describe_budget_performance_history(account_id, budget_name, params::Dict{String,<:Any})

Describes the history for DAILY, MONTHLY, and QUARTERLY budgets. Budget history isn't
available for ANNUAL budgets.

# Arguments
- `account_id`:
- `budget_name`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`:
- `"NextToken"`:
- `"TimePeriod"`: Retrieves how often the budget went into an ALARM state for the specified
  time period.
"""
function describe_budget_performance_history(
    AccountId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeBudgetPerformanceHistory",
        Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budget_performance_history(
    AccountId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudgetPerformanceHistory",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the budgets that are associated with an account.  The Request Syntax section shows
the BudgetLimit syntax. For PlannedBudgetLimits, see the Examples section.

# Arguments
- `account_id`: The accountId that is associated with the budgets that you want
  descriptions of.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: An optional integer that represents how many entries a paginated response
  contains. The maximum is 100.
- `"NextToken"`: The pagination token that you include in your request to indicate the next
  set of results that you want to retrieve.
"""
function describe_budgets(AccountId; aws_config::AbstractAWSConfig=global_aws_config())
    return budgets(
        "DescribeBudgets",
        Dict{String,Any}("AccountId" => AccountId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_budgets(
    AccountId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeBudgets",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("AccountId" => AccountId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_notifications_for_budget(account_id, budget_name)
    describe_notifications_for_budget(account_id, budget_name, params::Dict{String,<:Any})

Lists the notifications that are associated with a budget.

# Arguments
- `account_id`: The accountId that is associated with the budget whose notifications you
  want descriptions of.
- `budget_name`: The name of the budget whose notifications you want descriptions of.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: An optional integer that represents how many entries a paginated response
  contains. The maximum is 100.
- `"NextToken"`: The pagination token that you include in your request to indicate the next
  set of results that you want to retrieve.
"""
function describe_notifications_for_budget(
    AccountId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeNotificationsForBudget",
        Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_notifications_for_budget(
    AccountId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeNotificationsForBudget",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "BudgetName" => BudgetName),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_subscribers_for_notification(account_id, budget_name, notification)
    describe_subscribers_for_notification(account_id, budget_name, notification, params::Dict{String,<:Any})

Lists the subscribers that are associated with a notification.

# Arguments
- `account_id`: The accountId that is associated with the budget whose subscribers you want
  descriptions of.
- `budget_name`: The name of the budget whose subscribers you want descriptions of.
- `notification`: The notification whose subscribers you want to list.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: An optional integer that represents how many entries a paginated response
  contains. The maximum is 100.
- `"NextToken"`: The pagination token that you include in your request to indicate the next
  set of results that you want to retrieve.
"""
function describe_subscribers_for_notification(
    AccountId, BudgetName, Notification; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "DescribeSubscribersForNotification",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "Notification" => Notification,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_subscribers_for_notification(
    AccountId,
    BudgetName,
    Notification,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "DescribeSubscribersForNotification",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "Notification" => Notification,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    execute_budget_action(account_id, action_id, budget_name, execution_type)
    execute_budget_action(account_id, action_id, budget_name, execution_type, params::Dict{String,<:Any})

 Executes a budget action.

# Arguments
- `account_id`:
- `action_id`:  A system-generated universally unique identifier (UUID) for the action.
- `budget_name`:
- `execution_type`:  The type of execution.

"""
function execute_budget_action(
    AccountId,
    ActionId,
    BudgetName,
    ExecutionType;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "ExecuteBudgetAction",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "ActionId" => ActionId,
            "BudgetName" => BudgetName,
            "ExecutionType" => ExecutionType,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function execute_budget_action(
    AccountId,
    ActionId,
    BudgetName,
    ExecutionType,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "ExecuteBudgetAction",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "ActionId" => ActionId,
                    "BudgetName" => BudgetName,
                    "ExecutionType" => ExecutionType,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_budget(account_id, new_budget)
    update_budget(account_id, new_budget, params::Dict{String,<:Any})

Updates a budget. You can change every part of a budget except for the budgetName and the
calculatedSpend. When you modify a budget, the calculatedSpend drops to zero until AWS has
new usage data to use for forecasting.  Only one of BudgetLimit or PlannedBudgetLimits can
be present in the syntax at one time. Use the syntax that matches your case. The Request
Syntax section shows the BudgetLimit syntax. For PlannedBudgetLimits, see the Examples
section.

# Arguments
- `account_id`: The accountId that is associated with the budget that you want to update.
- `new_budget`: The budget that you want to update your budget to.

"""
function update_budget(
    AccountId, NewBudget; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "UpdateBudget",
        Dict{String,Any}("AccountId" => AccountId, "NewBudget" => NewBudget);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_budget(
    AccountId,
    NewBudget,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "UpdateBudget",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AccountId" => AccountId, "NewBudget" => NewBudget),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_budget_action(account_id, action_id, budget_name)
    update_budget_action(account_id, action_id, budget_name, params::Dict{String,<:Any})

 Updates a budget action.

# Arguments
- `account_id`:
- `action_id`:  A system-generated universally unique identifier (UUID) for the action.
- `budget_name`:

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ActionThreshold"`:
- `"ApprovalModel"`:  This specifies if the action needs manual or automatic approval.
- `"Definition"`:
- `"ExecutionRoleArn"`:  The role passed for action execution and reversion. Roles and
  actions must be in the same account.
- `"NotificationType"`:
- `"Subscribers"`:
"""
function update_budget_action(
    AccountId, ActionId, BudgetName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return budgets(
        "UpdateBudgetAction",
        Dict{String,Any}(
            "AccountId" => AccountId, "ActionId" => ActionId, "BudgetName" => BudgetName
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_budget_action(
    AccountId,
    ActionId,
    BudgetName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "UpdateBudgetAction",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "ActionId" => ActionId,
                    "BudgetName" => BudgetName,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_notification(account_id, budget_name, new_notification, old_notification)
    update_notification(account_id, budget_name, new_notification, old_notification, params::Dict{String,<:Any})

Updates a notification.

# Arguments
- `account_id`: The accountId that is associated with the budget whose notification you
  want to update.
- `budget_name`: The name of the budget whose notification you want to update.
- `new_notification`: The updated notification to be associated with a budget.
- `old_notification`: The previous notification that is associated with a budget.

"""
function update_notification(
    AccountId,
    BudgetName,
    NewNotification,
    OldNotification;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "UpdateNotification",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "NewNotification" => NewNotification,
            "OldNotification" => OldNotification,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_notification(
    AccountId,
    BudgetName,
    NewNotification,
    OldNotification,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "UpdateNotification",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "NewNotification" => NewNotification,
                    "OldNotification" => OldNotification,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_subscriber(account_id, budget_name, new_subscriber, notification, old_subscriber)
    update_subscriber(account_id, budget_name, new_subscriber, notification, old_subscriber, params::Dict{String,<:Any})

Updates a subscriber.

# Arguments
- `account_id`: The accountId that is associated with the budget whose subscriber you want
  to update.
- `budget_name`: The name of the budget whose subscriber you want to update.
- `new_subscriber`: The updated subscriber that is associated with a budget notification.
- `notification`: The notification whose subscriber you want to update.
- `old_subscriber`: The previous subscriber that is associated with a budget notification.

"""
function update_subscriber(
    AccountId,
    BudgetName,
    NewSubscriber,
    Notification,
    OldSubscriber;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "UpdateSubscriber",
        Dict{String,Any}(
            "AccountId" => AccountId,
            "BudgetName" => BudgetName,
            "NewSubscriber" => NewSubscriber,
            "Notification" => Notification,
            "OldSubscriber" => OldSubscriber,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_subscriber(
    AccountId,
    BudgetName,
    NewSubscriber,
    Notification,
    OldSubscriber,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return budgets(
        "UpdateSubscriber",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccountId" => AccountId,
                    "BudgetName" => BudgetName,
                    "NewSubscriber" => NewSubscriber,
                    "Notification" => Notification,
                    "OldSubscriber" => OldSubscriber,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
include("../include.jl")

using JuMP #,

## OBJ DOES NOT MATCH

function create_2(n_p::Int64)
  m = Model()
  #@variable(m, x[i=1:n_p], start=(i - 0.5 * n_p)/n_p)
  #@variable(m, y[i=1:n_p], start=(i - 0.5 * n_p)/n_p)
  #@variable(m, z[i=1:n_p], start=(i - 0.5 * n_p)/n_p)
  @variable(m, x[i=1:n_p], start=rand())
  @variable(m, y[i=1:n_p], start=rand())
  @variable(m, z[i=1:n_p], start=rand())

  @NLobjective(m, Min, sum(sum(((x[i] - x[j])^2 + (y[i] - y[j])^2 + (z[i] - z[j])^2)^(-0.5) for j = (i+1):n_p) for i = 1:(n_p - 1)))

  for i = 1:n_p
    @NLconstraint(m, x[i]^2 + y[i]^2 + z[i]^2 == 1.0)
  end

  return m;
end

srand(1)
m = create_2(100)

nlp_raw = MathProgNLPModel(m);
my_pars = Class_parameters();
it = one_phase_solve(nlp_raw,my_pars);
it.point.x

#=
using Ipopt
setsolver(m,IpoptSolver())
solve(m)
=#

ext: jl


text:
include("cpu.jl")

c = CPU([1,0,0,0,0,0])

p = read_program("inputs/19.txt")

ips = []
function part_2()::CPU
    c = CPU([1,0,0,0,0,0])
    while true
        #push!(ips, c.registers[p.ip+1])
        try
            ins = p.lines[c.registers[p.ip+1]]
            @fastmath run_instruction(c, ins)
            @fastmath c.registers[p.ip+1] += 1
            #@show c
        catch
            return c
        end
    end
    return c
end

p2 = part_2()

# 3 mulr F E C # C = F * E
# 4 eqrr C B C # C = C == B ? 1 : 0
# 5 addr C D D # ip += C
# 6 addi D 1 D # ip += 1
# 7 addr F A A # A += F
# 8 addi E 1 E # E += 1
# 9 gtrr E B C # C = E > B ? 1 : 0
# 10 addr D C D # ip += C -> if E > B exit, else keep looping
#  while E <= B...

# 11 seti 2 X D # ip = 2 -> loop to 3
D = 16
F = 1
E = 1

while E <= B
    C = F*E
    if C == B
        A += F
    end
    E += 1
end

r3 = 10551343

for r1 in 1:r3
    for r5 in 1:r3
        if r1*r5 == r3
            ro += r1

function get_answer()
    s = 0
    for x in 1:10551343
        if 10551343 % x == 0
            s += x
        end
    end
    return s
end

a = get_answer()


text:
using FFTW
export Fourier, FrequencySplit

# TODO: Allow width to the removed frequencies, ie. remove
# a Gaussian around the specified frequency
# This is possibly done via a combination of the
# DSP.Filters.Bandstop and probably some windowing function from DSP.

"""
    Fourier([s, ] frequencies, x=true) <: Decomposition
Decompose a timeseries `s` into a **sum** `x + r`, by identifying specific `frequencies`
at the Fourier space and removing them from the signal.
`x` is the removed periodic component while `r` is the residual.
If a given frequency is not exactly matching the Fourier frequencies, the closest one is
removed.

**Important**: periods/frequencies are defined with respect to the `t` axis length,
the actual `t` values are not used in this method. So, frequency `1/12` (a period of `12`)
means `12` data points (whose actual value depends on `t`).

If you provide `s` the method plans the forward and inverse
Fourier transforms (so that it is efficient to re-use it for `s` of same type and length).

This method works well when a periodic signal P is superimposed on fluctuations S,
and you have a good educated guess of what frequencies compose P.
This method works well if the given signal has length multiple of the periods given.

There is arbitrarity of which part of the signal `x, r`
gets the mean value of `s`, because it is deducted for a better fit.
The argument `x=true` attributes it to `x`, use `false` for `r`.
"""
struct Fourier{F, I} <: Decomposition
    fs::Vector{Float64}
    forward::F
    inverse::I
    x::Bool
end
Fourier(fs, x::Bool=true) = Fourier(fs, nothing, nothing, x)
function Fourier(s::AbstractVector, fs::AbstractVector, x::Bool=true)
    forward = plan_rfft(s)
    inverse = plan_irfft(forward*s, length(s))
    return Fourier(fs, forward, inverse, x)
end

function decompose(t, s, method::Fourier)
    isequispaced(t) || error("Input time axis must be equispaced for method Fourier.")
    if length(s) % round(Int, maximum(1/f for f in method.fs)) ≠ 0
        @warn "The signal length is not a multiple of largest period."
    end

    m = mean(s)
    𝓕 = isnothing(method.forward) ? rfft(s .- m) : method.forward*(s .- m)
    fs = rfftfreq(length(s))
    for f in method.fs
        i = findnearest(f, fs)
        𝓕[i] = 0.0
    end
    inv_𝓕 = isnothing(method.inverse) ? irfft(𝓕, length(s)) : method.inverse*𝓕
    residual = inv_𝓕
    periodic = s .- residual
    if !method.x
        periodic .-= m
        residual .+= m
    end
    return periodic, residual
end

"""
    FrequencySplit([s, ] f::Real) <: Decomposition
Similar to the [`Fourier`](@ref) method, but now the "residual" signal is the part
of `s` with frequencies higher than `f`, while the "seasonal" part has frequencies `≤ f`.
"""
struct FrequencySplit{F, I} <: Decomposition
    f::Float64
    forward::F
    inverse::I
end
FrequencySplit(fs) = FrequencySplit(fs, nothing, nothing)
function FrequencySplit(s, fs)
    forward = plan_rfft(s)
    inverse = plan_irfft(forward*s, length(s))
    return FrequencySplit(fs, forward, inverse)
end

function decompose(t, s, method::FrequencySplit)
    isequispaced(t) || error("Input time axis must be equispaced for method FrequencySplit.")

    m = mean(s)
    𝓕 = isnothing(method.forward) ? rfft(s .- m) : method.forward*(s .- m)
    fs = rfftfreq(length(s))
    i = findlast(f -> f ≤ method.f, fs)
    𝓕[1:i+1] .= 0.0
    inv_𝓕 = isnothing(method.inverse) ? irfft(𝓕, length(s)) : method.inverse*𝓕
    residual = inv_𝓕
    periodic = s .- residual
    return periodic, residual
end


text:
# ## Plotting

# Now we can plot the criterion values over time to see how the clustering process and ICVI evaluation evolve over time.
# Every ICVI behaves differently, so feel free to experiment with different ICVIs to see their trend lines in cases like that in this notebook.

## Plot the ICVIs over time
# p = scatter(collect(1:n_samples), criterion_values, title="CH ICVI on DDVFA Streaming Clustering of Iris Data")
# plot(p, legend = false, xtickfontsize=6, xguidefontsize=8, titlefont=font(8))
# title!("CH ICVI on DDVFA Streaming Clustering of Iris Data")
# xlabel!("Sample Index")
# ylabel!("Criterion Value")
# xlims!(1, n_samples)
# ylims!(0, Inf)
# try
#     display(p)
# catch
# end


text:
#############################################################################
#Copyright (c) 2016 Charles Le Losq
#
#The MIT License (MIT)
#
#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.
#
# baseline.jl contains several "baseline" functions. It is directly dependent on functions.jl.
#
#############################################################################

using JuMP
using Ipopt

include("gcvspl_wrapper.jl")

function baseline(x::Array{Float64},y::Array{Float64},roi::Array{Float64},basetype::AbstractString;p=1.0,SplOrder=3)
    ### PRELIMINARY CHECK: INCREASING SIGNAL
	if x[end,1] < x[1,1]
	    x = flipdim(x,1)
		y = flipdim(y,1)
	end

	#### PRELIMINARY STEP: FIRST WE GRAB THE GOOD SIGNAL IN THE ROI
    interest_index::Array{Int64} = find(roi[1,1] .<= x[:,1] .<= roi[1,2])
    if size(roi)[1] > 1
        for i = 2:size(roi)[1]
            interest_index = vcat(interest_index,  find(roi[i,1] .<= x[:,1] .<= roi[i,2]))
        end
    end
    interest_x = x[interest_index,1]
    interest_y = y[interest_index,1]
	ese_interest_y = sqrt(abs(interest_y)) # we assume errors as sqrt(y)

	#### THEN WE GO TO THE RIGHT METHOD FOR BASELINE CALCULATION

	######## POLYNOMIAL BASELINE
	if basetype == "poly"
        # The model for fitting baseline to roi signal
        #mod = Model(solver=IpoptSolver(print_level=0))
        #n::Int = size(p)[1] # number of coefficients
        #m::Int = size(interest_x)[1] # number of data

        #@variable(mod,p_val[i=1:n])
        #setvalue(p_val[i=1:n], p[i])
        #@NLobjective(mod,Min,sum{( sum{p_val[i]*interest_x[j]^(i-1), i=1:n} - interest_y[j])^2, j=1:m})
        #status = solve(mod)
        #println("Solver status: ", status)
		#best_p::Vector{Float64} = getvalue(p_val)
		best_p::Array{Float64} = polyfit(interest_x,interest_y,round(Int,p))
        y_calc::Array{Float64} = poly(best_p,x)
        return y[:,1] - y_calc, y_calc

	######## DIERCKX SPLINE BASELINE
	elseif basetype == "Dspline"
		spl = Spline1D(interest_x,interest_y,s=p[1],bc="extrapolate",k=SplOrder)
		y_calc = evaluate(spl,x[:,1])
		return y[:,1] - y_calc, y_calc

	######## GCV SPLINE BASELINE
	elseif basetype == "gcvspline"
		c, WK, IER = gcvspl_julia(interest_x[:,1],interest_y[:,1],ese_interest_y[:,1],p[1];SplineOrder = Int32(SplOrder-1)) # with cubic spline as the default
		y_calc = splderivative_julia(x[:,1],interest_x,c,SplineOrder= Int32(SplOrder-1))
		return y[:,1] - y_calc, y_calc

	######## KERNEL RIDGE REGRESSION WITH SCIKIT LEARN
	elseif basetype == "KRregression"
		# to be sure everything is in the right shape for SciKit Learn API
		interest_x = reshape(interest_x,size(interest_x,1),1)
		interest_y = reshape(interest_y,size(interest_y,1),1)

		# initialising the preprocessor scaler
		X_scaler = preprocessing[:StandardScaler]()
		Y_scaler = preprocessing[:StandardScaler]()

		X_scaler[:fit](interest_x)
		Y_scaler[:fit](interest_y)

		#scaling the data
		x_bas_sc = X_scaler[:transform](interest_x)
		y_bas_sc = Y_scaler[:transform](interest_y)
		x_sc = X_scaler[:transform](reshape(x,size(x,1),1))

		# constructing a GridSearchCV instance for grabing the best parameters
		clf = kernel_ridge[:KernelRidge](kernel="rbf", gamma=0.1)
		kr = grid_search[:GridSearchCV](clf,cv=5,param_grid=Dict("alpha"=> [1e1, 1e0, 0.5, 0.1, 5e-2, 1e-2, 5e-3, 1e-3,1e-4],"gamma"=> logspace(-4, 4, 9)))
		kr[:fit](x_bas_sc, squeeze(y_bas_sc,2)) #SciKit learn is expecting a y vector, not an array...
		y_kr_sc = kr[:predict](x_sc)
		y_kr = Y_scaler[:inverse_transform](y_kr_sc)
		return y[:,1] - y_kr, y_kr

	######## SUPPORT VECTOR MACHINES REGRESSION WITH SCIKIT LEARN
	elseif basetype == "SVMregression"
		# to be sure everything is in the right shape for SciKit Learn API
		interest_x = reshape(interest_x,size(interest_x,1),1)
		interest_y = reshape(interest_y,size(interest_y,1),1)

		# initialising the preprocessor scaler
		X_scaler = preprocessing[:StandardScaler]()
		Y_scaler = preprocessing[:StandardScaler]()

		X_scaler[:fit](interest_x)
		Y_scaler[:fit](interest_y)

		#scaling the data
		x_bas_sc = X_scaler[:transform](interest_x)
		y_bas_sc = Y_scaler[:transform](interest_y)
		x_sc = X_scaler[:transform](reshape(x,size(x,1),1))

		# constructing a GridSearchCV instance for grabing the best parameters
		clf = svm[:SVR](kernel="rbf", gamma=0.1)
		svr = grid_search[:GridSearchCV](clf,cv=5,param_grid=Dict("C"=> [1e-1, 1e0, 1e1, 1e2, 1e3],"gamma"=> logspace(-4, 4, 9)))
		svr[:fit](x_bas_sc, squeeze(y_bas_sc,2)) #SciKit learn is expecting a y vector, not an array...
		y_svr_sc = svr[:predict](x_sc)
		y_svr = Y_scaler[:inverse_transform](y_svr_sc)
		return y[:,1] - y_svr, y_svr

		######## GRADIENT BOOSTING ENSEMBLE REGRESSION WITH SCIKIT LEARN
		elseif basetype == "GPregression"
			# to be sure everything is in the right shape for SciKit Learn API
			interest_x = reshape(interest_x,size(interest_x,1),1)
			interest_y = reshape(interest_y,size(interest_y,1),1)
			ese_interest_y = reshape(ese_interest_y,size(ese_interest_y,1),1)

			# initialising the preprocessor scaler
			X_scaler = preprocessing[:StandardScaler]()
			Y_scaler = preprocessing[:StandardScaler]()

			X_scaler[:fit](interest_x)
			Y_scaler[:fit](interest_y)

			#scaling the data
			x_bas_sc = X_scaler[:transform](interest_x)
			y_bas_sc = Y_scaler[:transform](interest_y)
			x_sc = X_scaler[:transform](reshape(x,size(x,1),1))

			# constructing a GridSearchCV instance for grabing the best parameters
			gpr = gaussian_process[:GaussianProcess](corr="squared_exponential", theta0=1e-1,thetaL=1e-3, thetaU=1,nugget=(ese_interest_y[:] ./ interest_y[:]).^2,random_start=100)
			#svr = grid_search[:GridSearchCV](clf,cv=5,param_grid=Dict("C"=> [1e-1, 1e0, 1e1, 1e2, 1e3],"gamma"=> logspace(-2, 2, 5)))
			gpr[:fit](interest_x, squeeze(interest_y,2)) #SciKit learn is expecting a y vector, not an array...
			y_gpr = gpr[:predict](x_sc)
			#y_gpr = Y_scaler[:inverse_transform](y_gpr_sc)
			return y[:,1] - y_gpr, y_gpr

	######## RAISING ERROR IF NOT THE GOOD CHOICE
	else
        error("Not implemented, choose between poly, Dspline, gcvspline and KRregression.")
    end
end


text:
# This file is a part of BAT.jl, licensed under the MIT License (MIT).

struct CubaIntegrand{D<:AbstractDensity,T<:Real} <: Function
    density::D
    log_density_shift::T
    log_support_volume::T
end

function CubaIntegrand(density::AbstractDensity, log_density_shift::Real)
    vol = spatialvolume(var_bounds(density))
    isinf(vol) && throw(ArgumentError("CUBA integration doesn't support densities with infinite support"))
    log_support_volume = log_volume(vol)
    @assert _cuba_valid_value(log_support_volume)

    CubaIntegrand(density, float(log_density_shift), log_support_volume)
end

_cuba_valid_value(x) = !isnan(x) && x < typeof(x)(+Inf)

function (integrand::CubaIntegrand)(x::AbstractVector{<:Real}, f::AbstractVector{<:Real})
    idxs = axes(f, 1)
    @assert length(idxs) == 1

    vol = spatialvolume(var_bounds(integrand.density))
    x_trafo = fromuhc(x, vol)
    logd = eval_logval(integrand.density, x_trafo)
    @assert _cuba_valid_value(logd)

    f[first(idxs)] = exp(logd + integrand.log_density_shift)
    @assert all(_cuba_valid_value,f)

    f
end

function (integrand::CubaIntegrand)(X::AbstractMatrix{<:Real}, f::AbstractMatrix{<:Real})
    idxs1 = axes(f, 1)
    @assert length(idxs1) == 1
    idxs2 = axes(f, 2)
    @assert idxs2 == axes(X, 2)

    vol = spatialvolume(var_bounds(integrand.density))
    x_trafo = fromuhc(nestedview(X), vol)
    @threads for i in idxs2
        logd = eval_logval(integrand.density, x_trafo[i])
        @assert _cuba_valid_value(logd)
        y = exp(logd + integrand.log_density_shift)
        @assert _cuba_valid_value(y)
        f[first(idxs1), i] = y
    end

    f
end

"""
    VEGASIntegration

VEGASIntegration integration algorithm.

Only supports densities with finite rectangular bounds.

!!! note

    This functionality is only available then the
    [Cuba](https://github.com/giordano/Cuba.jl) package is loaded (e.g. via
    `import CUBA`).
"""
@with_kw struct VEGASIntegration{TR<:AbstractDensityTransformTarget} <: IntegrationAlgorithm
    trafo::TR = PriorToUniform()
    log_density_shift::Float64 = 0.0
    rtol::Float64 = Cuba.RTOL
    atol::Float64 = Cuba.ATOL
    minevals::Int = Cuba.MINEVALS
    maxevals::Int = Cuba.MAXEVALS
    nstart::Int = Cuba.NSTART
    nincrease::Int = Cuba.NINCREASE
    nbatch::Int = Cuba.NBATCH
    nthreads::Int = Base.Threads.nthreads()
end
export VEGASIntegration

function bat_integrate_impl(integrand::CubaIntegrand, algorithm::VEGASIntegration)
    r = Cuba.vegas(
        integrand, totalndof(integrand.density), 1, nvec = algorithm.nthreads,
        rtol = algorithm.rtol, atol = algorithm.atol,
        minevals = algorithm.minevals, maxevals = algorithm.maxevals,
        nstart = algorithm.nstart, nincrease = algorithm.nincrease, nbatch = algorithm.nbatch
    )
end

"""
    SuaveIntegration

    SuaveIntegration integration algorithm.

Only supports densities with finite rectangular bounds.

!!! note

    This functionality is only available then the
    [Cuba](https://github.com/giordano/Cuba.jl) package is loaded (e.g. via
    `import CUBA`).
"""
@with_kw struct SuaveIntegration{TR<:AbstractDensityTransformTarget} <: IntegrationAlgorithm
    trafo::TR = PriorToUniform()
    log_density_shift::Float64 = 0.0
    rtol::Float64 = Cuba.RTOL
    atol::Float64 = Cuba.ATOL
    minevals::Int = Cuba.MINEVALS
    maxevals::Int = Cuba.MAXEVALS
    nnew::Int = Cuba.NNEW
    nmin::Int = Cuba.NMIN
    flatness::Float64 = Cuba.FLATNESS
    nthreads::Int = Base.Threads.nthreads()
end
export SuaveIntegration

function bat_integrate_impl(integrand::CubaIntegrand, algorithm::SuaveIntegration)
    Cuba.suave(
        integrand, totalndof(integrand.density), 1, nvec = algorithm.nthreads,
        rtol = algorithm.rtol, atol = algorithm.atol,
        minevals = algorithm.minevals, maxevals = algorithm.maxevals,
        nnew = algorithm.nnew, nmin = algorithm.nmin, flatness = algorithm.flatness
    )
end

"""
    DivonneIntegration

    DivonneIntegration integration algorithm.

Only supports densities with finite rectangular bounds.

!!! note

    This functionality is only available then the
    [Cuba](https://github.com/giordano/Cuba.jl) package is loaded (e.g. via
    `import CUBA`).
"""
@with_kw struct DivonneIntegration{TR<:AbstractDensityTransformTarget} <: IntegrationAlgorithm
    trafo::TR = PriorToUniform()
    log_density_shift::Float64 = 0.0
    rtol::Float64 = Cuba.RTOL
    atol::Float64 = Cuba.ATOL
    minevals::Int = Cuba.MINEVALS
    maxevals::Int = Cuba.MAXEVALS
    key1::Int = Cuba.KEY1
    key2::Int = Cuba.KEY2
    key3::Int = Cuba.KEY3
    maxpass::Int = Cuba.MAXPASS
    border::Float64 = Cuba.BORDER
    maxchisq::Float64 = Cuba.MAXCHISQ
    mindeviation::Float64 = Cuba.MINDEVIATION
    ngiven::Int = Cuba.NGIVEN
    ldxgiven::Int = Cuba.LDXGIVEN
    nextra::Int = Cuba.NEXTRA
    nthreads::Int = Base.Threads.nthreads()
end
export DivonneIntegration

function bat_integrate_impl(integrand::CubaIntegrand, algorithm::DivonneIntegration)
    Cuba.divonne(
        integrand, totalndof(integrand.density), 1, nvec = algorithm.nthreads,
        rtol = algorithm.rtol, atol = algorithm.atol,
        minevals = algorithm.minevals, maxevals = algorithm.maxevals,
        key1 = algorithm.key1, key2 = algorithm.key2, key3 = algorithm.key3,
        maxpass = algorithm.maxpass, border = algorithm.border, maxchisq = algorithm.maxchisq,
        mindeviation = algorithm.mindeviation, ngiven = algorithm.ngiven, ldxgiven = algorithm.ldxgiven,
        nextra = algorithm.nextra
    )
end

"""
    CuhreIntegration

    CuhreIntegration integration algorithm.

Only supports densities with finite rectangular bounds.

!!! note

    This functionality is only available then the
    [Cuba](https://github.com/giordano/Cuba.jl) package is loaded (e.g. via
    `import CUBA`).
"""
@with_kw struct CuhreIntegration{TR<:AbstractDensityTransformTarget} <: IntegrationAlgorithm
    trafo::TR = PriorToUniform()
    log_density_shift::Float64 = 0.0
    rtol::Float64 = Cuba.RTOL
    atol::Float64 = Cuba.ATOL
    minevals::Int = Cuba.MINEVALS
    maxevals::Int = Cuba.MAXEVALS
    key::Int = Cuba.KEY
    nthreads::Int = Base.Threads.nthreads()
end
export CuhreIntegration

function bat_integrate_impl(integrand::CubaIntegrand, algorithm::CuhreIntegration)
    Cuba.cuhre(
        integrand, totalndof(integrand.density), 1, nvec = algorithm.nthreads,
        rtol = algorithm.rtol, atol = algorithm.atol,
        minevals = algorithm.minevals, maxevals = algorithm.maxevals,
        key = algorithm.key
    )
end

const CubaIntegration = Union{VEGASIntegration, SuaveIntegration, DivonneIntegration, CuhreIntegration}

function bat_integrate_impl(target::AnyDensityLike, algorithm::CubaIntegration)
    density_notrafo = convert(AbstractDensity, target)
    density, trafo = bat_transform(algorithm.trafo, density_notrafo)
    integrand = CubaIntegrand(density, algorithm.log_density_shift)

    r_cuba = bat_integrate_impl(integrand, algorithm)

    log_renorm_corr = -integrand.log_density_shift + integrand.log_support_volume
    T = promote_type(BigFloat, typeof(log_renorm_corr))
    renorm_corr = exp(convert(T, log_renorm_corr))

    ival = first(r_cuba.integral) * renorm_corr
    ierr = first(r_cuba.error) * renorm_corr

    (result = Measurements.measurement(ival, ierr), cuba_result = r_cuba, renorm_corr = renorm_corr)
end


text:
# This module uses (abuses) the TerminalMenus.jl package to create an interactive
# spreadsheet application user interface. It's not very interesting as far as helping
# to explain the Salsa.jl package, but having an interactive UI makes this demo much more
# fun! 😁
module UI

import REPL

using ..SpreadsheetApp: SpreadsheetApp, Spreadsheet, CellId, cell_text, set_cell_text!

import TerminalMenus
using TerminalMenus: request

Base.@kwdef mutable struct SpreadsheetDisplay <: TerminalMenus.AbstractMenu
    # Spreadsheet fields
    ss::Spreadsheet
    maxrows::Int = 0
    maxcols::Int = 0
    # Track the current position (TerminalMenus only provides vertical scrolling)
    row_cursor::Int = 1
    column_cursor::Int = 1

    # Required TerminalMenus fields
    pagesize::Int = maxrows + 4
    pageoffset::Int = 0
    # Output when user hits enter
    selected::CellId = (-1,-1)
end
SpreadsheetDisplay(ss::Spreadsheet; kwargs...) = SpreadsheetDisplay(ss=ss; kwargs...)

function TerminalMenus.options(ui::SpreadsheetDisplay)
    [
        #"---",  # Fake row for the column cursor
        #([cell_text(ui.ss, (r,i)) for i in 1:ui.maxcols] for r in 1:ui.maxrows)...,
        ""
        for _ in 1:ui.maxrows
    ]
end
TerminalMenus.cancel(ui::SpreadsheetDisplay) = ui.selected = (-1,-1)

function TerminalMenus.header(ui::SpreadsheetDisplay)
    "──────"^ui.maxcols
end

function TerminalMenus.keypress(ui::SpreadsheetDisplay, key::UInt32)
    if key == Int(TerminalMenus.ARROW_RIGHT)
        # TODO: Allow scrolling infinitely
        ui.column_cursor = min(ui.maxcols, ui.column_cursor + 1)
    elseif key == Int(TerminalMenus.ARROW_LEFT)
        ui.column_cursor = max(1, ui.column_cursor - 1)
    end
    return false
end

function TerminalMenus.pick(ui::SpreadsheetDisplay, row_cursor::Int)
    ui.selected = (row_cursor, ui.column_cursor)
    return true # break out of the ui
end
function TerminalMenus.writeLine(buf::IOBuffer, ui::SpreadsheetDisplay, idx::Int, selected_row::Bool, term_width::Int)
    row = idx - 3   # To account for the fake rows

    col_width = 5

    # Issue a single read transaction to read an entire row of the display.
    txn = ui.ss.rt

    # Handle fake row for column cursor
    if idx === 1
        print(buf, "Cell Text:")
    elseif idx === 2
        print(buf, cell_text(txn, (ui.row_cursor, ui.column_cursor)))
    elseif idx === 3
        print(buf,
            join(["  ",
                  (lpad(rpad(SpreadsheetApp.column_idx_to_name(i),2), col_width)
                   for i in 1:ui.maxcols)..., ""], " "))
    # Handle fake final row for error display
    elseif idx === ui.pagesize
        try
            selected_value = SpreadsheetApp.cell_value(txn, (ui.row_cursor, ui.column_cursor))

            if selected_value isa SpreadsheetApp.AbstractUserFacingException
                print(buf, selected_value.err)
            elseif selected_value isa Exception
                print(buf, selected_value)
            end
        catch e
            print(buf, "Error while rendering: $e")
        end
    else
        # Task Parallelism: Compute each cell independently, in parallel along the row.
        cell_row = Vector{String}(undef, ui.maxcols)
        @sync for i in 1:ui.maxcols
            Threads.@spawn cell_row[$i] =
                            lpad(SpreadsheetApp.cell_display_str(txn, (row,$i)), col_width)
        end

        # Display the line
        line = if ui.row_cursor == row
            join(["$row ", cell_row[1:ui.column_cursor-1]...], "|") *
            "[$(cell_row[ui.column_cursor])]" *
            join([cell_row[ui.column_cursor+1:end]..., ""], "|")
        else
            join(["$row ", cell_row..., ""], "|")
        end

        print(buf, line)
    end
end

# This is sad, but we need to override this whole function to correctly handle printing the
# header:
# The generic printMenu function is used for displaying the state of a
#   menu to the screen. Menus must implement `writeLine` and `options`
#   and have fields `pagesize::Int` and `pageoffset::Int` as part of
#   their type definition
function TerminalMenus.printMenu(out, m::SpreadsheetDisplay, cursor::Int; init::Bool=false)
    # Store the current cursor in the ui:
    m.row_cursor = cursor

    # -----------------------------------------------------------------------
    # --- The rest of this function is copied verbatim from TerminalMenus ---

    TerminalMenus.CONFIG[:supress_output] && return

    buf = IOBuffer()

    # Move the cursor to the beginning of where it should print
    # Don't do this on the initial print
    lines = m.pagesize-1
    if init
        m.pageoffset = 0
    else
        print(buf, "\x1b[999D\x1b[$(lines)A")
    end

    for i in (m.pageoffset+1):(m.pageoffset + m.pagesize)
        print(buf, "\x1b[2K")

        if i == m.pageoffset+1 && m.pageoffset > 0
            # first line && scrolled past first entry
            print(buf, TerminalMenus.CONFIG[:up_arrow])
        elseif i == m.pagesize+m.pageoffset && i != length(TerminalMenus.options(m))
            # last line && not last option
            print(buf, TerminalMenus.CONFIG[:down_arrow])
        else
            # non special line
            print(buf, " ")
        end

        term_width = REPL.Terminals.width(TerminalMenus.terminal)

        TerminalMenus.writeLine(buf, m, i, i == cursor, term_width)

        # don't print an \r\n on the last line unless there is only one line
        if m.pagesize == 1 || i != (m.pagesize+m.pageoffset)
            print(buf, "\r\n")
        end
    end

    print(out, String(take!(buf)))
end

function run_ui(ui::SpreadsheetDisplay)
    TerminalMenus.config(down_arrow = ' ')
    while true
        choice = request("", ui)
        if choice === (-1,-1)
            return
        end
        print(stdout, "Enter new contents: ")
        new_text = readline(stdin)
        set_cell_text!(ui.ss, choice, new_text)
    end
end

end  # module


text:
# TODO Add VPRKpSecondary tableau.

"Parameters for right-hand side function of Variational Partitioned Runge-Kutta methods."
const ParametersVPRKpSecondary = AbstractParametersVPRK{:vprk_psecondary}

@doc raw"""
Variational partitioned Runge-Kutta integrator with projection on secondary constraint.

The VPRK integrator solves the following system of equations for the internal stages,
```math
\begin{aligned}
Q_{n,i} &= q_{n} + h \sum \limits_{j=1}^{s} a_{ij} \, \big( V_{n,j} + \Lambda_{n,j} \big) , \\
P_{n,i} &= p_{n} + h \sum \limits_{i=1}^{s} \bar{a}_{ij} \, \big( F_{n,j} + \nabla \vartheta (Q_{n,j})^{T} \cdot \Lambda_{n,j} \big) - d_i \lambda , \\
0 &= \sum \limits_{i=1}^{s} d_i V_i , \\
0 &= \sum \limits_{j=1}^{s} \omega_{ij} \Psi_{n,j} ,
\end{aligned}
```
with definitions
```math
\begin{aligned}
P_{n,i} &= \dfrac{\partial L}{\partial v} (Q_{n,i}, V_{n,i}) , \\
F_{n,i} &= \dfrac{\partial L}{\partial q} (Q_{n,i}, V_{n,i}) , \\
\Psi_{n,i} &= \psi(Q_{n,i}, V_{n,i}, P_{n,i}, F_{n,i}) ,
\end{aligned}
```
and update rule
```math
\begin{aligned}
q_{n+1} &= q_{n} + h \sum \limits_{i=1}^{s} b_{i} \, \big( V_{n,i} + \Lambda_{n,i} \big) , \\
p_{n+1} &= p_{n} + h \sum \limits_{i=1}^{s} \bar{b}_{i} \, \big( F_{n,i} + \nabla \vartheta (Q_{n,j})^{T} \cdot \Lambda_{n,j} \big) , \\
0 &= \phi (q_{n+1}, p_{n+1}) ,
\end{aligned}
```
satisfying the symplecticity conditions
```math
\begin{aligned}
b_{i} \bar{a}_{ij} + \bar{b}_{j} a_{ji} &= b_{i} \bar{b}_{j} , &
\bar{b}_i &= b_i ,
\end{aligned}
```
the primary constraint,
```math
\begin{aligned}
\phi(q,p) = p - \vartheta (q) = 0 ,
\end{aligned}
```
at the final solution ``(q_{n+1}, p_{n+1})``,
and super positions of the secondary constraints,
```math
\begin{aligned}
\psi(q,\dot{q},p,\dot{p})
= \dot{p} - \dot{q} \cdot \nabla \vartheta (q)
= \big( \nabla \vartheta (q) - \nabla \vartheta^{T} (q) \big) \cdot \dot{q} - \nabla H (q)
= 0,
\end{aligned}
```
which, evaluated at the internal stages, read
```math
\begin{aligned}
\Psi_{n,j} = \big( \nabla \vartheta (Q_{n,j}) - \nabla \vartheta^{T} (Q_{n,j}) \big) \cdot V_{n,j} - \nabla H (Q_{n,j}) .
\end{aligned}
```
Here, ``\omega`` is a ``(s-1) \times s`` matrix, chosen such that the resulting
method has optimal order.
The vector ``d`` is zero for Gauss-Legendre methods and needs to be chosen
appropriately for Gauss-Lobatto methods (for details see documentation of
VPRK methods).
"""
struct IntegratorVPRKpSecondary{DT, TT, D, S,
                PT <: ParametersVPRKpSecondary{DT,TT},
                ST <: NonlinearSolver{DT},
                IT <: InitialGuessIODE{TT}} <: AbstractIntegratorVPRK{DT,TT,D,S}
    params::PT
    solver::ST
    iguess::IT
    caches::CacheDict{PT}

    function IntegratorVPRKpSecondary(params::ParametersVPRKpSecondary{DT,TT,D,S}, solver::ST, iguess::IT, caches) where {DT,TT,D,S,ST,IT}
        new{DT, TT, D, S, typeof(params), ST, IT}(params, solver, iguess, caches)
    end

    function IntegratorVPRKpSecondary{DT,D}(equations::NamedTuple, tableau::TableauVPRK{TT}, Δt::TT) where {DT,TT,D}
        # get number of stages
        S = tableau.s

        # compute reduction matrix
        ω = zeros(TT, S-1, S)
        for i in 1:(S-1)
            for j in 1:S
                ω[i,j] = tableau.q.b[j] * tableau.q.c[j]^(i-1)
            end
        end

        # create params
        R = convert(Vector{TT}, [1, tableau.R∞])
        params = ParametersVPRKpSecondary{DT,D}(equations, tableau, Δt, NamedTuple{(:R,:ω)}((R,ω)))

        # create cache dict
        caches = CacheDict(params)

        # create solver
        solver = create_nonlinear_solver(DT, 2*D*S, params, caches)

        # create initial guess
        iguess = InitialGuessIODE(get_config(:ig_extrapolation), equations[:v̄], equations[:f̄], Δt)

        # create integrator
        IntegratorVPRKpSecondary(params, solver, iguess, caches)
    end

    function IntegratorVPRKpSecondary(equation::LDAE{DT}, tableau, Δt; kwargs...) where {DT}
        @assert hassecondary(equation)
        IntegratorVPRKpSecondary{DT, ndims(equation)}(get_functions(equation), tableau, Δt; kwargs...)
    end
end

function initial_guess!(int::IntegratorVPRKpSecondary{DT}, sol::AtomicSolutionPDAE{DT},
                        cache::IntegratorCacheVPRK{DT}=int.caches[DT]) where {DT}
    for i in eachstage(int)
        evaluate!(int.iguess, sol.q̄, sol.p̄, sol.v̄, sol.f̄,
                              sol.q, sol.p, sol.v, sol.f,
                              cache.q̃, cache.ṽ,
                              tableau(int).q.c[i])

        for k in eachdim(int)
            int.solver.x[ndims(int)*(0*nstages(int)+i-1)+k] = cache.ṽ[k]
            int.solver.x[ndims(int)*(1*nstages(int)+i-1)+k] = 0
        end
    end
end

function compute_stages_vprk!(x, q, v, p, Q, V, Λ, P, F, R, Φ, params)
    # copy x to V
    compute_stages_v_vprk!(x, V, params)

    # copy x to Λ
    compute_stages_λ_vprk!(x, Λ, params)

    # compute Q
    compute_stages_q_vprk!(q, Q, V, Λ, params)

    # compute P and F
    compute_stages_p_vprk!(Q, V, P, F, params)

    # compute p̄, R and Ψ
    compute_projection_vprk!(q, v, p, Q, P, V, F, Λ, R, Φ, params)
end

function compute_stages_q_vprk!(q::Vector{ST}, Q::Vector{Vector{ST}},
                V::Vector{Vector{ST}}, Λ::Vector{Vector{ST}},
                params::ParametersVPRKpSecondary{DT,TT,D,S}) where {ST,DT,TT,D,S}

    for (Qᵢ,Vᵢ,Λᵢ) in zip(Q,V,Λ)
        @assert D == length(Qᵢ) == length(Vᵢ) == length(Λᵢ) == length(q)
    end

    @assert S == length(Q) == length(V) == length(Λ)

    local y1::ST
    local y2::ST
    local y3::ST
    local y4::ST

    # compute Q
    for i in 1:S
        for k in 1:D
            y1 = y2 = y3 = y4 = 0
            for j in 1:S
                y1 += params.tab.q.a[i,j] * V[j][k]
                y2 += params.tab.q.â[i,j] * V[j][k]
                y3 += params.tab.q.a[i,j] * Λ[j][k]
                y4 += params.tab.q.â[i,j] * Λ[j][k]
            end
            Q[i][k] = params.q̄[k] + params.Δt * (y1 + y2) + params.Δt * (y3 + y4)
        end
    end

    # compute q
    for k in 1:D
        y1 = y2 = y3 = y4 = 0
        for j in 1:S
            y1 += params.tab.q.b[j] * V[j][k]
            y2 += params.tab.q.b̂[j] * V[j][k]
            y3 += params.tab.q.b[j] * Λ[j][k]
            y4 += params.tab.q.b̂[j] * Λ[j][k]
        end
        q[k] = params.q̄[k] + params.Δt * (y1 + y2) + params.Δt * (y3 + y4)
    end
end

function compute_projection_vprk!(q::Vector{ST}, v::Vector{ST}, p::Vector{ST},
                Q::Vector{Vector{ST}}, P::Vector{Vector{ST}},
                V::Vector{Vector{ST}}, F::Vector{Vector{ST}},
                Λ::Vector{Vector{ST}}, R::Vector{Vector{ST}}, Ψ::Vector{Vector{ST}},
                params::ParametersVPRKpSecondary{DT,TT,D,S}) where {ST,DT,TT,D,S}

    # create temporary variables
    local t₀::TT = params.t̄
    local t₁::TT = params.t̄ + params.Δt
    local tᵢ::TT
    # local dH = zeros(ST,D)
    # local Ω  = zeros(ST,D,D)

    # compute p=ϑ(q)
    params.equ[:ϑ](t₁, q, v, p)

    for i in 1:S
        tᵢ = t₀ + params.Δt * params.tab.p.c[i]

        # params.equ[:ω](tᵢ, Q[i], V[i], Ω)
        # params.equ[:∇H](tᵢ, Q[i], dH)

        # Ψ[i] .= Ω * V[i] .- dH

        # params.equ[:u](tᵢ, Q[i], Λ[i], U[i])
        params.equ[:g](tᵢ, Q[i], Λ[i], R[i])
        params.equ[:ψ](tᵢ, Q[i], P[i], V[i], F[i], Ψ[i])
    end
end

function compute_rhs_vprk!(b::Vector{ST}, P::Vector{Vector{ST}}, F::Vector{Vector{ST}}, R::Vector{Vector{ST}},
                params::ParametersVPRKpSecondary{DT,TT,D,S}) where {ST,DT,TT,D,S}

    local z1::ST
    local z2::ST
    local z3::ST
    local z4::ST

    # compute b = - [(P-AF-AR)]
    for i in 1:S
        for k in 1:D
            z1 = z2 = z3 = z4 = 0
            for j in 1:S
                z1 += params.tab.p.a[i,j] * F[j][k]
                z2 += params.tab.p.â[i,j] * F[j][k]
                z3 += params.tab.p.a[i,j] * R[j][k]
                z4 += params.tab.p.â[i,j] * R[j][k]
            end
            b[D*(i-1)+k] = (P[i][k] - params.p̄[k]) - params.Δt * (z1 + z2) - params.Δt * (z3 + z4)
        end
    end
end

function compute_rhs_vprk_projection!(b::Vector{ST}, p::Vector{ST},
                F::Vector{Vector{ST}}, R::Vector{Vector{ST}}, Ψ::Vector{Vector{ST}}, offset::Int,
                params::ParametersVPRKpSecondary{DT,TT,D,S}) where {ST,DT,TT,D,S}

    local z1::ST
    local z2::ST
    local z3::ST
    local z4::ST

    for i in 1:S
        for k in 1:D
            b[offset+D*(i-1)+k] = Ψ[i][k]
        end
    end

    # TODO # reactivate averaging of secondary constraints and primary constraint of solution

    # for i in 1:(S-1)
    #     for k in 1:D
    #         b[offset+D*(i-1)+k] = 0
    #         for j in 1:S
    #             b[offset+D*(i-1)+k] += params.pparams[:ω][i,j] * Ψ[j][k]
    #         end
    #     end
    # end

    # for k in 1:D
    #     z1 = z2 = z3 = z4 = 0
    #     for j in 1:S
    #         z1 += params.tab.p.b[j] * F[j][k]
    #         z2 += params.tab.p.b̂[j] * F[j][k]
    #         z3 += params.tab.p.b[j] * R[j][k]
    #         z4 += params.tab.p.b̂[j] * R[j][k]
    #     end
    #     b[offset+D*(S-1)+k] = (p[k] - params.p̄[k]) - params.Δt * (z1 + z2) + params.Δt * (z3 + z4)
    # end
end

"Compute stages of variational partitioned Runge-Kutta methods."
function Integrators.function_stages!(x::Vector{ST}, b::Vector{ST},
                params::ParametersVPRKpSecondary{DT,TT,D,S},
                caches::CacheDict) where {ST,DT,TT,D,S}

    # get cache for internal stages
    cache = caches[ST]

    compute_stages_vprk!(x, cache.q̃, cache.ṽ, cache.p̃,
                            cache.Q, cache.V, cache.Λ,
                            cache.P, cache.F, cache.R,
                            cache.Φ, params)

    # compute b = [P-AF-AR]
    compute_rhs_vprk!(b, cache.P, cache.F, cache.R, params)

    # compute b = Φ
    compute_rhs_vprk_projection!(b, cache.p̃, cache.F, cache.R, cache.Φ, D*S, params)

    compute_rhs_vprk_correction!(b, cache.V, params)
end

function Integrators.integrate_step!(int::IntegratorVPRKpSecondary{DT,TT}, sol::AtomicSolutionPDAE{DT,TT},
                                     cache::IntegratorCacheVPRK{DT}=int.caches[DT]) where {DT,TT}
    # update nonlinear solver parameters from cache
    update_params!(int.params, sol)

    # compute initial guess
    initial_guess!(int, sol, cache)

    # reset solution
    reset!(sol, timestep(int))

    # call nonlinear solver
    solve!(int.solver)

    # print solver status
    print_solver_status(int.solver.status, int.solver.params)

    # check if solution contains NaNs or error bounds are violated
    check_solver_status(int.solver.status, int.solver.params)

    # compute final update
    compute_stages_vprk!(int.solver.x,
                         cache.q̃, cache.ṽ, cache.p̃,
                         cache.Q, cache.V, cache.Λ,
                         cache.P, cache.F, cache.R,
                         cache.Φ, int.params)

    # compute unprojected solution
    update_solution!(sol.q, sol.q̃, cache.V, tableau(int).q.b, tableau(int).q.b̂, timestep(int))
    update_solution!(sol.p, sol.p̃, cache.F, tableau(int).p.b, tableau(int).p.b̂, timestep(int))

    # add projection to solution
    update_solution!(sol.q, sol.q̃, cache.Λ, tableau(int).q.b, tableau(int).q.b̂, timestep(int))
    update_solution!(sol.p, sol.p̃, cache.R, tableau(int).p.b, tableau(int).p.b̂, timestep(int))

    # copy solution to initial guess
    update_vector_fields!(int.iguess, sol.t, sol.q, sol.p, sol.v, sol.f)
end


text:
# This file was generated, do not modify it. # hide
Set(fp.continuous_encoder.features_to_keep) == Set(schema(X).names)

max_stars_repo_name: alan-turing-institute/MLJTutorials

max_issues_repo_name: alan-turing-institute/MLJTutorials

max_forks_repo_name: alan-turing-institute/MLJTutorials


text:
# Parameters
ϵ0 = 1.0e-12
timestep0 = 1.0e-2
start0 = Int(floor(1 / timestep0)) + 1

joint_types = [
    :Fixed,
    :Prismatic,
    :Planar,
    :FixedOrientation,
    :Revolute,
    :Cylindrical,
    :PlanarAxis,
    :FreeRevolute,
    :Orbital,
    :PrismaticOrbital,
    :PlanarOrbital,
    :FreeOrbital,
    :Spherical,
    :CylindricalFree,
    :PlanarFree
    ]

# Controllers
function controller!(mechanism, k;
    U=0.5,
    timestep=timestep0)

    N = Int(floor(1 / timestep))
    for joint in mechanism.joints
        nu = input_dimension(joint)
        u = (nu <= 5 && k ∈ (1:N)) * U * timestep * sones(nu)
        set_input!(joint, u)
    end
    return
end
nocontrol!(mechanism, k) = controller!(mechanism, k, U=0.0)

function humanoid_controller!(mechanism, k;
    U=0.05,
    timestep=timestep0)

    N = Int(floor(1 / timestep))
    for joint in mechanism.joints
        nu = input_dimension(joint)
        u = (nu <= 5 && k ∈ (1:N)) * U * timestep * sones(nu)
        set_input!(joint, u)
    end
    return
end

function quadruped_controller!(mechanism, k;
    U=0.05,
    timestep=timestep0)

    N = Int(floor(1.0 / timestep))
    for joint in mechanism.joints
        nu = input_dimension(joint)
        u = (nu <= 5 && k ∈ (1:N)) * U * timestep * sones(nu)
        set_input!(joint, u)
    end
    return
end

function snake_controller!(mechanism, k;
    U=0.05,
    timestep=timestep0)
    N = Int(floor(1.0 / timestep))
    for joint in mechanism.joints
        nu = input_dimension(joint)
        u = (nu <= 5 && k ∈ (1:N)) * U * timestep * sones(nu)
        set_input!(joint, u)
    end
    return
end

function twister_controller!(mechanism, k;
    U=0.01,
    timestep=timestep0)
    N = Int(floor(1.0 / timestep))
    for joint in mechanism.joints
        nu = input_dimension(joint)
        u = (nu <= 5 && k ∈ (1:N)) * U * timestep * sones(nu)
        set_input!(joint, u)
    end
    return
end

################################################################################
# DICE
################################################################################
# single body
# initial linear and angular velocity
# no gravity
# no spring and damper
# no control
################################################################################
@testset "Dice" begin
    gravity0 = -10.0

    mech = get_mechanism(:box,
        timestep=timestep0,
        gravity=gravity0,
        contact=false)

    v0 = [1,2,3.0]
    ω0 = [1,1,1.0]
    initialize!(mech, :box,
        v=v0,
        ω=ω0)

    storage = simulate!(mech, 5.0, nocontrol!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    # With spring the error is or the order of timestep, but there is no drift
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-8
end

################################################################################
# SINGLE PENDULUM
################################################################################
# single body
# initial angular velocity
# gravity
# no damper
# no control
################################################################################
@testset "Pendulum" begin
    gravity0 = 0.0
    spring0 = 1.0
    damper0 = 0.0

    mech = get_mechanism(:pendulum,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0)

    ϕ0 = 0.5π
    ω0 = 0.0

    initialize!(mech, :pendulum,
        angle=ϕ0,
        angular_velocity=ω0)
    storage = simulate!(mech, 25.0, controller!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    # With spring the error is or the order of timestep, but there is no drift
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-2
end

################################################################################
# SLIDER 1
################################################################################
# single body
# no initial linear velocity
# no gravity
# no damper
# no control
################################################################################
@testset "Slider 1" begin
    gravity0 = 0.0
    spring0 = 10.0
    damper0 = 0.0
    mech = get_mechanism(:slider,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0)

    z0 = 0.5
    initialize!(mech, :slider,
        position=z0)

    # Analytical
    pbody = mech.bodies[1]
    zmax = z0
    vmax = z0 * sqrt(spring0 / pbody.mass)
    pe_max = 0.5 * spring0 * zmax^2
    ke_max = 0.5 * pbody.mass * vmax^2

    storage = simulate!(mech, 5.0,  nocontrol!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test maximum amplitude and velocity
    @test norm(maximum([x[3] for x in storage.x[1]]) - zmax + 0.5) < 1.0e-4
    @test norm(maximum([vl[3] for vl in storage.vl[1]]) - vmax) < 1.0e-4

    # Test mechanical energy conservation
    # With spring the error is or the order of timestep, but there is no drift
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-3
    # @show norm((me0 .- me0[1]) ./ mean(me0), Inf)
end

################################################################################
# SLIDER 2
################################################################################
# single body
# no initial linear velocity
# gravity
# no damper
# no control
################################################################################
@testset "Slider 2" begin
    gravity0 = -9.81
    spring0 = 0.0
    damper0 = 0.0

    mech = get_mechanism(:slider,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0)

    z0 = 0.5
    initialize!(mech, :slider,
        position=z0)

    storage = simulate!(mech, 1.5,  nocontrol!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    # For gravity the conservation is perfect
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-6
end

################################################################################
# SLIDER 3
################################################################################
# single body
# no initial linear velocity
# gravity
# no damper
# no control
################################################################################
@testset "Slider 3" begin
    gravity0 = -9.81
    spring0 = 1.0
    damper0 = 0.0

    mech = get_mechanism(:slider,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0)

    z0 = 0.1
    initialize!(mech, :slider,
        position=z0)

    storage = simulate!(mech, 10.0,  nocontrol!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[start0])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[start0])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[start0])

    # Test mechanical energy conservation
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-3
end

################################################################################
#  HUMANOID
################################################################################
# multiple bodies
# initial linear and angular velocity
# no gravity
# with spring and damper
# with control
################################################################################
@testset "Humanoid" begin
    gravity0 = 0.0
    spring0 = 1.0
    damper0 = 0.0

    mech = get_mechanism(:humanoid,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0,
        contact_feet=false,
        contact_body=false)

    initialize!(mech, :humanoid)
    bodies = mech.bodies
    for body in mech.bodies
        set_maximal_velocities!(body,
            ω=0.5 * rand(3))
        # set_maximal_velocities!(body, v=1.0 * rand(3))
    end

    for joint in mech.joints
        joint.rotational.spring_type = :linear
    end

    storage = simulate!(mech, 3.0, humanoid_controller!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 2.0e-3
end

################################################################################
#  ATLAS
################################################################################
# multiple bodies
# initial linear and angular velocity
# no gravity
# with spring and damper
# with control
################################################################################
@testset "Atlas" begin
    gravity0 = 0.0
    spring0 = 1.0
    damper0 = 0.0
    mech = get_mechanism(:atlas,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0,
        contact_feet=false,
        contact_body=false)

    initialize!(mech, :atlas)
    bodies = mech.bodies
    set_maximal_velocities!.(bodies,
        ω=1.0 * rand(3))

    storage = simulate!(mech, 5.0, humanoid_controller!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 3.0e-3
end

################################################################################
#  QUADRUPED
################################################################################
# multiple bodies
# initial linear and angular velocity
# no gravity
# with spring and damper
# with control
################################################################################
@testset "Quadruped" begin
    gravity0 = 0.0
    spring0 = 1.0
    damper0 = 0.0

    mech = get_mechanism(:quadruped,
        timestep=timestep0,
        gravity=gravity0,
        spring=spring0,
        damper=damper0,
        contact_feet=false,
        contact_body=false,
        limits=false)

    initialize!(mech, :quadruped)
    storage = simulate!(mech, 5.0,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-2
end

################################################################################
# 5-LINK SNAKE
################################################################################
# multiple bodies
# initial linear and angular velocity
# no gravity
# with spring and damper
# with control
################################################################################
@testset "Snake" begin
    Nb0 = 5
    gravity0 = 0.0
    spring0 = 0.01
    damper0 = 0.0

    mech = get_mechanism(:snake,
        timestep=timestep0,
        gravity=gravity0,
        num_bodies=Nb0,
        spring=spring0,
        damper=damper0,
        joint_type=:Revolute,
        contact=false,
        radius=0.05);

    v0 = 10.0 * [1, 2, 3] * timestep0
    ω0 = 10.0 * [1, 2, 3] * timestep0
    q10 = Dojo.RotX(0.5*π)

    initialize!(mech, :snake,
        base_orientation=q10,
        base_linear_velocity=v0,
        base_angular_velocity=ω0)
    storage = simulate!(mech, 3.0, snake_controller!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-3

    for joint_type in joint_types
        # @show joint_type
        mech = get_mechanism(:snake,
            timestep=timestep0,
            gravity=gravity0,
            num_bodies=Nb0,
            spring=spring0,
            damper=damper0,
            joint_type=joint_type,
            contact=false,
            radius=0.05)

        v0 = 10.0 * [1, 2, 3] * timestep0
        ω0 = 10.0 * [1, 2, 3] * timestep0
        q10 = Dojo.RotX(0.5*π)

        initialize!(mech, :snake,
            base_orientation=q10,
            base_linear_velocity=v0,
            base_angular_velocity=ω0)
        storage = simulate!(mech, 3.0, snake_controller!,
            record=true,
            verbose=false,
            opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

        # visualize(mech, storage, vis=vis)

        ke0 = kinetic_energy(mech, storage)[start0:end]
        pe0 = potential_energy(mech, storage)[start0:end]
        me0 = mechanical_energy(mech, storage)[start0:end]

        # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
        # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
        # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

        # Test mechanical energy conservation
        @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-2
        norm((me0 .- me0[1]) ./ mean(me0), Inf)
    end
end

################################################################################
# 5-lINK TWISTER
################################################################################
# multiple bodies
# initial linear and angular velocity
# no gravity
# with spring and damper
# with control
################################################################################
@testset "Twister" begin
    Nb0 = 5
    gravity0 = 0.0
    spring0 = 0.01
    damper0 = 0.0

    mech = get_mechanism(:twister,
        timestep=timestep0,
        gravity=gravity0,
        num_bodies=Nb0,
        spring=spring0,
        damper=damper0,
        joint_type=:Revolute,
        contact=false,
        radius=0.05);

    v0 = 10.0 * [1, 2, 3] * timestep0
    ω0 = 10.0 * [1, 2, 3] * timestep0
    q10 = Dojo.RotX(0.5*π)

    initialize!(mech, :twister,
        base_orientation=q10,
        base_linear_velocity=v0,
        base_angular_velocity=ω0)
    storage = simulate!(mech, 3.0, twister_controller!,
        record=true,
        verbose=false,
        opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

    # visualize(mech, storage, vis=vis)

    ke0 = kinetic_energy(mech, storage)[start0:end]
    pe0 = potential_energy(mech, storage)[start0:end]
    me0 = mechanical_energy(mech, storage)[start0:end]

    # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
    # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
    # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

    # Test mechanical energy conservation
    @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-3

    for joint_type in joint_types
        mech = get_mechanism(:twister,
            timestep=timestep0,
            gravity=gravity0,
            num_bodies=Nb0,
            spring=spring0,
            damper=damper0,
            joint_type=joint_type,
            contact=false,
            radius=0.05)

        v0 = 10.0 * [1, 2, 3] * timestep0
        ω0 = 10.0 * [1, 2, 3] * timestep0
        q10 = Dojo.RotX(0.5*π)

        initialize!(mech, :twister,
            base_orientation=q10,
            base_linear_velocity=v0,
            base_angular_velocity=ω0)
        storage = simulate!(mech, 3.0, snake_controller!,
            record=true,
            verbose=false,
            opts=SolverOptions(rtol=ϵ0, btol=ϵ0))

        # visualize(mech, storage, vis=vis)

        ke0 = kinetic_energy(mech, storage)[start0:end]
        pe0 = potential_energy(mech, storage)[start0:end]
        me0 = mechanical_energy(mech, storage)[start0:end]

        # plot([(i-1) * timestep0 for i in 1:length(ke0)], ke0 .- ke0[1])
        # plot([(i-1) * timestep0 for i in 1:length(pe0)], pe0 .- pe0[1])
        # plot([(i-1) * timestep0 for i in 1:length(me0)], me0 .- me0[1])

        # Test mechanical energy conservation
        @test norm((me0 .- me0[1]) ./ mean(me0), Inf) < 1.0e-2
        norm((me0 .- me0[1]) ./ mean(me0), Inf)
    end
end


text:
abstract type Classification <: Task end

struct TwoClass <: Classification end
struct MultiClass <: Classification end

function args(
    ::Type{<:Classification};
    binarize = true,
    poslabels = [],
    kwargs...
)

    return (; binarize, poslabels)
end

# partition by classes
partition_size(targets, at) = partition_size(targets, [at...,])

function partition_size(targets, at::AbstractVector)
    n = StatsBase.counts(targets)
    ns = round.(Int, n .* at')
    return [ns n .- sum(ns, dims = 2)]
end

function targets_map(targets)
    classes = sort(unique(targets))
    mapping = Dict(tuple.(classes, 1:length(classes)))
    return y -> mapping[y]
end

targets_map(::BitArray) = y -> y ? 2 : 1

function partition(::Dataset{N, F, T}, targets, at) where {N, F, T<:Classification}
    ymap = targets_map(targets)
    ns = partition_size(ymap.(targets), at)

    parts = zeros.(Int, sum(ns; dims = 1))
    class_part = ones(Int, size(ns, 1))
    class_count = zero(class_part)
    pos_part = ones(Int, size(ns, 2))

    for (ind, y) in pairs(targets)
        class = ymap(y)

        count_max = ns[class, class_part[class]]
        if class_count[class] == count_max
            class_count[class] = 1
            class_part[class] += 1
        else
            class_count[class] += 1
        end

        i = class_part[class]
        parts[i][pos_part[i]] = ind
        pos_part[i] += 1
    end
    return Tuple(parts)
end

function postprocess(
    N::Type{<:Name},
    ::Type{<:Classification},
    data;
    binarize,
    poslabels,
    kwargs...
    )

    if binarize && !isempty(poslabels)
        data = data_binarize(data, poslabels)
    end
    return data
end

max_stars_repo_name: VaclavMacha/DatasetProvider

max_issues_repo_name: VaclavMacha/DatasetProvider

max_forks_repo_name: VaclavMacha/DatasetProvider