text:
#==============================================================================#
# WorkMail.jl
#
# This file is generated from:
# https://github.com/aws/aws-sdk-js/blob/master/apis/workmail-2017-10-01.normal.json
#==============================================================================#

__precompile__()

module WorkMail

using AWSCore

"""
    using AWSSDK.WorkMail.associate_delegate_to_resource
    associate_delegate_to_resource([::AWSConfig], arguments::Dict)
    associate_delegate_to_resource([::AWSConfig]; OrganizationId=, ResourceId=, EntityId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "AssociateDelegateToResource", arguments::Dict)
    workmail([::AWSConfig], "AssociateDelegateToResource", OrganizationId=, ResourceId=, EntityId=)

# AssociateDelegateToResource Operation

Adds a member to the resource's set of delegates.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization under which the resource exists.

## `ResourceId = ::String` -- *Required*
The resource for which members are associated.

## `EntityId = ::String` -- *Required*
The member (user or group) to associate to the resource.

# Returns

`AssociateDelegateToResourceResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/AssociateDelegateToResource)
"""
@inline associate_delegate_to_resource(aws::AWSConfig=default_aws_config(); args...) = associate_delegate_to_resource(aws, args)

@inline associate_delegate_to_resource(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "AssociateDelegateToResource", args)

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

"""
    using AWSSDK.WorkMail.associate_member_to_group
    associate_member_to_group([::AWSConfig], arguments::Dict)
    associate_member_to_group([::AWSConfig]; OrganizationId=, GroupId=, MemberId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "AssociateMemberToGroup", arguments::Dict)
    workmail([::AWSConfig], "AssociateMemberToGroup", OrganizationId=, GroupId=, MemberId=)

# AssociateMemberToGroup Operation

Adds a member to the group's set.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization under which the group exists.

## `GroupId = ::String` -- *Required*
The group for which the member is associated.

## `MemberId = ::String` -- *Required*
The member to associate to the group.

# Returns

`AssociateMemberToGroupResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException`, `OrganizationStateException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/AssociateMemberToGroup)
"""
@inline associate_member_to_group(aws::AWSConfig=default_aws_config(); args...) = associate_member_to_group(aws, args)

@inline associate_member_to_group(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "AssociateMemberToGroup", args)

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

"""
    using AWSSDK.WorkMail.create_alias
    create_alias([::AWSConfig], arguments::Dict)
    create_alias([::AWSConfig]; OrganizationId=, EntityId=, Alias=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "CreateAlias", arguments::Dict)
    workmail([::AWSConfig], "CreateAlias", OrganizationId=, EntityId=, Alias=)

# CreateAlias Operation

Adds an alias to the set of a given member of Amazon WorkMail.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization under which the member exists.

## `EntityId = ::String` -- *Required*
The alias is added to this Amazon WorkMail entity.

## `Alias = ::String` -- *Required*
The alias to add to the user.

# Returns

`CreateAliasResponse`

# Exceptions

`EmailAddressInUseException`, `EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `MailDomainNotFoundException`, `MailDomainStateException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/CreateAlias)
"""
@inline create_alias(aws::AWSConfig=default_aws_config(); args...) = create_alias(aws, args)

@inline create_alias(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "CreateAlias", args)

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

"""
    using AWSSDK.WorkMail.create_group
    create_group([::AWSConfig], arguments::Dict)
    create_group([::AWSConfig]; OrganizationId=, Name=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "CreateGroup", arguments::Dict)
    workmail([::AWSConfig], "CreateGroup", OrganizationId=, Name=)

# CreateGroup Operation

Creates a group that can be used in Amazon WorkMail by calling the RegisterToWorkMail operation.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization under which the group is to be created.

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

# Returns

`CreateGroupResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `InvalidParameterException`, `NameAvailabilityException`, `OrganizationNotFoundException`, `OrganizationStateException`, `ReservedNameException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/CreateGroup)
"""
@inline create_group(aws::AWSConfig=default_aws_config(); args...) = create_group(aws, args)

@inline create_group(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "CreateGroup", args)

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

"""
    using AWSSDK.WorkMail.create_resource
    create_resource([::AWSConfig], arguments::Dict)
    create_resource([::AWSConfig]; OrganizationId=, Name=, Type=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "CreateResource", arguments::Dict)
    workmail([::AWSConfig], "CreateResource", OrganizationId=, Name=, Type=)

# CreateResource Operation

Creates a new Amazon WorkMail resource. The available types are equipment and room.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier associated with the organization for which the resource is created.

## `Name = ::String` -- *Required*
The name of the created resource.

## `Type = "ROOM" or "EQUIPMENT"` -- *Required*
The type of the created resource.

# Returns

`CreateResourceResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `InvalidParameterException`, `NameAvailabilityException`, `OrganizationNotFoundException`, `OrganizationStateException` or `ReservedNameException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/CreateResource)
"""
@inline create_resource(aws::AWSConfig=default_aws_config(); args...) = create_resource(aws, args)

@inline create_resource(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "CreateResource", args)

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

"""
    using AWSSDK.WorkMail.create_user
    create_user([::AWSConfig], arguments::Dict)
    create_user([::AWSConfig]; OrganizationId=, Name=, DisplayName=, Password=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "CreateUser", arguments::Dict)
    workmail([::AWSConfig], "CreateUser", OrganizationId=, Name=, DisplayName=, Password=)

# CreateUser Operation

Creates a user who can be used in Amazon WorkMail by calling the RegisterToWorkMail operation.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier of the organization for which the user is created.

## `Name = ::String` -- *Required*
The name for the user to be created.

## `DisplayName = ::String` -- *Required*
The display name for the user to be created.

## `Password = ::String` -- *Required*
The password for the user to be created.

# Returns

`CreateUserResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `InvalidParameterException`, `InvalidPasswordException`, `NameAvailabilityException`, `OrganizationNotFoundException`, `OrganizationStateException`, `ReservedNameException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/CreateUser)
"""
@inline create_user(aws::AWSConfig=default_aws_config(); args...) = create_user(aws, args)

@inline create_user(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "CreateUser", args)

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

"""
    using AWSSDK.WorkMail.delete_alias
    delete_alias([::AWSConfig], arguments::Dict)
    delete_alias([::AWSConfig]; OrganizationId=, EntityId=, Alias=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DeleteAlias", arguments::Dict)
    workmail([::AWSConfig], "DeleteAlias", OrganizationId=, EntityId=, Alias=)

# DeleteAlias Operation

Remove the alias from a set of aliases for a given user.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the user exists.

## `EntityId = ::String` -- *Required*
The identifier for the Amazon WorkMail entity to have the aliases removed.

## `Alias = ::String` -- *Required*
The aliases to be removed from the user's set of aliases. Duplicate entries in the list are collapsed into single entries (the list is transformed into a set).

# Returns

`DeleteAliasResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DeleteAlias)
"""
@inline delete_alias(aws::AWSConfig=default_aws_config(); args...) = delete_alias(aws, args)

@inline delete_alias(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DeleteAlias", args)

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

"""
    using AWSSDK.WorkMail.delete_group
    delete_group([::AWSConfig], arguments::Dict)
    delete_group([::AWSConfig]; OrganizationId=, GroupId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DeleteGroup", arguments::Dict)
    workmail([::AWSConfig], "DeleteGroup", OrganizationId=, GroupId=)

# DeleteGroup Operation

Deletes a group from Amazon WorkMail.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization that contains the group.

## `GroupId = ::String` -- *Required*
The identifier of the group to be deleted.

# Returns

`DeleteGroupResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException`, `OrganizationStateException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DeleteGroup)
"""
@inline delete_group(aws::AWSConfig=default_aws_config(); args...) = delete_group(aws, args)

@inline delete_group(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DeleteGroup", args)

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

"""
    using AWSSDK.WorkMail.delete_mailbox_permissions
    delete_mailbox_permissions([::AWSConfig], arguments::Dict)
    delete_mailbox_permissions([::AWSConfig]; OrganizationId=, EntityId=, GranteeId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DeleteMailboxPermissions", arguments::Dict)
    workmail([::AWSConfig], "DeleteMailboxPermissions", OrganizationId=, EntityId=, GranteeId=)

# DeleteMailboxPermissions Operation

Deletes permissions granted to a user or group.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier of the organization under which the entity (user or group) exists.

## `EntityId = ::String` -- *Required*
The identifier of the entity (user or group) for which to delete mailbox permissions.

## `GranteeId = ::String` -- *Required*
The identifier of the entity (user or group) for which to delete granted permissions.

# Returns

`DeleteMailboxPermissionsResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DeleteMailboxPermissions)
"""
@inline delete_mailbox_permissions(aws::AWSConfig=default_aws_config(); args...) = delete_mailbox_permissions(aws, args)

@inline delete_mailbox_permissions(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DeleteMailboxPermissions", args)

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

"""
    using AWSSDK.WorkMail.delete_resource
    delete_resource([::AWSConfig], arguments::Dict)
    delete_resource([::AWSConfig]; OrganizationId=, ResourceId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DeleteResource", arguments::Dict)
    workmail([::AWSConfig], "DeleteResource", OrganizationId=, ResourceId=)

# DeleteResource Operation

Deletes the specified resource.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier associated with the organization for which the resource is deleted.

## `ResourceId = ::String` -- *Required*
The identifier of the resource to be deleted.

# Returns

`DeleteResourceResponse`

# Exceptions

`EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DeleteResource)
"""
@inline delete_resource(aws::AWSConfig=default_aws_config(); args...) = delete_resource(aws, args)

@inline delete_resource(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DeleteResource", args)

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

"""
    using AWSSDK.WorkMail.delete_user
    delete_user([::AWSConfig], arguments::Dict)
    delete_user([::AWSConfig]; OrganizationId=, UserId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DeleteUser", arguments::Dict)
    workmail([::AWSConfig], "DeleteUser", OrganizationId=, UserId=)

# DeleteUser Operation

Deletes a user from Amazon WorkMail and all subsequent systems. The action can't be undone. The mailbox is kept as-is for a minimum of 30 days, without any means to restore it.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization that contains the user.

## `UserId = ::String` -- *Required*
The identifier of the user to be deleted.

# Returns

`DeleteUserResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException`, `OrganizationStateException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DeleteUser)
"""
@inline delete_user(aws::AWSConfig=default_aws_config(); args...) = delete_user(aws, args)

@inline delete_user(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DeleteUser", args)

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

"""
    using AWSSDK.WorkMail.deregister_from_work_mail
    deregister_from_work_mail([::AWSConfig], arguments::Dict)
    deregister_from_work_mail([::AWSConfig]; OrganizationId=, EntityId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DeregisterFromWorkMail", arguments::Dict)
    workmail([::AWSConfig], "DeregisterFromWorkMail", OrganizationId=, EntityId=)

# DeregisterFromWorkMail Operation

Mark a user, group, or resource as no longer used in Amazon WorkMail. This action disassociates the mailbox and schedules it for clean-up. Amazon WorkMail keeps mailboxes for 30 days before they are permanently removed. The functionality in the console is *Disable*.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the Amazon WorkMail entity exists.

## `EntityId = ::String` -- *Required*
The identifier for the entity to be updated.

# Returns

`DeregisterFromWorkMailResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DeregisterFromWorkMail)
"""
@inline deregister_from_work_mail(aws::AWSConfig=default_aws_config(); args...) = deregister_from_work_mail(aws, args)

@inline deregister_from_work_mail(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DeregisterFromWorkMail", args)

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

"""
    using AWSSDK.WorkMail.describe_group
    describe_group([::AWSConfig], arguments::Dict)
    describe_group([::AWSConfig]; OrganizationId=, GroupId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DescribeGroup", arguments::Dict)
    workmail([::AWSConfig], "DescribeGroup", OrganizationId=, GroupId=)

# DescribeGroup Operation

Returns the data available for the group.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the group exists.

## `GroupId = ::String` -- *Required*
The identifier for the group to be described.

# Returns

`DescribeGroupResponse`

# Exceptions

`EntityNotFoundException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DescribeGroup)
"""
@inline describe_group(aws::AWSConfig=default_aws_config(); args...) = describe_group(aws, args)

@inline describe_group(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DescribeGroup", args)

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

"""
    using AWSSDK.WorkMail.describe_organization
    describe_organization([::AWSConfig], arguments::Dict)
    describe_organization([::AWSConfig]; OrganizationId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DescribeOrganization", arguments::Dict)
    workmail([::AWSConfig], "DescribeOrganization", OrganizationId=)

# DescribeOrganization Operation

Provides more information regarding a given organization based on its identifier.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization to be described.

# Returns

`DescribeOrganizationResponse`

# Exceptions

`InvalidParameterException` or `OrganizationNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DescribeOrganization)
"""
@inline describe_organization(aws::AWSConfig=default_aws_config(); args...) = describe_organization(aws, args)

@inline describe_organization(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DescribeOrganization", args)

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

"""
    using AWSSDK.WorkMail.describe_resource
    describe_resource([::AWSConfig], arguments::Dict)
    describe_resource([::AWSConfig]; OrganizationId=, ResourceId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DescribeResource", arguments::Dict)
    workmail([::AWSConfig], "DescribeResource", OrganizationId=, ResourceId=)

# DescribeResource Operation

Returns the data available for the resource.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier associated with the organization for which the resource is described.

## `ResourceId = ::String` -- *Required*
The identifier of the resource to be described.

# Returns

`DescribeResourceResponse`

# Exceptions

`EntityNotFoundException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DescribeResource)
"""
@inline describe_resource(aws::AWSConfig=default_aws_config(); args...) = describe_resource(aws, args)

@inline describe_resource(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DescribeResource", args)

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

"""
    using AWSSDK.WorkMail.describe_user
    describe_user([::AWSConfig], arguments::Dict)
    describe_user([::AWSConfig]; OrganizationId=, UserId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DescribeUser", arguments::Dict)
    workmail([::AWSConfig], "DescribeUser", OrganizationId=, UserId=)

# DescribeUser Operation

Provides information regarding the user.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the user exists.

## `UserId = ::String` -- *Required*
The identifier for the user to be described.

# Returns

`DescribeUserResponse`

# Exceptions

`EntityNotFoundException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DescribeUser)
"""
@inline describe_user(aws::AWSConfig=default_aws_config(); args...) = describe_user(aws, args)

@inline describe_user(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DescribeUser", args)

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

"""
    using AWSSDK.WorkMail.disassociate_delegate_from_resource
    disassociate_delegate_from_resource([::AWSConfig], arguments::Dict)
    disassociate_delegate_from_resource([::AWSConfig]; OrganizationId=, ResourceId=, EntityId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DisassociateDelegateFromResource", arguments::Dict)
    workmail([::AWSConfig], "DisassociateDelegateFromResource", OrganizationId=, ResourceId=, EntityId=)

# DisassociateDelegateFromResource Operation

Removes a member from the resource's set of delegates.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the resource exists.

## `ResourceId = ::String` -- *Required*
The identifier of the resource from which delegates' set members are removed.

## `EntityId = ::String` -- *Required*
The identifier for the member (user, group) to be removed from the resource's delegates.

# Returns

`DisassociateDelegateFromResourceResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DisassociateDelegateFromResource)
"""
@inline disassociate_delegate_from_resource(aws::AWSConfig=default_aws_config(); args...) = disassociate_delegate_from_resource(aws, args)

@inline disassociate_delegate_from_resource(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DisassociateDelegateFromResource", args)

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

"""
    using AWSSDK.WorkMail.disassociate_member_from_group
    disassociate_member_from_group([::AWSConfig], arguments::Dict)
    disassociate_member_from_group([::AWSConfig]; OrganizationId=, GroupId=, MemberId=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "DisassociateMemberFromGroup", arguments::Dict)
    workmail([::AWSConfig], "DisassociateMemberFromGroup", OrganizationId=, GroupId=, MemberId=)

# DisassociateMemberFromGroup Operation

Removes a member from a group.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the group exists.

## `GroupId = ::String` -- *Required*
The identifier for the group from which members are removed.

## `MemberId = ::String` -- *Required*
The identifier for the member to be removed to the group.

# Returns

`DisassociateMemberFromGroupResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException`, `OrganizationStateException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/DisassociateMemberFromGroup)
"""
@inline disassociate_member_from_group(aws::AWSConfig=default_aws_config(); args...) = disassociate_member_from_group(aws, args)

@inline disassociate_member_from_group(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "DisassociateMemberFromGroup", args)

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

"""
    using AWSSDK.WorkMail.list_aliases
    list_aliases([::AWSConfig], arguments::Dict)
    list_aliases([::AWSConfig]; OrganizationId=, EntityId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListAliases", arguments::Dict)
    workmail([::AWSConfig], "ListAliases", OrganizationId=, EntityId=, <keyword arguments>)

# ListAliases Operation

Creates a paginated call to list the aliases associated with a given entity.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the entity exists.

## `EntityId = ::String` -- *Required*
The identifier for the entity for which to list the aliases.

## `NextToken = ::String`
The token to use to retrieve the next page of results. The first call does not contain any tokens.

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListAliasesResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListAliases)
"""
@inline list_aliases(aws::AWSConfig=default_aws_config(); args...) = list_aliases(aws, args)

@inline list_aliases(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListAliases", args)

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

"""
    using AWSSDK.WorkMail.list_group_members
    list_group_members([::AWSConfig], arguments::Dict)
    list_group_members([::AWSConfig]; OrganizationId=, GroupId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListGroupMembers", arguments::Dict)
    workmail([::AWSConfig], "ListGroupMembers", OrganizationId=, GroupId=, <keyword arguments>)

# ListGroupMembers Operation

Returns an overview of the members of a group.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the group exists.

## `GroupId = ::String` -- *Required*
The identifier for the group to which the members are associated.

## `NextToken = ::String`
The token to use to retrieve the next page of results. The first call does not contain any tokens.

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListGroupMembersResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListGroupMembers)
"""
@inline list_group_members(aws::AWSConfig=default_aws_config(); args...) = list_group_members(aws, args)

@inline list_group_members(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListGroupMembers", args)

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

"""
    using AWSSDK.WorkMail.list_groups
    list_groups([::AWSConfig], arguments::Dict)
    list_groups([::AWSConfig]; OrganizationId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListGroups", arguments::Dict)
    workmail([::AWSConfig], "ListGroups", OrganizationId=, <keyword arguments>)

# ListGroups Operation

Returns summaries of the organization's groups.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the groups exist.

## `NextToken = ::String`
The token to use to retrieve the next page of results. The first call does not contain any tokens.

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListGroupsResponse`

# Exceptions

`EntityNotFoundException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListGroups)
"""
@inline list_groups(aws::AWSConfig=default_aws_config(); args...) = list_groups(aws, args)

@inline list_groups(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListGroups", args)

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

"""
    using AWSSDK.WorkMail.list_mailbox_permissions
    list_mailbox_permissions([::AWSConfig], arguments::Dict)
    list_mailbox_permissions([::AWSConfig]; OrganizationId=, EntityId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListMailboxPermissions", arguments::Dict)
    workmail([::AWSConfig], "ListMailboxPermissions", OrganizationId=, EntityId=, <keyword arguments>)

# ListMailboxPermissions Operation

Lists the mailbox permissions associated with a mailbox.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier of the organization under which the entity (user or group) exists.

## `EntityId = ::String` -- *Required*
The identifier of the entity (user or group) for which to list mailbox permissions.

## `NextToken = ::String`
The token to use to retrieve the next page of results. The first call does not contain any tokens.

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListMailboxPermissionsResponse`

# Exceptions

`EntityNotFoundException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListMailboxPermissions)
"""
@inline list_mailbox_permissions(aws::AWSConfig=default_aws_config(); args...) = list_mailbox_permissions(aws, args)

@inline list_mailbox_permissions(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListMailboxPermissions", args)

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

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

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

# ListOrganizations Operation

Returns summaries of the customer's non-deleted organizations.

# Arguments

## `NextToken = ::String`
The token to use to retrieve the next page of results. The first call does not contain any tokens.

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListOrganizationsResponse`

# Exceptions

`InvalidParameterException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListOrganizations)
"""
@inline list_organizations(aws::AWSConfig=default_aws_config(); args...) = list_organizations(aws, args)

@inline list_organizations(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListOrganizations", args)

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

"""
    using AWSSDK.WorkMail.list_resource_delegates
    list_resource_delegates([::AWSConfig], arguments::Dict)
    list_resource_delegates([::AWSConfig]; OrganizationId=, ResourceId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListResourceDelegates", arguments::Dict)
    workmail([::AWSConfig], "ListResourceDelegates", OrganizationId=, ResourceId=, <keyword arguments>)

# ListResourceDelegates Operation

Lists the delegates associated with a resource. Users and groups can be resource delegates and answer requests on behalf of the resource.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization that contains the resource for which delegates are listed.

## `ResourceId = ::String` -- *Required*
The identifier for the resource whose delegates are listed.

## `NextToken = ::String`
The token used to paginate through the delegates associated with a resource.

## `MaxResults = ::Int`
The number of maximum results in a page.

# Returns

`ListResourceDelegatesResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListResourceDelegates)
"""
@inline list_resource_delegates(aws::AWSConfig=default_aws_config(); args...) = list_resource_delegates(aws, args)

@inline list_resource_delegates(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListResourceDelegates", args)

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

"""
    using AWSSDK.WorkMail.list_resources
    list_resources([::AWSConfig], arguments::Dict)
    list_resources([::AWSConfig]; OrganizationId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListResources", arguments::Dict)
    workmail([::AWSConfig], "ListResources", OrganizationId=, <keyword arguments>)

# ListResources Operation

Returns summaries of the organization's resources.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the resources exist.

## `NextToken = ::String`
The token to use to retrieve the next page of results. The first call does not contain any tokens.

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListResourcesResponse`

# Exceptions

`InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListResources)
"""
@inline list_resources(aws::AWSConfig=default_aws_config(); args...) = list_resources(aws, args)

@inline list_resources(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListResources", args)

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

"""
    using AWSSDK.WorkMail.list_users
    list_users([::AWSConfig], arguments::Dict)
    list_users([::AWSConfig]; OrganizationId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ListUsers", arguments::Dict)
    workmail([::AWSConfig], "ListUsers", OrganizationId=, <keyword arguments>)

# ListUsers Operation

Returns summaries of the organization's users.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the users exist.

## `NextToken = ::String`
TBD

## `MaxResults = ::Int`
The maximum number of results to return in a single call.

# Returns

`ListUsersResponse`

# Exceptions

`InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ListUsers)
"""
@inline list_users(aws::AWSConfig=default_aws_config(); args...) = list_users(aws, args)

@inline list_users(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ListUsers", args)

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

"""
    using AWSSDK.WorkMail.put_mailbox_permissions
    put_mailbox_permissions([::AWSConfig], arguments::Dict)
    put_mailbox_permissions([::AWSConfig]; OrganizationId=, EntityId=, GranteeId=, PermissionValues=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "PutMailboxPermissions", arguments::Dict)
    workmail([::AWSConfig], "PutMailboxPermissions", OrganizationId=, EntityId=, GranteeId=, PermissionValues=)

# PutMailboxPermissions Operation

Sets permissions for a user or group. This replaces any pre-existing permissions set for the entity.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier of the organization under which the entity (user or group) exists.

## `EntityId = ::String` -- *Required*
The identifier of the entity (user or group) for which to update mailbox permissions.

## `GranteeId = ::String` -- *Required*
The identifier of the entity (user or group) to which to grant the permissions.

## `PermissionValues = ["FULL_ACCESS", "SEND_AS" or "SEND_ON_BEHALF", ...]` -- *Required*
The permissions granted to the grantee. SEND_AS allows the grantee to send email as the owner of the mailbox (the grantee is not mentioned on these emails). SEND_ON_BEHALF allows the grantee to send email on behalf of the owner of the mailbox (the grantee is not mentioned as the physical sender of these emails). FULL_ACCESS allows the grantee full access to the mailbox, irrespective of other folder-level permissions set on the mailbox.

# Returns

`PutMailboxPermissionsResponse`

# Exceptions

`EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/PutMailboxPermissions)
"""
@inline put_mailbox_permissions(aws::AWSConfig=default_aws_config(); args...) = put_mailbox_permissions(aws, args)

@inline put_mailbox_permissions(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "PutMailboxPermissions", args)

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

"""
    using AWSSDK.WorkMail.register_to_work_mail
    register_to_work_mail([::AWSConfig], arguments::Dict)
    register_to_work_mail([::AWSConfig]; OrganizationId=, EntityId=, Email=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "RegisterToWorkMail", arguments::Dict)
    workmail([::AWSConfig], "RegisterToWorkMail", OrganizationId=, EntityId=, Email=)

# RegisterToWorkMail Operation

Registers an existing and disabled user, group, or resource/entity for Amazon WorkMail use by associating a mailbox and calendaring capabilities. It performs no change if the entity is enabled and fails if the entity is deleted. This operation results in the accumulation of costs. For more information, see [Pricing](http://aws.amazon.com/workmail/pricing). The equivalent console functionality for this operation is *Enable*. Users can either be created by calling the CreateUser API or they can be synchronized from your directory. For more information, see DeregisterFromWorkMail.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier for the organization under which the Amazon WorkMail entity exists.

## `EntityId = ::String` -- *Required*
The identifier for the entity to be updated.

## `Email = ::String` -- *Required*
The email for the entity to be updated.

# Returns

`RegisterToWorkMailResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EmailAddressInUseException`, `EntityNotFoundException`, `EntityStateException`, `EntityAlreadyRegisteredException`, `InvalidParameterException`, `MailDomainNotFoundException`, `MailDomainStateException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/RegisterToWorkMail)
"""
@inline register_to_work_mail(aws::AWSConfig=default_aws_config(); args...) = register_to_work_mail(aws, args)

@inline register_to_work_mail(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "RegisterToWorkMail", args)

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

"""
    using AWSSDK.WorkMail.reset_password
    reset_password([::AWSConfig], arguments::Dict)
    reset_password([::AWSConfig]; OrganizationId=, UserId=, Password=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "ResetPassword", arguments::Dict)
    workmail([::AWSConfig], "ResetPassword", OrganizationId=, UserId=, Password=)

# ResetPassword Operation

Allows the administrator to reset the password for a user.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier of the organization that contains the user for which the password is reset.

## `UserId = ::String` -- *Required*
The identifier of the user for whom the password is reset.

## `Password = ::String` -- *Required*
The new password for the user.

# Returns

`ResetPasswordResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `InvalidPasswordException`, `OrganizationNotFoundException`, `OrganizationStateException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/ResetPassword)
"""
@inline reset_password(aws::AWSConfig=default_aws_config(); args...) = reset_password(aws, args)

@inline reset_password(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "ResetPassword", args)

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

"""
    using AWSSDK.WorkMail.update_primary_email_address
    update_primary_email_address([::AWSConfig], arguments::Dict)
    update_primary_email_address([::AWSConfig]; OrganizationId=, EntityId=, Email=)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "UpdatePrimaryEmailAddress", arguments::Dict)
    workmail([::AWSConfig], "UpdatePrimaryEmailAddress", OrganizationId=, EntityId=, Email=)

# UpdatePrimaryEmailAddress Operation

Updates the primary email for an entity. The current email is moved into the list of aliases (or swapped between an existing alias and the current primary email) and the email provided in the input is promoted as the primary.

# Arguments

## `OrganizationId = ::String` -- *Required*
The organization that contains the entity to update.

## `EntityId = ::String` -- *Required*
The entity to update (user, group, or resource).

## `Email = ::String` -- *Required*
The value of the email to be updated as primary.

# Returns

`UpdatePrimaryEmailAddressResponse`

# Exceptions

`DirectoryServiceAuthenticationFailedException`, `DirectoryUnavailableException`, `EmailAddressInUseException`, `EntityNotFoundException`, `EntityStateException`, `InvalidParameterException`, `MailDomainNotFoundException`, `MailDomainStateException`, `InvalidParameterException`, `OrganizationNotFoundException`, `OrganizationStateException` or `UnsupportedOperationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/UpdatePrimaryEmailAddress)
"""
@inline update_primary_email_address(aws::AWSConfig=default_aws_config(); args...) = update_primary_email_address(aws, args)

@inline update_primary_email_address(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "UpdatePrimaryEmailAddress", args)

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

"""
    using AWSSDK.WorkMail.update_resource
    update_resource([::AWSConfig], arguments::Dict)
    update_resource([::AWSConfig]; OrganizationId=, ResourceId=, <keyword arguments>)

    using AWSCore.Services.workmail
    workmail([::AWSConfig], "UpdateResource", arguments::Dict)
    workmail([::AWSConfig], "UpdateResource", OrganizationId=, ResourceId=, <keyword arguments>)

# UpdateResource Operation

Updates data for the resource. It must be preceded by a describe call in order to have the latest information. The dataset in the request should be the one expected when performing another describe call.

# Arguments

## `OrganizationId = ::String` -- *Required*
The identifier associated with the organization for which the resource is updated.

## `ResourceId = ::String` -- *Required*
The identifier of the resource to be updated.

## `Name = ::String`
The name of the resource to be updated.

## `BookingOptions = [ ... ]`
The resource's booking options to be updated.
```
 BookingOptions = [
        "AutoAcceptRequests" =>  ::Bool,
        "AutoDeclineRecurringRequests" =>  ::Bool,
        "AutoDeclineConflictingRequests" =>  ::Bool
    ]
```

# Returns

`UpdateResourceResponse`

# Exceptions

`DirectoryUnavailableException`, `EntityNotFoundException`, `EntityStateException`, `InvalidConfigurationException`, `EmailAddressInUseException`, `MailDomainNotFoundException`, `MailDomainStateException`, `NameAvailabilityException`, `OrganizationNotFoundException` or `OrganizationStateException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/workmail-2017-10-01/UpdateResource)
"""
@inline update_resource(aws::AWSConfig=default_aws_config(); args...) = update_resource(aws, args)

@inline update_resource(aws::AWSConfig, args) = AWSCore.Services.workmail(aws, "UpdateResource", args)

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

end # module WorkMail

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


text:
"""
    corr = v2r(cov)

Convert a covariance matrix `cov` to the correlation matrix `corr`.
This is a wrapper of `StatsBase.cov2cor.`
"""
function v2r(C::Matrix{T}) where {T<:AbstractFloat}
   s = sqrt.(diag(C))
   R = StatsBase.cov2cor(C, s)
   return R
end
function c2r(C::Matrix{T}) where {T<:AbstractFloat}
   return v2r(C)
end

"""
    cov = r2v(corr, stdev)

Convert a correlation matrix `cov` and the vector of standard deviation `stdev` to the covariance matrix `cov`.
This is a wrapper of `StatsBase.cor2cov.`
"""
function r2v(C::Matrix{T}, s::Vector{T}) where {T<:AbstractFloat}
   return StatsBase.cor2cov(C,s)
end
function r2c(C::Matrix{T}, s::Vector{T}) where {T<:AbstractFloat}
   return r2v(C,s)
end

"""
   v = vech(A)

Convert a square matrix to a half-stored vector.
It was written by Steven G. Johnson; see `https://discourse.julialang.org/t/half-vectorization/7399` for details.
"""
function vech(A::AbstractMatrix{T}) where T
   m = LinearAlgebra.checksquare(A)
   v = zeros(T,((m*(m+1))>>1))
   k = 0
   for j = 1:m, i = j:m
       @inbounds v[k += 1] = A[i,j]
   end
   return v
end

# CS 4220/Math 4260 Numerical Analysis: Linear and Nonlinear Problems
# by Charles Van Loan
# http://www.cs.cornell.edu/courses/cs4220/2014sp/CVLBook/chap7.pdf
"""
     dv,ev = chol_symtrid(T::SymTridiagonal)

Calculate the Cholesky factor of a symmetric tridiagonal matrix.
Note that the factor is a lower tridiagonal matrix, and the output is a couple of vectors.
The factor is stored in `dv` for the diagonal matrix (D) and in `ev` for the off-diagonal elements (L).
"""
function chol_symtrid(T::SymTridiagonal)
   n = length(T.dv)
   dv = zeros(n)
   ev = zeros(n-1)
   dv[1] = sqrt(T.dv[1])
   for i=2:n
      ev[i-1] = T.ev[i-1]/dv[i-1]
      dv[i] = sqrt(T.dv[i] - ev[i-1]^2)
   end
   return dv,ev
end

# The Tridiagonal LDL T Calculus for the Indefinite Generalized Symmetric Eigenproblem
# By Peter Strobach
# https://www.researchgate.net/publication/271196070_THE_TRIDIAGONAL_LDL_T_CALCULUS_FOR_THE_INDEFINITE_GENERALIZED_SYMMETRIC_EIGENPROBLEM
"""
    dv,ev = ldlt_symtrid(T::SymTridiagonal)

Calculate the LDLT factor of a symmetric tridiagonal matrix.
Note that the factor is a lower tridiagonal matrix, and the output is a couple of vectors.
The factor is stored in `dv` for the diagonal matrix (D) and in `ev` for the off-diagonal elements (L).
"""
function ldlt_symtrid(T::SymTridiagonal)
   n = length(T.dv)
   dv = zeros(n)
   ev = zeros(n-1)
   dv[1] = T.dv[1]
   for i=1:n-1
      ev[i] = T.ev[i]/dv[i]
      dv[i+1] = T.dv[i+1] - (ev[i]^2)*dv[i]
   end
   return dv,ev
end

# Takahashi method for the LDLT factor
"""
    takahashi_ldlt_symtrid!(dv,ev)

Compute the sparse inverse (as known as selected inverse) of the LDLT factor of a symmetric trigiagonal matrix.
The LDLT factor is stored in two vectors (`dv` and `ev`), which should be calculated with `ldlt_symtrid`.
"""
function takahashi_ldlt_symtrid!(dv,ev)
   n = size(dv,1)
   dv[n] = 1/dv[n]
   for i=n-1:-1:1
      evi = ev[i]
      ev[i] = -evi*dv[i+1]
      dv[i] = 1/dv[i] - evi*ev[i]
   end
end

function get_invT11_fast(T::SymTridiagonal)
   dv,ev = ldlt_symtrid(T::SymTridiagonal)
   takahashi_ldlt_symtrid!(dv,ev)
   return dv[1]
end

"""
    bentB = bending(B [, W], gamma=nothing, tol=eps, verbose=false)
    bentB = bending(B, gamma=nothing, tol=eps, verbose=false)

Apply a traditional "bending" method to a symmetric matrix `B` (between-group-variance) using another symmetric matrix `W` (within-group-variance), as described by Hayes and Hill (1981).
The resulting matrix is `bentB = (1-gamma)*B + gamma*v*W`, where `v` is the average eigenvalues of `B` and `gamma` is a constant between 0 and 1, which can be provided by the user or generated by this function.
When `W` is omitted, the identity matrix will be used instead.

If the user supplies `gamma`, the function will simply apply the bending formula and return `bentB` without checking whether `bentB` is positive defnite or not.
Otherwise (`gamma=nothing`), this function generates `gamma=1/1000`, applies bending, and returns `bentB` if it is positive definite as `minimum(eigen(bentB).values)>tol` (the default `tol` is the machine epsilon).
If not, the function tries `gamma=2/1000`, `gamma=3/1000`, ..., and `gamma=1000/1000` until `bentB` becomes positive definite.

With `verbose=true`, this function prints `v` (average eigenvalue of `B`) and `gamma` that has been used for bending (default: `verbose=false`).
"""
function bending(B::Matrix{Tv}, W=I(size(B,1)); gamma::Union{Nothing,Float64}=nothing, verbose::Bool=false, tol=eps(one(Tv))) where Tv<:AbstractFloat
   if !issymmetric(B)
      throw(ArgumentError("B must be symmetric."))
   end
   if !issymmetric(W)
      throw(ArgumentError("W must be symmetric."))
   end

   v = mean(eigen(B).values)
   if verbose; println("mean(eig(B)): $(v)"); end
   if !isnothing(gamma)
      if gamma<0 || gamma>1
         throw(ArgumentError("gamma should be between 0 and 1."))
      else
         if verbose; println("gamma: $(gamma)"); end
         M = (1-gamma)*B + gamma*v*W
         return M
      end
   end

   M = copy(B)
   if minimum(eigen(M).values)>tol
      return M
   end

   for i=1000:-1:1
      g = 1/i
      M .= (1-g)*B + g*v*W
      if minimum(eigen(M).values)>tol
         if verbose; println("gamma: $(g)"); end
         return M
      end
   end

   return M
end

"""
    bentV = bending2(V [, W], corr=false, mineig=eps, tol=eps, maxiter=10000, force=false, verbose=false)

Apply an alternative "bending" method to a variance-covariance matrix `V` using a weighting matrix `W`, as described by Jorjani et al. (2003).
When omitting `W`, `J` (matrix of ones) will be used, implying the same weights for all elements.
Jorjani et al. (2003) suggested `W` as the reciprocal of number of individuals used in the estimation of covariances.

The function calculates the eigenvalues of `V`, and an eigenvalue is replaced with `mineig` if it is smaller than `mineig`.
The covariance matrix is reconstructed with modified eigenvalues, then tests whether it is positive definite by checking the minimum eigenvalue larger than `tol`.
The function repeats the process until the resulting matrix becomes positive definite, and the number of maximum iterations is defind by `maxiter`.

See some options below.

- With `corr=true`, this function assumes the input is a correlation matrix; otherwise, the input is expected to be a variance-covariance matrix.
- With `force=true`, this function iterates the formula `maxiter` times regardless of whether it has converged or not.
- With `verbose=true`, this function shows the number of iterations required when `force=false`.
"""
function bending2(V::Matrix{Tv}, W=ones(size(V,1),size(V,2)); mineig=eps(one(Tv)), tol=eps(one(Tv)), corr=false, maxiter=10000, force=false, verbose=false) where Tv<:AbstractFloat
   if !issymmetric(V)
      throw(ArgumentError("B must be symmetric."))
   end
   if !issymmetric(W)
      throw(ArgumentError("W must be symmetric."))
   end
   if size(V) != size(W)
      throw(DimensionMismatch("V and W"))
   end

   if corr
      dim = size(V,1)
      Rprev = copy(V)
      Rn = copy(V)
      Rnew = copy(V)
      Dn = zeros(Tv,dim)
      Δn = zeros(Tv,dim)
      for i=1:maxiter
         (D, U) = eigen(Rprev)
         Dn .= D
         Dn[ D .< tol ] .= 2*mineig
         Δn .= Dn * (sum(D)/sum(Dn))
         Rn .= Rprev - ((Rprev - U*diagm(Δn)*U') .* W)
         Rnew = v2r(Rn)
         if !force && minimum(eigen(Rnew).values)>tol
            if verbose; println("round: $(i)"); end
            break
         end
         Rprev .= Rnew
      end
      return Rnew
   else
      Vprev = copy(V)
      Vnew = copy(V)
      for i=1:maxiter
         (D, U) = eigen(Vprev)
         D[ D .< tol ] .= mineig
         Vnew .= Vprev - ((Vprev - U*diagm(D)*U') .* W)
         if !force && minimum(eigen(Vnew).values)>tol
            if verbose; println("round: $(i)"); end
            break
         end
         Vprev .= Vnew
      end
      return Vnew
   end
end

ext: jl


text:
# Belief Propagation                #
# --------------------------------- #
@rule NormalMeanPrecision(:μ, Marginalisation) (m_out::PointMass, m_τ::PointMass) = NormalMeanPrecision(mean(m_out), mean(m_τ))

@rule NormalMeanPrecision(:μ, Marginalisation) (m_out::UnivariateNormalDistributionsFamily, m_τ::PointMass) = begin
    m_out_mean, m_out_cov = mean_cov(m_out)
    return NormalMeanVariance(m_out_mean, m_out_cov + inv(mean(m_τ)))
end

# Variational                       #
# --------------------------------- #
@rule NormalMeanPrecision(:μ, Marginalisation) (q_out::Any, q_τ::Any) = NormalMeanPrecision(mean(q_out), mean(q_τ))

@rule NormalMeanPrecision(:μ, Marginalisation) (m_out::UnivariateNormalDistributionsFamily, q_τ::Any) = begin
    m_out_mean, m_out_cov = mean_cov(m_out)
    return NormalMeanVariance(m_out_mean, m_out_cov + cholinv(mean(q_τ)))
end


text:
using JuMP
using LightGraphs, SimpleWeightedGraphs

function miller_tucker_zemlin(model, graph :: SimpleWeightedGraph, k :: Int)
    n::Int = nv(graph)
    @variables(model, begin
        u[1:n], (integer=true, upper_bound = k+1)
        d[2:n], (integer=true, lower_bound = 0, upper_bound = k)
        z[2:n], (binary=true, lower_bound = 0, upper_bound = 1)
    end)
    omega :: Int = 1
    fix(u[omega], 0, force=true) # Constraint (6)
    for i in (omega+1):n
        set_lower_bound(u[i], 1)
        for j in 1:n
            if has_edge(graph, i, j)
                y_ij = variable_by_name(model, "y[$i,$j]")
                @constraint(model,
                    u[i] >= u[j] + y_ij - k * (1 - y_ij) # Constraint (7)
                )
            end
        end
    end

    for i in (omega+1):n
        @constraint(model, sum(variable_by_name(model, "y[$i,$j]") for j = 1:n if has_edge(graph, i, j)) == 1) # Constraint (8)
    end
    @constraint(model, sum(variable_by_name(model, "y[$omega,$j]") for j = (omega + 1):n) == 0) # Constraint (9)

    for j in (omega+1):n
        @constraint(model,
            sum(variable_by_name(model, "y[$i,$j]") for i in 2:n if has_edge(graph, i, j))
            + sum(variable_by_name(model, "y[$j,$k_]") for k_ in 2:n if has_edge(graph, j, k_)) == d[j]) # Constraint (10)
        @constraints(model, begin
            z[j] * k >= d[j] # Constraint (11)
            z[j] <= d[j] # Constraint (12)
        end)
    end

    @constraint(model, sum(z[i] for i = 2:n) == k) # Constraint (13)

end

function miller_tucker_zemlin_warmstart(model, graph :: SimpleWeightedGraph, k :: Int, solution)
    n :: Int = nv(graph)
    z :: Vector{Int} = zeros(Int, n)
    d :: Vector{Int} = zeros(Int, n)
    connected :: Vector{Vector{Int}} = [[] for i=1:n]
    for e in solution
        i = src(e)
        j = dst(e)
        z[i] = 1
        z[j] = 1
        push!(connected[i], j)
        push!(connected[j], i)
        d[i] += 1
        d[j] += 1
    end
    s = nothing
    for c in 2:n
        if !isempty(connected[c])
            s = c
            break
        end
    end

    stack = [s]
    marked :: Vector{Bool} = zeros(Bool, n)
    dagsort :: Vector{Int} = []
    while !isempty(stack)
        node = pop!(stack)
        neighbours = connected[node]
        for n in neighbours
            if !marked[n]
                push!(stack, n)
                set_start_value(variable_by_name(model, "y[$n,$node]"), 1)
                set_start_value(variable_by_name(model, "y[$node,$n]"), 0)
            end
        end
        marked[node] = true
        pushfirst!(dagsort, node)
    end

    for i in 2:n
        set_start_value(variable_by_name(model, "z[$i]"), z[i])
        set_start_value(variable_by_name(model, "d[$i]"), d[i])
        if z[i] == 0
            set_start_value(variable_by_name(model, "y[$i,1]"), 1)
            for j in 2:n
                if has_edge(graph, i, j)
                    set_start_value(variable_by_name(model, "y[$i,$j]"), 0)
                    set_start_value(variable_by_name(model, "y[$j,$i]"), 0)
                end
            end
        end
    end
end

function check_miller_tucker_zemlin_warmstart(model, graph :: SimpleWeightedGraph, k :: Int) :: Int
    n :: Int = nv(graph)
    m :: Int = ne(graph)
    es :: Vector{SimpleWeightedEdge} = collect(edges(graph))
    sort!(es, by=dst)
    sort!(es, by=src, alg=MergeSort)
    x :: Vector{Union{Nothing, Int}} = fill(nothing, m)
    y :: Matrix{Union{Nothing, Int}} = fill(nothing, n, n)
    z :: Vector{Union{Nothing, Int}} = fill(nothing, n)
    u :: Vector{Union{Nothing, Int}} = fill(nothing, n)
    d :: Vector{Union{Nothing, Int}} = fill(nothing, n)
    filled :: Int = 0
    totalweight :: Int = 0
    for r in 1:m
        e::SimpleWeightedEdge = es[r]
        i::Int = src(e)
        j::Int = dst(e)
        x[r] = start_value(variable_by_name(model, "x[$r]"))
        if x[r] == 1
            totalweight += Int(round(Int,weight(e)))
        end
        for v in [i,j]
            if v != 1
                if isnothing(z[v])
                    z[v] = start_value(variable_by_name(model, "z[$v]"))
                end
                if isnothing(u[v])
                    u[v] = start_value(variable_by_name(model, "u[$v]"))
                end
                if isnothing(d[v])
                    d[v] = start_value(variable_by_name(model, "d[$v]"))
                end

            end
        end
        y[i, j] = start_value(variable_by_name(model, "y[$i,$j]"))
        y[j, i] = start_value(variable_by_name(model, "y[$j,$i]"))

        for v in [i,j]
            if v != 1
                if !isnothing(d[v]) && !isnothing(z[v])
                    if (d[v] >= 1 && z[v] == 0) || (d[v] == 0 && z[v] == 1)
                        @warn "z[$v] != sgn(d[$v]) | ($(z[v]) != sgn($(d[v])))"
                    end
                elseif !isnothing(d[v]) && isnothing(z[v])
                    if d[v] == 0
                        z[v] = 0
                    else
                        z[v] = 1
                    end
                    set_start_value(variable_by_name(model, "z[$v]"), z[v])
                    filled += 1
                elseif isnothing(d[v]) && !isnothing(z[v])
                    if z[v] == 0
                        d[v] = 0
                        set_start_value(variable_by_name(model, "d[$v]"), d[v])
                    end
                end
            end
        end
    end

    for i in 2:n
        if !isnothing(u[i])
            for j in 2:n
                if !isnothing(u[j]) && !isnothing(y[i,j]) && has_edge(graph, i, j)
                    if u[i] < u[j] + y[i,j] - k * (1 - y[i,j])
                        @warn "u[$i] < u[$j] + y[$i,$j] - k * (1 - y[$i,$j]) | ($(u[i]) < $(u[j]) + $(y[i,j]) - $deg * (1 - $(y[i,j])))"
                    end
                end
            end
        end
    end

    return filled
end

max_stars_repo_name: Entze/jkMST

max_issues_repo_name: Entze/jkMST

max_forks_repo_name: Entze/jkMST


text:
import Base: inv

"""
    inv(C::Cline, z::Number)
    inv(C::Cline, D::Cline)
    inv(C::Cline)
Invert the point `z`  [or the cline `D`] with respect to the cline `C`
giving a new point [cline].

`inv(C)` returns a function `F` such that `F(X) = inv(C,X)`.
"""
function inv(X::Cline, z::Number)::Complex{Float64}
    pp = three_points(X)
    F = LFT(pp...)
    zz = F(z)
    return (inv(F))(conj(zz))
end

function inv(C::Cline, D::Cline)::Cline
    pp = three_points(D)
    qq = [inv(C, p) for p in pp]

    return Cline(qq...)
end

function inv(C::Cline)::Function
    return x -> inv(C, x)
end


text:
abstract type ModelAtmosphere end

struct PlanarAtmosphereLayer{F}
    tau_5000::F                #dimensionless
    z::F                       #g
    temp::F                    #K
    electron_number_density::F #cm^-3
    number_density::F          #cm^-3
end

struct PlanarAtmosphere{F} <: ModelAtmosphere
    layers::Vector{PlanarAtmosphereLayer{F}}
end

struct ShellAtmosphereLayer{F}
    tau_5000::F                #dimensionless
    z::F                       #cm
    temp::F                    #K
    electron_number_density::F #cm^-3
    number_density::F          #cm^-3
end

struct ShellAtmosphere{F} <: ModelAtmosphere
    layers::Vector{ShellAtmosphereLayer{F}}
    R::F #the radius of the star where τ_ros == 1, i.e. the photosphere (not the top)
end

"""
    PlanarAtmosphere(atm::ShellAtmosphere)

Construct a planar atmosphere with the data from a shell atmosphere.  Mostly useful for testing.
"""
function PlanarAtmosphere(atm::ShellAtmosphere)
    PlanarAtmosphere([PlanarAtmosphereLayer(l.tau_5000, l.z, l.temp, l.electron_number_density,
                                            l.number_density) for l in atm.layers])
end

"""
    ShellAtmosphere(atm::PlanarAtmosphere, R)

Construct a shell atmosphere with the data from a planar atmosphere and an outer radius.  Mostly
useful for testing.
"""
function ShellAtmosphere(atm::PlanarAtmosphere, R)
    ShellAtmosphere([ShellAtmosphereLayer(l.tau_5000, l.z, l.temp, l.electron_number_density,
                                          l.number_density) for l in atm.layers], R)
end

#pretty-printing
function Base.show(io::IO, m::MIME"text/plain", atm::A) where A <: ModelAtmosphere
    print(io, "$(A) with $(length(atm.layers)) layers")
end

"""
    read_model_atmosphere(filename; truncate_at_10000K=true)

Parse the provided model atmosphere file in MARCS ".mod" format.  Returns either a
`PlanarAtmosphere` or a `ShellAtmosphere`.

When `truncate_at_10000K` is true, layers with temperatures greater than 10,000 Kelvin will be
elided.  These are typically at the deepest level, where optical depth is very large, and have
minimal impact on the surface spectrum.  They are ignored by default because Korg's default
partition and molecular equillibrium functions are tabulated only up to that tempurature.
"""
function read_model_atmosphere(fname::AbstractString; truncate_at_10000K=true) :: ModelAtmosphere
    open(fname) do f
        #these files are small, so it's not a big deal to load them entirely into memory
        lines = collect(eachline(f))

        Rind = findfirst(occursin.("adius", lines)) # {rR}adius has uncertain capitalization
        if isnothing(Rind)
            throw(ArgumentError("Can't parse .mod file:  can't detect radius." *
                                " (should be 1.0 for plane-parallel atmospheres.)"))
        end
        R = parse(Float64, split(lines[Rind])[1])
        planar = R == 1

        i = findfirst(occursin.("Number of depth points", lines))
        if isnothing(i)
            throw(ArgumentError("Can't parse .mod file: can't detect number of layers."))
        end
        nlayers = parse(Int, split(lines[i])[1])

        header = findfirst(occursin.("lgTauR", lines))
        if isnothing(header)
            throw(ArgumentError("Can't parse .mod file: can't find header."))
        end
        blockA = lines[header+1:header+nlayers]
        blockB = lines[header+nlayers+2 : header+1+2nlayers]

        layers = map(zip(blockA, blockB)) do (lineA, lineB)
            nums = parse.(Float64, strip.(split(lineA * lineB)))
            temp = nums[5]
            nₑ = nums[6]/(temp*kboltz_cgs) #electron number density
            n = nums[7]/(temp*kboltz_cgs)  #non-electron number density
            if planar
                PlanarAtmosphereLayer(10^nums[3], -nums[4], temp, nₑ, n)
            else
                ShellAtmosphereLayer(10^nums[3], -nums[4], temp, nₑ, n)
            end
        end

        if truncate_at_10000K
            filter!(layers) do layer
                layer.temp < 10_000
            end
        end

        if planar
            PlanarAtmosphere(layers)
        else
            ShellAtmosphere(layers, R)
        end
     end
end


text:
"""
    RelativeEntropyBridge{T}

The `RelativeEntropyCone` is representable with exponential cone and LP constraints, since
``u \\ge \\sum_{i=1}^n w_i \\log (\\frac{w_i}{v_i})`` if and only if there exists a vector
``y`` such that ``u \\ge \\sum_i y_i`` and ``y_i \\ge w_i \\log (\\frac{w_i}{v_i})`` or
equivalently ``v_i \\ge w_i \\exp (\\frac{-y_i}{w_i})`` or equivalently
``(-y_i, w_i, v_i) \\in ExponentialCone``, for all ``i``.
"""
struct RelativeEntropyBridge{T,F,G,H} <: AbstractBridge
    y::Vector{MOI.VariableIndex}
    ge_index::CI{F,MOI.GreaterThan{T}}
    exp_indices::Vector{CI{G,MOI.ExponentialCone}}
end
function bridge_constraint(
    ::Type{RelativeEntropyBridge{T,F,G,H}},
    model::MOI.ModelLike,
    f::H,
    s::MOI.RelativeEntropyCone,
) where {T,F,G,H}
    f_scalars = MOIU.eachscalar(f)
    d = MOI.dimension(s)
    v_dim = div(d - 1, 2)
    y = MOI.add_variables(model, v_dim)
    ge_index = MOIU.normalize_and_add_constraint(
        model,
        MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)),
        MOI.GreaterThan(zero(T)),
        allow_modify_function = true,
    )
    w_start = 1 + v_dim
    exp_funcs = [
        MOIU.operate(
            vcat,
            T,
            MOIU.operate(-, T, MOI.SingleVariable(y[i])),
            f_scalars[w_start+i],
            f_scalars[1+i],
        ) for i in 1:v_dim
    ]
    exp_indices = [
        MOI.add_constraint(model, exp_func_i, MOI.ExponentialCone()) for
        exp_func_i in exp_funcs
    ]
    return RelativeEntropyBridge{T,F,G,H}(y, ge_index, exp_indices)
end

function MOI.supports_constraint(
    ::Type{RelativeEntropyBridge{T}},
    ::Type{<:MOI.AbstractVectorFunction},
    ::Type{MOI.RelativeEntropyCone},
) where {T}
    return true
end
function MOIB.added_constrained_variable_types(::Type{<:RelativeEntropyBridge})
    return Tuple{DataType}[]
end
function MOIB.added_constraint_types(
    ::Type{RelativeEntropyBridge{T,F,G,H}},
) where {T,F,G,H}
    return [(F, MOI.GreaterThan{T}), (G, MOI.ExponentialCone)]
end
function concrete_bridge_type(
    ::Type{<:RelativeEntropyBridge{T}},
    H::Type{<:MOI.AbstractVectorFunction},
    ::Type{MOI.RelativeEntropyCone},
) where {T}
    S = MOIU.scalar_type(H)
    F = MOIU.promote_operation(-, T, S, S)
    G = MOIU.promote_operation(
        vcat,
        T,
        MOIU.promote_operation(-, T, MOI.SingleVariable),
        S,
    )
    return RelativeEntropyBridge{T,F,G,H}
end

# Attributes, Bridge acting as a model
function MOI.get(bridge::RelativeEntropyBridge, ::MOI.NumberOfVariables)
    return length(bridge.y)
end
MOI.get(bridge::RelativeEntropyBridge, ::MOI.ListOfVariableIndices) = bridge.y
function MOI.get(
    bridge::RelativeEntropyBridge{T,F},
    ::MOI.NumberOfConstraints{F,MOI.GreaterThan{T}},
) where {T,F}
    return 1
end
function MOI.get(
    bridge::RelativeEntropyBridge{T,F,G},
    ::MOI.NumberOfConstraints{G,MOI.ExponentialCone},
) where {T,F,G}
    return length(bridge.y)
end
function MOI.get(
    bridge::RelativeEntropyBridge{T,F},
    ::MOI.ListOfConstraintIndices{F,MOI.GreaterThan{T}},
) where {T,F}
    return [bridge.ge_index]
end
function MOI.get(
    bridge::RelativeEntropyBridge{T,F,G},
    ::MOI.ListOfConstraintIndices{G,MOI.ExponentialCone},
) where {T,F,G}
    return bridge.exp_indices
end

# References
function MOI.delete(model::MOI.ModelLike, bridge::RelativeEntropyBridge)
    for exp_index_i in bridge.exp_indices
        MOI.delete(model, exp_index_i)
    end
    MOI.delete(model, bridge.ge_index)
    return MOI.delete(model, bridge.y)
end

# Attributes, Bridge acting as a constraint
function MOI.get(
    model::MOI.ModelLike,
    ::MOI.ConstraintFunction,
    bridge::RelativeEntropyBridge{T,F,G,H},
) where {T,F,G,H}
    func = MOIU.zero_with_output_dimension(G, 1 + 2 * length(bridge.y))
    MOIU.operate_output_index!(
        +,
        T,
        1,
        func,
        MOI.get(model, MOI.ConstraintFunction(), bridge.ge_index),
    )
    w_start = 1 + length(bridge.y)
    for i in eachindex(bridge.y)
        exp_func_i = MOIU.eachscalar(
            MOI.get(model, MOI.ConstraintFunction(), bridge.exp_indices[i]),
        )
        MOIU.operate_output_index!(-, T, 1, func, exp_func_i[1])
        MOIU.operate_output_index!(+, T, 1 + i, func, exp_func_i[3])
        MOIU.operate_output_index!(+, T, w_start + i, func, exp_func_i[2])
    end
    return MOIU.convert_approx(H, MOIU.remove_variable(func, bridge.y))
end
function MOI.get(
    model::MOI.ModelLike,
    ::MOI.ConstraintSet,
    bridge::RelativeEntropyBridge,
)
    return MOI.RelativeEntropyCone(1 + 2 * length(bridge.y))
end
function MOI.supports(
    ::MOI.ModelLike,
    ::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart},
    ::Type{<:RelativeEntropyBridge},
)
    return true
end
function MOI.get(
    model::MOI.ModelLike,
    attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
    bridge::RelativeEntropyBridge{T},
) where {T}
    primal = zeros(T, 1 + 2 * length(bridge.y))
    primal[1] = MOI.get(model, attr, bridge.ge_index)
    w_start = 1 + length(bridge.y)
    for i in eachindex(bridge.y)
        exp_primal_i = MOI.get(model, attr, bridge.exp_indices[i])
        primal[1] -= exp_primal_i[1]
        primal[1+i] = exp_primal_i[3]
        primal[w_start+i] = exp_primal_i[2]
    end
    return primal
end
function MOI.set(
    model::MOI.ModelLike,
    attr::MOI.ConstraintPrimalStart,
    bridge::RelativeEntropyBridge{T},
    value,
) where {T}
    v_dim = length(bridge.y)
    v_value = value[2:(v_dim+1)]
    w_value = value[(v_dim+2):end]
    y_value = [w_i * log(w_i / v_i) for (v_i, w_i) in zip(v_value, w_value)]
    MOI.set(
        model,
        attr,
        bridge.ge_index,
        value[1] - reduce(+, y_value, init = zero(T)),
    )
    for i in 1:v_dim
        MOI.set(
            model,
            attr,
            bridge.exp_indices[i],
            [-y_value[i], w_value[i], v_value[i]],
        )
        MOI.set(model, MOI.VariablePrimalStart(), bridge.y[i], y_value[i])
    end
    return
end
# Given a is dual on u - sum(y) >= 0 and (b_i, c_i, d_i) is dual on (-y_i, w_i, v_i)
# in ExponentialCone, the dual on (u, v, w) in RelativeEntropyCone is (a, d_i, c_i).
function MOI.get(
    model::MOI.ModelLike,
    attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
    bridge::RelativeEntropyBridge{T},
) where {T}
    dual = zeros(T, 1 + 2 * length(bridge.y))
    dual[1] = MOI.get(model, attr, bridge.ge_index)[1]
    w_start = 1 + length(bridge.y)
    for i in eachindex(bridge.y)
        exp_dual_i = MOI.get(model, attr, bridge.exp_indices[i])
        dual[1+i] = exp_dual_i[3]
        dual[w_start+i] = exp_dual_i[2]
    end
    return dual
end
# Given constraint dual start of (u, v, w), constraint dual on GreaterThan is u
# and on exponential cone constraint i is (r_i, w_i, v_i), but since y_i is free,
# its dual is 0, so we have -r_i + u == 0 hence r_i = u.
# Note: alternatively, we could use the Lambert W function to calculate
# r_i = exp(W(-w_i / (-v_i * e))) * (-v_i * e), but this is more complicated.
function MOI.set(
    model::MOI.ModelLike,
    ::MOI.ConstraintDualStart,
    bridge::RelativeEntropyBridge,
    value,
)
    u = value[1]
    MOI.set(model, MOI.ConstraintDualStart(), bridge.ge_index, u)
    w_start = 1 + length(bridge.y)
    for i in eachindex(bridge.y)
        MOI.set(
            model,
            MOI.ConstraintDualStart(),
            bridge.exp_indices[i],
            [u, value[w_start+i], value[1+i]],
        )
    end
    return
end


text:
# This file is illustrates multiple simulations of a model

using Causal

# Constrcut the model
@defmodel model begin
    @nodes begin
        gen = SinewaveGenerator()
        writer = Writer()
    end
    @branches begin
        gen => writer
    end
end

# Multiple simulations.
for i in 1 : 5
    simulate!(model)
end


text:
#
# A simple steepest descent algorithm implementation
#
export subGradientMethod
@doc doc"""
    subGradientMethod(M, F, ∂F, x)
perform a subgradient method $x_{k+1} = \mathrm{retr}(x_k, s_k∂F(x_k))$,

where $\mathrm{retr}$ is a retraction, $s_k$ can be specified as a function but is
usually set to a constant value. Though the subgradient might be set valued,
the argument `∂F` should always return _one_ element from the subgradient.

# Input
* `M` – a manifold $\mathcal M$
* `F` – a cost function $F\colon\mathcal M\to\mathbb R$ to minimize
* `∂F`: the (sub)gradient $\partial F\colon\mathcal M\to T\mathcal M$ of F
  restricted to always only returning one value/element from the subgradient
* `x` – an initial value $x\in\mathcal M$

# Optional
* `stepsize` – ([`ConstantStepsize`](@ref)`(1.)`) specify a [`Stepsize`](@ref)
* `retraction` – (`exp`) a `retraction(M,x,ξ)` to use.
* `stoppingCriterion` – ([`stopWhenAny`](@ref)`(`[`stopAfterIteration`](@ref)`(200), `[`stopWhenGradientNormLess`](@ref)`(10.0^-8))`)
  a functor, see[`StoppingCriterion`](@ref), indicating when to stop.

and the ones that are passed to [`decorateOptions`](@ref) for decorators.

# Output
* `xOpt` – the resulting (approximately critical) point of gradientDescent
* `record` - if activated (using the `record` key, see [`RecordOptions`](@ref)
  an array containing the recorded values.
"""
function subGradientMethod(M::mT,
        F::Function, ∂F::Function, x::MP;
        retraction::Function = exp,
        stepsize::Stepsize = DecreasingStepsize( typicalDistance(M)/5),
        stoppingCriterion::StoppingCriterion = stopAfterIteration(5000),
        kwargs... #especially may contain debug
    ) where {mT <: Manifold, MP <: MPoint}
    p = SubGradientProblem(M,F,∂F)
    o = SubGradientMethodOptions(x,stoppingCriterion, stepsize, retraction)

    o = decorateOptions(o; kwargs...)
    resultO = solve(p,o)
    if hasRecord(resultO)
        return getSolverResult(p,getOptions(resultO)), getRecord(resultO)
    end
    return getSolverResult(p,resultO)
end
function initializeSolver!(p::SubGradientProblem, o::SubGradientMethodOptions)
    o.xOptimal = o.x
    o.∂ = zeroTVector(p.M,o.x)
end
function doSolverStep!(p::SubGradientProblem, o::SubGradientMethodOptions,iter)
    o.∂ = getSubGradient(p,o.x)
    s = getStepsize!(p,o,iter)
    o.x = o.retraction(p.M,o.x,-s*o.∂)
    if getCost(p,o.x) < getCost(p,o.xOptimal)
        o.xOptimal = o.x
    end
end
function getSolverResult(p::SubGradientProblem, o::SubGradientMethodOptions)
    return o.xOptimal
end

#
# TODO specific debugs and records.
#


text:
"""
Object that stores the search results from a single searcher.
"""
struct SearchResult{T<:AbstractFloat}
    id::String
    query_matches::Vector{Tuple{T, Int}}  # vector of (score, idx)
    needle_matches::Vector{String}
    suggestions::MultiDict{String, Tuple{T, String}} # needle => tuples of (score,partial match)
    score_weight::T  # a default weight for scores
end

isempty(result::SearchResult) = isempty(result.query_matches)

"""
Constructs a search result from a list of data ids.
"""
function build_result_from_ids(dbdata,
                               idvals,
                               idvals_key,
                               result_id;
                               id_key=Garamond.DEFAULT_DB_ID_KEY,
                               score_eltype=DEFAULT_VECTORS_ELTYPE,
                               default_score=one(score_eltype),
                               max_matches=length(idvals),
                               linear_scoring=false)
    if idvals_key != id_key
        idxs = db_select_idxs_from_values(dbdata, idvals, idvals_key; id_key=id_key)
    else
        idxs = idvals
    end

    n = min(max_matches, length(idxs))
    if linear_scoring && length(idxs) > 1
        scores = collect(score_eltype, range(1, 0, length=length(idxs)))
    else
        scores = fill(default_score, n)
    end
    return SearchResult(result_id,
                        collect(zip(scores, idxs[1:n])),
                        String[],
                        MultiDict{String, Tuple{score_eltype, String}}(),
                        one(score_eltype))
end

"""
Aggregates search results from several searchers based on
their `aggregation_id` i.e. results from searchers with identical
aggregation id's are merged together into a new search result that
replaces the individual searcher ones.
"""
function aggregate!(results::Vector{S},
                    aggregation_ids::Vector{String};
                    method=RESULT_AGGREGATION_STRATEGY,
                    max_matches=MAX_MATCHES,
                    max_suggestions=MAX_SUGGESTIONS,
                    custom_weights=DEFAULT_CUSTOM_WEIGHTS
                   ) where S<:SearchResult{T} where T
    if !(method in [:minimum, :maximum, :median, :product, :mean])
        @warn "Unknown aggregation strategy :$method. " *
              "Defaulting to $RESULT_AGGREGATION_STRATEGY."
        method = RESULT_AGGREGATION_STRATEGY
    end
    # If all aggregation ids are different (i.e. no aggregation)
    # return results unchanged
    uids  = unique(aggregation_ids)
    length(uids) == length(aggregation_ids) && return results

    # Some aggregation ids are identical (i.e. aggregate)
    for uid in uids
        positions = findall(x->x==uid, aggregation_ids)
        if length(positions) > 1
            target_results = results[positions]
            # aggregate
            qm = [result.query_matches for result in target_results]
            weights = [result.score_weight * T(get(custom_weights, Symbol(result.id), 1.0))
                       for result in target_results]
            merged_query_matches = __aggregate(qm, weights; method=method, max_matches=max_matches)
            # Create SearchResult object
            agg_result = SearchResult(
                uid,
                merged_query_matches,
                unique(vcat((result.needle_matches for result in target_results)...)),
                squash_suggestions(target_results, max_suggestions),
                one(T))
            # replace first occurence that has the non-unique id_aggregation
            results[positions[1]] = agg_result
            # remove other occurences (these have been merged)
            deleteat!(results, positions[2:end])
            deleteat!(aggregation_ids, positions[2:end])
        end
    end
end

function __aggregate(query_matches::Vector{Vector{Tuple{T,Int}}},
                     weights::Vector{T};
                     method=RESULT_AGGREGATION_STRATEGY,
                     max_matches=DEFAULT_MAX_MATCHES,
                    ) where T<:AbstractFloat
    # Preprocess data
    # col  --> searcher index
    # doc  --> document index in search index
    # row  --> index in a matrix
    row = 0
    doc2row = Dict{Int,Int}()
    rowcol2score = Dict{Tuple{Int,Int},T}()
    for (col, qm) in enumerate(query_matches)
        for (score, doc) in qm
            if !(doc in keys(doc2row))
                row += 1
                push!(doc2row, doc=>row)
                push!(rowcol2score, (row, col)=>score)
            else
                push!(rowcol2score, (doc2row[doc], col)=>score)
            end
        end
    end

    # Build matrix with scores for all documents from all searchers
    m, n = length(doc2row), length(query_matches)
    scores = zeros(T, m, n)
    @inbounds for ((row, col), score) in rowcol2score
        scores[row, col] = weights[col] * score
    end

    # Merge results
    final_scores::Vector{T} = zeros(T, m)
    (method == :minimum) && (final_scores = firstcol(minimum(scores, dims=2)))
    (method == :maximum) && (final_scores = firstcol(maximum(scores, dims=2)))
    (method == :median) && (final_scores = firstcol(median(scores, dims=2)))
    (method == :product) && (final_scores = firstcol(prod(scores, dims=2)))
    (method == :mean) && (final_scores = firstcol(mean(scores, dims=2)))

    # Sort and trim result list: intersect sorted score indices by the
    # indices with scores larger than 0 and then trim (the result of the
    # intersection is still reverse ordered)
    row2doc = Dict(v=>k for (k,v) in doc2row)
    idxs = intersect(sortperm(final_scores, rev=true),
                     findall(x->x>zero(T), final_scores))
    tidxs = idxs[1:min(length(idxs), max_matches)]
    return collect(zip(final_scores[tidxs], [row2doc[i] for i in tidxs]))
end

function squash_suggestions(results::Vector{SearchResult{T}},
                            max_suggestions::Int=MAX_SUGGESTIONS
                           ) where T<:AbstractFloat
    suggestions = MultiDict{String, Tuple{T, String}}()
    # Quickly exit if no suggestions are sought
    max_suggestions <=0 && return suggestions
    if length(results) > 1
        # Results from multiple searchers, suggestions have to
        # be processed somewhat:
        #  - keep only needles not found across all searchers
        #  - remove suggestions that correspond to found needles

        # Get the needles not found across all results
        matched_needles = (needle for _result in results
                           for needle in _result.needle_matches)
        missed_needles = union((keys(_result.suggestions)
                                for _result in results)...)
        # Construct suggestions for the whole AggregateSearcher
        for needle in missed_needles
            all_needle_suggestions = Vector{Tuple{T,String}}()
            for _result in results
                if haskey(_result.suggestions, needle) &&
                   !(any(suggestion in matched_needles
                         for (_, suggestion) in _result.suggestions[needle]))
                   # Current key was not found and the suggestions
                   # for it are not found in the matched needles
                   union!(all_needle_suggestions,
                          _result.suggestions[needle])
                end
            end
            if !isempty(all_needle_suggestions)
                sort!(all_needle_suggestions, by=t->t[1])  # sort vector of tuples by distance
                # Keep results with the same distance even if the number is
                # larger than the maximum
                n = min(max_suggestions, length(all_needle_suggestions))
                nn = 0
                d = -1.0
                for (i, (dist, _)) in enumerate(all_needle_suggestions)
                    if i <= n || d == dist
                        d = dist
                        nn = i
                    end
                end
                push!(suggestions, needle=>all_needle_suggestions[1:nn])
            end
        end
    elseif length(results) == 1
        # Results from one searcher, easy situation, just copy the suggestions
        suggestions = results[1].suggestions
    end
    return suggestions
end

# Pretty printer of results
function print_search_results(io::IO,
                              dbdata,
                              result;
                              fields=colnames(dbdata),
                              id_key=DEFAULT_DB_ID_KEY,
                              max_length=50,
                              separator=" - ")
    db_check_id_key(dbdata, id_key)
    nm = length(result.query_matches)

    printstyled(io, "[$(result.id)] ", color=:blue, bold=true)
    printstyled(io, "$(nm) search results", bold=true)

    ch = ifelse(nm==0, ".", ":");
    printstyled(io, "$ch\n")
    print_fields = Tuple(intersect!(unique!([fields..., id_key]), colnames(dbdata)))
    scores, indices = unzip(result.query_matches; ndims=2)
    dataresult = sort(rows(filter(in(indices), dbdata, select=id_key), print_fields),
                      by=row->getproperty(row, id_key))
    for (entry, score) in sort(collect(zip(dataresult, scores[sortperm(indices)])),
                               by=zipped->zipped[2], rev=true)
        entry_string = dbentry2printable(entry,
                                         print_fields,
                                         max_length=max_length,
                                         separator=separator)
        printstyled(io, "  $score ~ ", color=:normal, bold=true)
        printstyled(io, entry_string, "\n", color=:normal)
    end
    __print_suggestions(io, result.suggestions)
end

__print_suggestions(io::IO, suggestions) = begin
    ns = length(suggestions)
    ns > 0 && printstyled(io, "$ns suggestions:\n")
    for (key, s) in suggestions
        printstyled(io, "  \"$key\": ", color=:normal, bold=true)
        printstyled(io, "$(join(map(x->x[2], s), ", "))\n", color=:normal)
    end
end

print_search_results(dbdata,
                     result;
                     fields=colnames(dbdata),
                     id_key=DEFAULT_DB_ID_KEY,
                     max_length=50,
                     separator=" - ") =
    print_search_results(stdout, dbdata, result, fields=fields, id_key=id_key,
                         max_length=max_length, separator=separator)

function print_search_results(io::IO,
                              dbdata,
                              results::AbstractVector;
                              fields=colnames(dbdata),
                              id_key=DEFAULT_DB_ID_KEY,
                              max_length=50,
                              separator=" - ",
                              max_suggestions=MAX_SUGGESTIONS)
    map(result->print_search_results(io, dbdata, result, fields=fields, id_key=id_key,
                                     max_length=max_length, separator=separator), results)
    suggestions = squash_suggestions(results, max_suggestions)
    __print_suggestions(io, suggestions)
end

print_search_results(dbdata,
                     results::AbstractVector;
                     fields=colnames(dbdata),
                     id_key=DEFAULT_DB_ID_KEY,
                     max_length=50,
                     separator=" - ",
                     max_suggestions=MAX_SUGGESTIONS) =
    print_search_results(stdout, dbdata, results, fields=fields, id_key=id_key,
                         max_length=max_length, separator=separator,
                         max_suggestions=max_suggestions)


text:
using Test
using LocalCongruence

@testset "Sample" begin
    @test 1==1
end


text:
### A Pluto.jl notebook ###
# v0.14.7

using Markdown
using InteractiveUtils

# ╔═╡ b9eba282-67d3-11eb-3734-033538ed6c62
using AstroLib, Dates, Plots

# ╔═╡ cf3f5210-67e4-11eb-2b2b-bb38ff2d21b3
md"
# Planetary Orbits

This notebook draws the lines connecting two planets (`Planet1` and `Planet2`) in 2D projection of their respective heliocentric coordinates from `Date1` until `Date2` in positions which are `δDays` apart.

Planets are `1=Mercury`, `2=Venus`, `3=Earth`, `4=Mars`, `5=Jupyter`, `6=Saturnus`, `7=Uranius`, `8=Neptun`, `9=Pluto`.

See also [link1](https://en.wikipedia.org/wiki/Orbit#Planetary_orbits) and [link2](https://www.keplerstern.com/signature-of-the-celestial-spheres/) for extended information.

The notebook uses Julia packages [AstroLib.jl](https://github.com/JuliaAstro/AstroLib.jl) and [Plots.jl](https://github.com/JuliaPlots/Plots.jl), and the module  [Dates](https://julia-doc.readthedocs.io/en/latest/manual/dates/).
"

# ╔═╡ 7cfe5360-69ed-11eb-3fea-8daf1f2706ec
Planets=["Mercury", "Venus", "Earth", "Mars", "Jupyter", "Saturn", "Uranus","Neptune","Pluto"]

# ╔═╡ 44da6540-696c-11eb-2705-efcff349ece8
begin
	Date1=DateTime(2001,1,1)
	Date2=DateTime(2021,1,1)
	δDays=Day(10)
	Planet1=3
	Planet2=4
end

# ╔═╡ 06a3a6b0-67d7-11eb-0c38-5599b546084b
# Range od days in DateTime format
drange=Date1:δDays:Date2

# ╔═╡ d385a4c0-67d8-11eb-2682-2fecbef2468a
# Convert to number of Julian calendar days since epoch -4713-11-24T12:00:00.
jrange=jdcnv.(drange)

# ╔═╡ c657a82e-696c-11eb-26d8-afb78bbd18f7
# Number of timesteps
length(jrange)

# ╔═╡ 9c5221f0-67d7-11eb-0779-09121e38cfb3
# Weekly (or δDays) coordinates of Planet1 in heliocentric spherical system
coord1=[helio(i,Planet1,true) for i in jrange]

# ╔═╡ f9268460-67d8-11eb-0efc-6390d7311ece
# 2D Cartesian projection od heliocentric coordinates of Planet1
xy1=[(i[1]*cos(i[2])*cos(i[3]),i[1]*sin(i[2])*cos(i[3])) for i in coord1]

# ╔═╡ 1592c830-67d8-11eb-2532-6128ac089578
# Weekly (or δDays) coordinates of Planet2 in heliocentric spherical system
coord2=[helio(i,Planet2,true) for i in jrange]

# ╔═╡ 2850f972-67e1-11eb-0ade-dfaff95058dc
# 2D Cartesian projection od heliocentric coordinates of Planet2
xy2=[(i[1]*cos(i[2])*cos(i[3]),i[1]*sin(i[2])*cos(i[3])) for i in coord2]

# ╔═╡ 284659d0-67db-11eb-1c13-75528f34153b
begin
	plotly()
	label1=Planets[Planet1]
	label2=Planets[Planet2]
	scatter(xy1,label=label1,aspect_ratio=1)
	scatter!(xy2,label=label2)
end

# ╔═╡ 067f952e-67dd-11eb-0be6-73f7cd45d333
begin
	plotly()
	title=Planets[Planet1]*"-"*Planets[Planet2]
	plot(color=:white, background=:lightgreen, showaxis=:hide, axis=nothing, 		grid=false, leg=false, aspect_ratio=1,size=(400,400),title=title,fg=:white)
	for i=2:length(xy2)
		plot!([xy1[i],xy2[i]], color=:blue, w=0.5)
	end
	plot!([xy1[1],xy2[1]], color=:white,w=1)
	scatter!([xy1[1],xy2[1]], mcolor=[:red,:red],mscolor=[:red,:red])
end

# ╔═╡ 993655e0-69ee-11eb-2201-6bc1eca2f420
# savefig(title*".svg")

# ╔═╡ Cell order:
# ╟─cf3f5210-67e4-11eb-2b2b-bb38ff2d21b3
# ╠═98cfdfff-6884-4967-a356-19e9e94d5be1
# ╠═b9eba282-67d3-11eb-3734-033538ed6c62
# ╠═7cfe5360-69ed-11eb-3fea-8daf1f2706ec
# ╠═06a3a6b0-67d7-11eb-0c38-5599b546084b
# ╠═d385a4c0-67d8-11eb-2682-2fecbef2468a
# ╠═c657a82e-696c-11eb-26d8-afb78bbd18f7
# ╠═9c5221f0-67d7-11eb-0779-09121e38cfb3
# ╠═1592c830-67d8-11eb-2532-6128ac089578
# ╠═f9268460-67d8-11eb-0efc-6390d7311ece
# ╠═2850f972-67e1-11eb-0ade-dfaff95058dc
# ╠═284659d0-67db-11eb-1c13-75528f34153b
# ╠═44da6540-696c-11eb-2705-efcff349ece8
# ╠═067f952e-67dd-11eb-0be6-73f7cd45d333
# ╠═993655e0-69ee-11eb-2201-6bc1eca2f420

max_stars_repo_path: PlanetaryOrbits backup 1.jl

max_issues_repo_path: PlanetaryOrbits backup 1.jl

max_forks_repo_path: PlanetaryOrbits backup 1.jl


text:
"""
    IRDumps.dump(code::AbstractString; options = ``) -> irs
    IRDumps.dump(cmd::AbstractCmd) -> irs
    IRDumps.parsefile(filepath) -> irs
    IRDumps.parse(io::IO) -> irs

`irs` is an `AbstractDict{String,<:AbstractVector{IRDump}}` where each `IRDump`
is an object with the following fields:

* `function::Union{String,Nothing}`: LLVM-level name of the function
* `pass::Union{String,Nothing}`: LLVM pass name
* `code::String`: LLVM IR
"""
(IRDumps.dump, IRDumps.parse, IRDumps.parsefile)

function IRDumps.dump(code::AbstractString; options = ``)
    cmd = Base.julia_cmd()
    cmd = `$cmd -e 'include_string(Main, read(stdin, String))' $options`
    input = IOBuffer(code)
    return _dump(cmd, input)
end

function IRDumps.dump(cmd::Base.AbstractCmd)
    return _dump(cmd, devnull)
end

_dump(cmd, input) = _dumpwith(IRDumps.parse, cmd, input)

function _dumpwith(f, cmd, input, stdout′ = stderr)
    ipipe = Pipe()
    epipe = Pipe()
    cmd = addenv(cmd, "JULIA_LLVM_ARGS" => "-print-after-all")
    cmd = pipeline(cmd; stdout = stdout′, stderr = epipe, stdin = ipipe)
    proc = run(cmd; wait = false)
    close(epipe.in)
    @sync begin
        @async try
            write(ipipe, input)
        catch
            close(epipe)
        finally
            close(ipipe)
        end
        ans = try
            f(epipe)
        finally
            close(ipipe)
            close(epipe)
        end
        wait(proc)
        return ans
    end
end