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

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

Associates a partner account with your AWS account.

# Arguments
- `sidewalk`: The Sidewalk account credentials.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"Tags"`: The tags attached to the specified resource. Tags are metadata that can be used
  to manage a resource
"""
associate_aws_account_with_partner_account(Sidewalk; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/partner-accounts", Dict{String, Any}("Sidewalk"=>Sidewalk, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
associate_aws_account_with_partner_account(Sidewalk, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/partner-accounts", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Sidewalk"=>Sidewalk, "ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

"""
    associate_wireless_device_with_thing(id, thing_arn)
    associate_wireless_device_with_thing(id, thing_arn, params::Dict{String,<:Any})

Associates a wireless device with a thing.

# Arguments
- `id`: The ID of the resource to update.
- `thing_arn`: The ARN of the thing to associate with the wireless device.

"""
associate_wireless_device_with_thing(Id, ThingArn; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PUT", "/wireless-devices/$(Id)/thing", Dict{String, Any}("ThingArn"=>ThingArn); aws_config=aws_config)
associate_wireless_device_with_thing(Id, ThingArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PUT", "/wireless-devices/$(Id)/thing", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ThingArn"=>ThingArn), params)); aws_config=aws_config)

"""
    associate_wireless_gateway_with_certificate(id, iot_certificate_id)
    associate_wireless_gateway_with_certificate(id, iot_certificate_id, params::Dict{String,<:Any})

Associates a wireless gateway with a certificate.

# Arguments
- `id`: The ID of the resource to update.
- `iot_certificate_id`: The ID of the certificate to associate with the wireless gateway.

"""
associate_wireless_gateway_with_certificate(Id, IotCertificateId; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PUT", "/wireless-gateways/$(Id)/certificate", Dict{String, Any}("IotCertificateId"=>IotCertificateId); aws_config=aws_config)
associate_wireless_gateway_with_certificate(Id, IotCertificateId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PUT", "/wireless-gateways/$(Id)/certificate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("IotCertificateId"=>IotCertificateId), params)); aws_config=aws_config)

"""
    associate_wireless_gateway_with_thing(id, thing_arn)
    associate_wireless_gateway_with_thing(id, thing_arn, params::Dict{String,<:Any})

Associates a wireless gateway with a thing.

# Arguments
- `id`: The ID of the resource to update.
- `thing_arn`: The ARN of the thing to associate with the wireless gateway.

"""
associate_wireless_gateway_with_thing(Id, ThingArn; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PUT", "/wireless-gateways/$(Id)/thing", Dict{String, Any}("ThingArn"=>ThingArn); aws_config=aws_config)
associate_wireless_gateway_with_thing(Id, ThingArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PUT", "/wireless-gateways/$(Id)/thing", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ThingArn"=>ThingArn), params)); aws_config=aws_config)

"""
    create_destination(expression, expression_type, name, role_arn)
    create_destination(expression, expression_type, name, role_arn, params::Dict{String,<:Any})

Creates a new destination that maps a device message to an AWS IoT rule.

# Arguments
- `expression`: The rule name or topic rule to send messages to.
- `expression_type`: The type of value in Expression.
- `name`: The name of the new resource.
- `role_arn`: The ARN of the IAM Role that authorizes the destination.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"Description"`: The description of the new resource.
- `"Tags"`: The tags to attach to the new destination. Tags are metadata that can be used
  to manage a resource.
"""
create_destination(Expression, ExpressionType, Name, RoleArn; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/destinations", Dict{String, Any}("Expression"=>Expression, "ExpressionType"=>ExpressionType, "Name"=>Name, "RoleArn"=>RoleArn, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_destination(Expression, ExpressionType, Name, RoleArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/destinations", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Expression"=>Expression, "ExpressionType"=>ExpressionType, "Name"=>Name, "RoleArn"=>RoleArn, "ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

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

Creates a new device profile.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"LoRaWAN"`: The device profile information to use to create the device profile.
- `"Name"`: The name of the new resource.
- `"Tags"`: The tags to attach to the new device profile Tags are metadata that can be used
  to manage a resource.
"""
create_device_profile(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/device-profiles", Dict{String, Any}("ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_device_profile(params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/device-profiles", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

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

Creates a new service profile.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"LoRaWAN"`: The service profile information to use to create the service profile.
- `"Name"`: The name of the new resource.
- `"Tags"`: The tags to attach to the new service profile. Tags are metadata that can be
  used to manage a resource.
"""
create_service_profile(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/service-profiles", Dict{String, Any}("ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_service_profile(params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/service-profiles", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

"""
    create_wireless_device(destination_name, type)
    create_wireless_device(destination_name, type, params::Dict{String,<:Any})

Provisions a wireless device.

# Arguments
- `destination_name`: The name of the destination to assign to the new wireless device.
- `type`: The wireless device type.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"Description"`: The description of the new resource.
- `"LoRaWAN"`: The device configuration information to use to create the wireless device.
- `"Name"`: The name of the new resource.
"""
create_wireless_device(DestinationName, Type; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-devices", Dict{String, Any}("DestinationName"=>DestinationName, "Type"=>Type, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_wireless_device(DestinationName, Type, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-devices", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("DestinationName"=>DestinationName, "Type"=>Type, "ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

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

Provisions a wireless gateway.

# Arguments
- `lo_ra_wan`: The gateway configuration information to use to create the wireless gateway.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"Description"`: The description of the new resource.
- `"Name"`: The name of the new resource.
- `"Tags"`: The tags to attach to the new wireless gateway. Tags are metadata that can be
  used to manage a resource.
"""
create_wireless_gateway(LoRaWAN; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-gateways", Dict{String, Any}("LoRaWAN"=>LoRaWAN, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_wireless_gateway(LoRaWAN, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-gateways", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("LoRaWAN"=>LoRaWAN, "ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

"""
    create_wireless_gateway_task(id, wireless_gateway_task_definition_id)
    create_wireless_gateway_task(id, wireless_gateway_task_definition_id, params::Dict{String,<:Any})

Creates a task for a wireless gateway.

# Arguments
- `id`: The ID of the resource to update.
- `wireless_gateway_task_definition_id`: The ID of the WirelessGatewayTaskDefinition.

"""
create_wireless_gateway_task(Id, WirelessGatewayTaskDefinitionId; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-gateways/$(Id)/tasks", Dict{String, Any}("WirelessGatewayTaskDefinitionId"=>WirelessGatewayTaskDefinitionId); aws_config=aws_config)
create_wireless_gateway_task(Id, WirelessGatewayTaskDefinitionId, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-gateways/$(Id)/tasks", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("WirelessGatewayTaskDefinitionId"=>WirelessGatewayTaskDefinitionId), params)); aws_config=aws_config)

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

Creates a gateway task definition.

# Arguments
- `auto_create_tasks`: Whether to automatically create tasks using this task definition for
  all gateways with the specified current version. If false, the task must me created by
  calling CreateWirelessGatewayTask.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"ClientRequestToken"`: Each resource must have a unique client request token. If you try
  to create a new resource with the same token as a resource that already exists, an
  exception occurs. If you omit this value, AWS SDKs will automatically generate a unique
  client request.
- `"Name"`: The name of the new resource.
- `"Tags"`: The tags attached to the specified resource. Tags are metadata that can be used
  to manage a resource
- `"Update"`: Information about the gateways to update.
"""
create_wireless_gateway_task_definition(AutoCreateTasks; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-gateway-task-definitions", Dict{String, Any}("AutoCreateTasks"=>AutoCreateTasks, "ClientRequestToken"=>string(uuid4())); aws_config=aws_config)
create_wireless_gateway_task_definition(AutoCreateTasks, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-gateway-task-definitions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AutoCreateTasks"=>AutoCreateTasks, "ClientRequestToken"=>string(uuid4())), params)); aws_config=aws_config)

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

Deletes a destination.

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

"""
delete_destination(Name; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/destinations/$(Name)"; aws_config=aws_config)
delete_destination(Name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/destinations/$(Name)", params; aws_config=aws_config)

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

Deletes a device profile.

# Arguments
- `id`: The ID of the resource to delete.

"""
delete_device_profile(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/device-profiles/$(Id)"; aws_config=aws_config)
delete_device_profile(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/device-profiles/$(Id)", params; aws_config=aws_config)

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

Deletes a service profile.

# Arguments
- `id`: The ID of the resource to delete.

"""
delete_service_profile(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/service-profiles/$(Id)"; aws_config=aws_config)
delete_service_profile(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/service-profiles/$(Id)", params; aws_config=aws_config)

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

Deletes a wireless device.

# Arguments
- `id`: The ID of the resource to delete.

"""
delete_wireless_device(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-devices/$(Id)"; aws_config=aws_config)
delete_wireless_device(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-devices/$(Id)", params; aws_config=aws_config)

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

Deletes a wireless gateway.

# Arguments
- `id`: The ID of the resource to delete.

"""
delete_wireless_gateway(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)"; aws_config=aws_config)
delete_wireless_gateway(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)", params; aws_config=aws_config)

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

Deletes a wireless gateway task.

# Arguments
- `id`: The ID of the resource to delete.

"""
delete_wireless_gateway_task(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)/tasks"; aws_config=aws_config)
delete_wireless_gateway_task(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)/tasks", params; aws_config=aws_config)

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

Deletes a wireless gateway task definition. Deleting this task definition does not affect
tasks that are currently in progress.

# Arguments
- `id`: The ID of the resource to delete.

"""
delete_wireless_gateway_task_definition(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateway-task-definitions/$(Id)"; aws_config=aws_config)
delete_wireless_gateway_task_definition(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateway-task-definitions/$(Id)", params; aws_config=aws_config)

"""
    disassociate_aws_account_from_partner_account(partner_account_id, partner_type)
    disassociate_aws_account_from_partner_account(partner_account_id, partner_type, params::Dict{String,<:Any})

Disassociates your AWS account from a partner account. If PartnerAccountId and PartnerType
are null, disassociates your AWS account from all partner accounts.

# Arguments
- `partner_account_id`: The partner account ID to disassociate from the AWS account.
- `partner_type`: The partner type.

"""
disassociate_aws_account_from_partner_account(PartnerAccountId, partnerType; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/partner-accounts/$(PartnerAccountId)", Dict{String, Any}("partnerType"=>partnerType); aws_config=aws_config)
disassociate_aws_account_from_partner_account(PartnerAccountId, partnerType, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/partner-accounts/$(PartnerAccountId)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("partnerType"=>partnerType), params)); aws_config=aws_config)

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

Disassociates a wireless device from its currently associated thing.

# Arguments
- `id`: The ID of the resource to update.

"""
disassociate_wireless_device_from_thing(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-devices/$(Id)/thing"; aws_config=aws_config)
disassociate_wireless_device_from_thing(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-devices/$(Id)/thing", params; aws_config=aws_config)

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

Disassociates a wireless gateway from its currently associated certificate.

# Arguments
- `id`: The ID of the resource to update.

"""
disassociate_wireless_gateway_from_certificate(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)/certificate"; aws_config=aws_config)
disassociate_wireless_gateway_from_certificate(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)/certificate", params; aws_config=aws_config)

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

Disassociates a wireless gateway from its currently associated thing.

# Arguments
- `id`: The ID of the resource to update.

"""
disassociate_wireless_gateway_from_thing(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)/thing"; aws_config=aws_config)
disassociate_wireless_gateway_from_thing(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/wireless-gateways/$(Id)/thing", params; aws_config=aws_config)

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

Gets information about a destination.

# Arguments
- `name`: The name of the resource to get.

"""
get_destination(Name; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/destinations/$(Name)"; aws_config=aws_config)
get_destination(Name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/destinations/$(Name)", params; aws_config=aws_config)

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

Gets information about a device profile.

# Arguments
- `id`: The ID of the resource to get.

"""
get_device_profile(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/device-profiles/$(Id)"; aws_config=aws_config)
get_device_profile(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/device-profiles/$(Id)", params; aws_config=aws_config)

"""
    get_partner_account(partner_account_id, partner_type)
    get_partner_account(partner_account_id, partner_type, params::Dict{String,<:Any})

Gets information about a partner account. If PartnerAccountId and PartnerType are null,
returns all partner accounts.

# Arguments
- `partner_account_id`: The partner account ID to disassociate from the AWS account.
- `partner_type`: The partner type.

"""
get_partner_account(PartnerAccountId, partnerType; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/partner-accounts/$(PartnerAccountId)", Dict{String, Any}("partnerType"=>partnerType); aws_config=aws_config)
get_partner_account(PartnerAccountId, partnerType, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/partner-accounts/$(PartnerAccountId)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("partnerType"=>partnerType), params)); aws_config=aws_config)

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

Gets the account-specific endpoint for Configuration and Update Server (CUPS) protocol or
LoRaWAN Network Server (LNS) connections.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"serviceType"`: The service type for which to get endpoint information about. Can be
  CUPS for the Configuration and Update Server endpoint, or LNS for the LoRaWAN Network
  Server endpoint.
"""
get_service_endpoint(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/service-endpoint"; aws_config=aws_config)
get_service_endpoint(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/service-endpoint", params; aws_config=aws_config)

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

Gets information about a service profile.

# Arguments
- `id`: The ID of the resource to get.

"""
get_service_profile(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/service-profiles/$(Id)"; aws_config=aws_config)
get_service_profile(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/service-profiles/$(Id)", params; aws_config=aws_config)

"""
    get_wireless_device(identifier, identifier_type)
    get_wireless_device(identifier, identifier_type, params::Dict{String,<:Any})

Gets information about a wireless device.

# Arguments
- `identifier`: The identifier of the wireless device to get.
- `identifier_type`: The type of identifier used in identifier.

"""
get_wireless_device(Identifier, identifierType; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-devices/$(Identifier)", Dict{String, Any}("identifierType"=>identifierType); aws_config=aws_config)
get_wireless_device(Identifier, identifierType, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-devices/$(Identifier)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("identifierType"=>identifierType), params)); aws_config=aws_config)

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

Gets operating information about a wireless device.

# Arguments
- `id`: The ID of the wireless device for which to get the data.

"""
get_wireless_device_statistics(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-devices/$(Id)/statistics"; aws_config=aws_config)
get_wireless_device_statistics(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-devices/$(Id)/statistics", params; aws_config=aws_config)

"""
    get_wireless_gateway(identifier, identifier_type)
    get_wireless_gateway(identifier, identifier_type, params::Dict{String,<:Any})

Gets information about a wireless gateway.

# Arguments
- `identifier`: The identifier of the wireless gateway to get.
- `identifier_type`: The type of identifier used in identifier.

"""
get_wireless_gateway(Identifier, identifierType; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Identifier)", Dict{String, Any}("identifierType"=>identifierType); aws_config=aws_config)
get_wireless_gateway(Identifier, identifierType, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Identifier)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("identifierType"=>identifierType), params)); aws_config=aws_config)

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

Gets the ID of the certificate that is currently associated with a wireless gateway.

# Arguments
- `id`: The ID of the resource to get.

"""
get_wireless_gateway_certificate(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/certificate"; aws_config=aws_config)
get_wireless_gateway_certificate(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/certificate", params; aws_config=aws_config)

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

Gets the firmware version and other information about a wireless gateway.

# Arguments
- `id`: The ID of the resource to get.

"""
get_wireless_gateway_firmware_information(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/firmware-information"; aws_config=aws_config)
get_wireless_gateway_firmware_information(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/firmware-information", params; aws_config=aws_config)

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

Gets operating information about a wireless gateway.

# Arguments
- `id`: The ID of the wireless gateway for which to get the data.

"""
get_wireless_gateway_statistics(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/statistics"; aws_config=aws_config)
get_wireless_gateway_statistics(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/statistics", params; aws_config=aws_config)

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

Gets information about a wireless gateway task.

# Arguments
- `id`: The ID of the resource to get.

"""
get_wireless_gateway_task(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/tasks"; aws_config=aws_config)
get_wireless_gateway_task(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways/$(Id)/tasks", params; aws_config=aws_config)

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

Gets information about a wireless gateway task definition.

# Arguments
- `id`: The ID of the resource to get.

"""
get_wireless_gateway_task_definition(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateway-task-definitions/$(Id)"; aws_config=aws_config)
get_wireless_gateway_task_definition(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateway-task-definitions/$(Id)", params; aws_config=aws_config)

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

Lists the destinations registered to your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
"""
list_destinations(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/destinations"; aws_config=aws_config)
list_destinations(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/destinations", params; aws_config=aws_config)

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

Lists the device profiles registered to your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
"""
list_device_profiles(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/device-profiles"; aws_config=aws_config)
list_device_profiles(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/device-profiles", params; aws_config=aws_config)

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

Lists the partner accounts associated with your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
"""
list_partner_accounts(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/partner-accounts"; aws_config=aws_config)
list_partner_accounts(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/partner-accounts", params; aws_config=aws_config)

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

Lists the service profiles registered to your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
"""
list_service_profiles(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/service-profiles"; aws_config=aws_config)
list_service_profiles(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/service-profiles", params; aws_config=aws_config)

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

Lists the tags (metadata) you have assigned to the resource.

# Arguments
- `resource_arn`: The ARN of the resource for which to list tags.

"""
list_tags_for_resource(resourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/tags", Dict{String, Any}("resourceArn"=>resourceArn); aws_config=aws_config)
list_tags_for_resource(resourceArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/tags", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("resourceArn"=>resourceArn), params)); aws_config=aws_config)

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

Lists the wireless devices registered to your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"destinationName"`: A filter to list only the wireless devices that use this destination.
- `"deviceProfileId"`: A filter to list only the wireless devices that use this device
  profile.
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
- `"serviceProfileId"`: A filter to list only the wireless devices that use this service
  profile.
- `"wirelessDeviceType"`: A filter to list only the wireless devices that use this wireless
  device type.
"""
list_wireless_devices(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-devices"; aws_config=aws_config)
list_wireless_devices(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-devices", params; aws_config=aws_config)

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

List the wireless gateway tasks definitions registered to your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
- `"taskDefinitionType"`: A filter to list only the wireless gateway task definitions that
  use this task definition type.
"""
list_wireless_gateway_task_definitions(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateway-task-definitions"; aws_config=aws_config)
list_wireless_gateway_task_definitions(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateway-task-definitions", params; aws_config=aws_config)

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

Lists the wireless gateways registered to your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return in this operation.
- `"nextToken"`: To retrieve the next set of results, the nextToken value from a previous
  response; otherwise null to receive the first set of results.
"""
list_wireless_gateways(; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways"; aws_config=aws_config)
list_wireless_gateways(params::AbstractDict{String, Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("GET", "/wireless-gateways", params; aws_config=aws_config)

"""
    send_data_to_wireless_device(id, payload_data, transmit_mode)
    send_data_to_wireless_device(id, payload_data, transmit_mode, params::Dict{String,<:Any})

Sends a decrypted application data frame to a device.

# Arguments
- `id`: The ID of the wireless device to receive the data.
- `payload_data`: The message payload to send.
- `transmit_mode`: The transmit mode to use to send data to the wireless device. Can be: 0
  for UM (unacknowledge mode) or 1 for AM (acknowledge mode).

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"WirelessMetadata"`: Metadata about the message request.
"""
send_data_to_wireless_device(Id, PayloadData, TransmitMode; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-devices/$(Id)/data", Dict{String, Any}("PayloadData"=>PayloadData, "TransmitMode"=>TransmitMode); aws_config=aws_config)
send_data_to_wireless_device(Id, PayloadData, TransmitMode, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-devices/$(Id)/data", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("PayloadData"=>PayloadData, "TransmitMode"=>TransmitMode), params)); aws_config=aws_config)

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

Adds a tag to a resource.

# Arguments
- `tags`: Adds to or modifies the tags of the given resource. Tags are metadata that can be
  used to manage a resource.
- `resource_arn`: The ARN of the resource to add tags to.

"""
tag_resource(Tags, resourceArn; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/tags", Dict{String, Any}("Tags"=>Tags, "resourceArn"=>resourceArn); aws_config=aws_config)
tag_resource(Tags, resourceArn, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/tags", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Tags"=>Tags, "resourceArn"=>resourceArn), params)); aws_config=aws_config)

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

Simulates a provisioned device by sending an uplink data payload of Hello.

# Arguments
- `id`: The ID of the wireless device to test.

"""
test_wireless_device(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-devices/$(Id)/test"; aws_config=aws_config)
test_wireless_device(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("POST", "/wireless-devices/$(Id)/test", params; aws_config=aws_config)

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

Removes one or more tags from a resource.

# Arguments
- `resource_arn`: The ARN of the resource to remove tags from.
- `tag_keys`: A list of the keys of the tags to remove from the resource.

"""
untag_resource(resourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/tags", Dict{String, Any}("resourceArn"=>resourceArn, "tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(resourceArn, tagKeys, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("DELETE", "/tags", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("resourceArn"=>resourceArn, "tagKeys"=>tagKeys), params)); aws_config=aws_config)

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

Updates properties of a destination.

# Arguments
- `name`: The new name of the resource.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A new description of the resource.
- `"Expression"`: The new rule name or topic rule to send messages to.
- `"ExpressionType"`: The type of value in Expression.
- `"RoleArn"`: The ARN of the IAM Role that authorizes the destination.
"""
update_destination(Name; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/destinations/$(Name)"; aws_config=aws_config)
update_destination(Name, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/destinations/$(Name)", params; aws_config=aws_config)

"""
    update_partner_account(partner_account_id, sidewalk, partner_type)
    update_partner_account(partner_account_id, sidewalk, partner_type, params::Dict{String,<:Any})

Updates properties of a partner account.

# Arguments
- `partner_account_id`: The ID of the partner account to update.
- `sidewalk`: The Sidewalk account credentials.
- `partner_type`: The partner type.

"""
update_partner_account(PartnerAccountId, Sidewalk, partnerType; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/partner-accounts/$(PartnerAccountId)", Dict{String, Any}("Sidewalk"=>Sidewalk, "partnerType"=>partnerType); aws_config=aws_config)
update_partner_account(PartnerAccountId, Sidewalk, partnerType, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/partner-accounts/$(PartnerAccountId)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Sidewalk"=>Sidewalk, "partnerType"=>partnerType), params)); aws_config=aws_config)

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

Updates properties of a wireless device.

# Arguments
- `id`: The ID of the resource to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A new description of the resource.
- `"DestinationName"`: The name of the new destination for the device.
- `"LoRaWAN"`: The updated wireless device's configuration.
- `"Name"`: The new name of the resource.
"""
update_wireless_device(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/wireless-devices/$(Id)"; aws_config=aws_config)
update_wireless_device(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/wireless-devices/$(Id)", params; aws_config=aws_config)

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

Updates properties of a wireless gateway.

# Arguments
- `id`: The ID of the resource to update.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: A new description of the resource.
- `"Name"`: The new name of the resource.
"""
update_wireless_gateway(Id; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/wireless-gateways/$(Id)"; aws_config=aws_config)
update_wireless_gateway(Id, params::AbstractDict{String, <:Any}; aws_config::AbstractAWSConfig=global_aws_config()) = iot_wireless("PATCH", "/wireless-gateways/$(Id)", params; aws_config=aws_config)


text:
""" Code to compute Mie scattering parameters based on MieScatter julia
package

https://github.com/dronir/MieScatter.jl

"""

module MieScat

using FastGaussQuadrature
using MieScatter

"""
    g, ω₀, Qext = compute(r, λ, m; order=1000)

Compute the parameters `g`, `ω₀`, `Qext` for given radius of spherical particle,
`r`, wavelength `λ` and refractive index m (generally a complex number).
"""
function compute(r, λ, m; order=1000)
    μ, w = gausslegendre(order)
    theta = acos.(μ)

    S, _, _, _ = compute_mie(size_parameter(r, λ), m, theta)

    # For some reason compute_mie returns Qext and Qback only when we ask
    # for forward and / or backward scattering
    _, Qsca, Qext, Qback = compute_mie(size_parameter(r, λ), m, [0, π])

    ω₀ = Qsca / Qext
    g = sum(μ .* w .* S[:, 1]) / sum(w .* S[:, 1])

    g, ω₀, Qext
end

end

ext: jl


text:
cd(@__DIR__); include("setups/rgc100.jl");

##
ϵ = 0.3
pair_inds, 𝛟1 = find_pairinds(W; ϵ = ϵ)

## (a) fiedler vector on RGC100
gr(dpi = 200)
plt = gplot(W, X; width = 1)
    scatter_gplot!(X; marker = 𝛟1, plotOrder = :l2s)
    plot!(xlims = [-180, 220], xlabel = "x(μm)", ylabel = "y(μm)", frame = :box,
    size = (520, 500), right_margin = 5mm)
savefig(plt, "../figs/RGC100_fiedler.png")

## (b) fiedler embedding
pyplot(dpi = 200)
plt = plot(size = (520, 150))
    plot!(-0.055:0.11:0.055, zeros(2), c = :black, lw = 2, yticks = false,
    xlab = latexstring("\\phi^{rw}_1(v)"))
    scatter!(𝛟1, 0:0, ylim = [-0.2, 0.2], grid = false,
    ms = 4, mswidth = 0, legend = false, frame = :box, c = :black)
savefig(plt, "../figs/RGC100_fiedler_embedding.png")


text:
# Occupations
const WORKING = 1
const RETIRED = 2
const UNEMPLOYED = 3
const JOB_SEEKING = 4
const SCHOOL = 5
const OTHER = 6

# Age categories
const CHILD_AGE = 20
const INFANT = 3
const ELDER_AGE = 70
const RETIREMENT_AGE = 65

# Age categories - contact generation
const PRE_SCHOOL_LIMIT = 6
const PRIMARY_SCHOOL_LIMIT = 12
const SECONDARY_SCHOOL_LIMIT = 18

# Time interval
const DAY_START = 7
const DAY_MEDIAN = 12
const DAY_END = 24
const SCHOOL_START = 8
const SCHOOL_END = 15
const WORK_START = 8
const WORK_END = 18

# Places
const PLACE_HOME = 1
const PLACE_WORK = 2
const PLACE_SCHOOL = 3
const PLACE_TRANSPORT = 4
const PLACE_LEISURE = 5

# Day Phase
const MORNING = 1
const AFTERNOON = 2
const EVENING = 3

# Info JSON strings
const WORKPLACES_START_ID = "workplaces_start_id"
const SCHOOLS_START_ID = "schools_start_id"
const TRANSPORTS_START_ID = "transports_start_id"
const HOUSEHOLDS_START_ID = "households_start_id"

max_stars_repo_name: Luigi-Crisci/Hypergraphs-Epidemics

max_issues_repo_name: Luigi-Crisci/Hypergraphs-Epidemics

max_forks_repo_name: Luigi-Crisci/Hypergraphs-Epidemics


text:
"""
        initial_basic_subset_multivariate_data(X, m, method="mahalanobis")
This function returns the m-subset according to algorithm #1 for multivariate data.
Two methods V1 and V2 are defined in the paper which use Mahalanobis distance or distance from the coordinate-wise median. The m subset returned is guaranteed to be full rank.

# Arguments
 - `X`: The multivariate matrix where each row is a data point
 - `m`: The number of points to include in the initial subset
 - `method`: The distance method to use for selecting the points for initial subset
"""
function initial_basic_subset_multivariate_data(X::Array{Float64,2}, m::Int; method::String="mahalanobis")
    n, p = size(X)
    if method == "mahalanobis"
        distances = sqrt.(diag(mahalanobisSquaredMatrix(X)))
    elseif method == "median"
        median_vector = applyColumns(median, X)
        distances = [norm(X[i, :] - median_vector, 2) for i in 1:n]
    else
        return
    end

    return select_subset(X, m, distances)
end

"""
        select_subset(X, m, distances)
This function returns the list of indices which have the least distances as given in the distances array.
It also guarantees that at least m indices are returned and that the selected indices have the full rank.

# Arguments
 - `X`: The multivariate matrix where each row is a data point.
 - `m`: The minimum number of points to include in the subset indices.
 - `distances`: The distances vector used for selecting minumum distance indices.
"""
function select_subset(X::Array{Float64,2}, m::Int, distances::Array{Float64})
    rank_x = rank(X)
    sorted_distances = sortperm(distances)
    subset = sorted_distances[1:m]

    # Test for full rank of the covariance matrix and
    # increase the m until resulting covariance matrix is full rank
    cov_matrix = X[subset, :]'X[subset, :]
    rank_cov_matrix = rank(cov_matrix)
    computed_m = m
    while (rank_cov_matrix < rank_x)
        computed_m = computed_m + 1
        subset = sorted_distances[1:computed_m]
        cov_matrix = X[subset, :]'X[subset, :]
        rank_cov_matrix = rank(cov_matrix)
    end
    return subset
end

"""
        bacon_multivariate_outlier_detection(X, m, method, alpha)
This function performs the outlier detection for multivariate data according to algorithm #3. This is used by algorithm #4 to compute the initial subset.

# Arguments
 - `X`: The multivariate data matrix.
 - `m`: The minimum number of points to include in the initial subset
 - `method`: The distance method to use for selecting the points for initial subset
 - `alpha`: The quantile used for cutoff
"""
function bacon_multivariate_outlier_detection(X::Array{Float64,2}, m::Int; method::String="mahalanobis", alpha::Float64=0.025)
    n, p = size(X)
    chisquared = Chisq(p)
    chisqcrit = quantile(chisquared, 1.0 - (alpha / n))
    chi = sqrt(chisqcrit)
    h = Int(floor((n + p + 1) / 2))
    c_np = 1 + (p + 1) / (n - p) + 1 / (n - h - p)
    initial_basic_subset = initial_basic_subset_multivariate_data(X, m, method=method)
    r_prev = 0
    r = length(initial_basic_subset)
    subset = initial_basic_subset
    distances = zeros(Float64, n)

    # iterate until the size of the subset no longer changes
    while (r_prev != r)
        mean_basic_subset = mean(X[subset], dims=1)
        cov_basic_subset = X[subset]'X[subset]
        distances = sqrt.(diag(mahalanobisSquaredMatrix(X, meanvector=mean_basic_subset, covmatrix=cov_basic_subset)))
        c_hr = (h - r) / (h + r)
        c_hr = c_hr < 0 ? 0 : c_hr
        c_npr = c_hr + c_np
        cutoff = c_npr * chi
        subset = filter(x -> distances[x] < cutoff, 1:n)

        # update r
        r_prev = r
        r = length(subset)
    end
    d = Dict()
    d["outliers"] = setdiff(1:n, subset)
    d["distances"] = distances
    return d
end

"""
        compute_t_distance(X, y, subset)
This function computes the t distance for each point and returns the distance vector.

# Arguments:
 - `X`: The multivariate data matrix.
 - `y`: The output vector
 - `subset`: The vector which denotes the points inside the subset, used to scale the residuals accordingly.
"""
function compute_t_distance(X::Array{Float64,2}, y::Array{Float64}, subset::Array{Int64})
    n, p = size(X)
    t = zeros(Float64, n)
    least_squares_fit = ols(X[subset, :], y[subset])
    betas = coef(least_squares_fit)
    err = residuals(least_squares_fit)
    sigma = sqrt((err'err) / (n - p))
    covmatrix_inv = inv(X[subset, :]'X[subset, :])
    for i in 1:n
        scale_factor = (X[i, :]') * (covmatrix_inv * X[i, :])
        residual = (y[i] - X[i, :]' * betas)
        if i in subset
            @inbounds t[i] = residual / (sigma * sqrt(1 - scale_factor))
        else
            @inbounds t[i] = residual / (sigma * sqrt(1 + scale_factor))
        end
    end
    return abs.(t)
end

"""
        bacon_regression_initial_subset(X, y, m, method, alpha)
This function computes the initial subset having at least m elements which are likely to be free of outliers used for the BACON algorithm.

# Arguments:
 - `X`: The multivariate data matrix.
 - `y`: The response vector.
 - `m`: The minimum number of points to include in the initial subset
 - `method`: The distance method to use for selecting the points for initial subset
 - `alpha`: The quantile used for cutoff
"""
function bacon_regression_initial_subset(
        X::Array{Float64,2},
        y::Array{Float64},
        m::Int;
        method::String="mahalanobis",
        alpha=0.025
)
    n, p = size(X)

    # remove the constant column and apply bacon_multivariate algorithm
    distances = bacon_multivariate_outlier_detection(X[:, 2:end], m, method=method, alpha=alpha)["distances"]
    initial_subset = select_subset(X, m, distances)

    t = compute_t_distance(X, y, initial_subset)
    basic_subset = select_subset(X, p + 1, t)

    r = length(basic_subset)
    while (r < m)
        t = compute_t_distance(X, y, basic_subset)
        basic_subset = select_subset(X, r + 1, t)
        r = length(basic_subset)
    end

    return basic_subset
end

function bacon(
        X::Array{Float64,2},
        y::Array{Float64};
        m::Int,
        method::String="mahalanobis",
        alpha=0.025
)
    n, p = size(X)
    subset = bacon_regression_initial_subset(X, y, m, method=method, alpha=alpha)
    r_prev = 0
    r = length(subset)
    while (r != r_prev)
        t = compute_t_distance(X, y, subset)
        tdist = TDist(r - p)
        cutoff = quantile(tdist,  1 - alpha / (2 * (r + 1)))
        subset = filter(x -> t[x] < cutoff, 1:n)
        r_prev = r
        r = length(subset)
    end
    return setdiff(1:n, subset)
end

"""
        bacon(setting, m, method, alpha)
Run the BACON algorithm to detect outliers on regression data.

# Arguments:
 - `setting`: RegressionSetting object with a formula and a dataset.
 - `m`: The number of elements to be included in the initial subset.
 - `method`: The distance method to use for selecting the points for initial subset
 - `alpha`: The quantile used for cutoff

# Examples
```julia-repl
julia> reg = createRegressionSetting(@formula(stackloss ~ airflow + watertemp + acidcond), stackloss)
julia> bacon_multivariate_outlier_detection(reg, m=12)
4-element Array{Int64,1}:
  1
  3
  4
 21

```
# References
Billor, Nedret, Ali S. Hadi, and Paul F. Velleman. "BACON: blocked adaptive computationally efficient outlier nominators."
Computational statistics & data analysis 34.3 (2000): 279-298.
"""
function bacon(setting::RegressionSetting; m::Int, method::String="mahalanobis", alpha=0.025)
    X, y = @extractRegressionSetting setting
    return bacon(X, y, m=m, method=method, alpha=alpha)
end

max_stars_repo_name: angeris/LinRegOutliers

max_issues_repo_name: angeris/LinRegOutliers

max_forks_repo_name: angeris/LinRegOutliers


text:
module F1Method

#======================================================================
This package (the code below) implements the F-1 method as described
in the work of Pasquier et al. (2019). The numbers in parentheses
refer to the Equation numbers in the above manuscript. A bibtex
citation file is available in the GitHub repository.
======================================================================#

using LinearAlgebra, ForwardDiff, SciMLBase

"""
    Mem

Memory cache to store reusable objects.
Contains
- `s`   the steady-state solution, 𝒔(𝒑)
- `A`   the factors of 𝐀 = ∇ₓ𝑭(𝒔,𝒑)
- `∇s`  the derivative ∇𝒔(𝒑)
- `∇ₓf` the derivative ∇ₓ𝑓(𝒔,𝒑)
- `p`   the parameters 𝒑
The `Mem`-type object should be initialized with `initialize_mem`.
"""
mutable struct Mem{Ts, TA, T∇s, T∇ₓf, Tp}
    s::Ts     # 𝒔(𝒑)
    A::TA     # factors of 𝐀 = ∇ₓ𝑭(𝒔,𝒑)
    ∇s::T∇s   # ∇𝒔(𝒑)
    ∇ₓf::T∇ₓf # ∇ₓ𝑓(𝒔,𝒑)
    p::Tp     # 𝒑 that matches all but 𝒔(𝒑)
    psol::Tp  # 𝒑 that matches 𝒔(𝒑)
end

function update_mem!(f, F, ∇ₓf, mem, p, alg; options...)
    if p ≠ mem.p                      # only update mem if 𝒑 has changed
        update_solution!(F, mem, p, alg; options...)
        ∇ₚF = ForwardDiff.jacobian(p -> F(mem.s, p), p)
        mem.A = factorize(F.jac(mem.s, p)) # update factors of ∇ₓ𝑭(𝒔,𝒑)
        mem.∇s .= mem.A \ -∇ₚF           # update ∇𝒔
        mem.∇ₓf .= ∇ₓf(mem.s, p)         # update ∇ₓ𝑓(𝒔,𝒑)
        mem.p .= p                  # update 𝒑 for the variables above
    end
end

function update_solution!(F, mem, p, alg; options...)
    if p ≠ mem.psol
        prob = SteadyStateProblem(F, mem.s, p) # define problem
        mem.s .= solve(prob, alg; options...).u      # update 𝒔
        mem.psol .= p                          # update 𝒑 for 𝒔
    end
end

"""
    objective(f, F, mem, p, alg; options...)

Returns `f(x,p)` such that `F(x,p)=0` using the F-1 method.

Specifically, `objective(f, F, mem, p, alg; options...)`
evaluates the objective function defined by `f̂(p) = f(s(p),p)`, where
`s(p)`, which is the steady-state solution (i.e., such that `F(s(p),p)=0`)
is computed by the iterative Newton-type solver `alg`.
The memory cache `mem` must be supplied.
"""
function objective(f, F, mem, p, alg; options...)
    update_solution!(F, mem, p, alg; options...)
    return f(mem.s, p)
end

"""
    gradient(f, F, ∇ₓf, mem, p, alg; options...)

Returns the gradient of the `objective` function using the F-1 method.
"""
function gradient(f, F, ∇ₓf, mem, p, alg; options...)
    update_mem!(f, F, ∇ₓf, mem, p, alg; options...)
    s, ∇s, m = mem.s, mem.∇s, length(p)
    ∇ₚf = ForwardDiff.jacobian(p -> [f(s,p)], p)
    return mem.∇ₓf * ∇s + ∇ₚf
end

"""
    hessian(f, F, ∇ₓf, mem, p, alg; options...)

Returns the Hessian of the `objective` function using the F-1 method.
"""
function hessian(f, F, ∇ₓf, mem, p, alg; options...)
    update_mem!(f, F, ∇ₓf, mem, p, alg; options...)
    s, A, ∇s, m = mem.s, mem.A, mem.∇s, length(p)
    A⁻ᵀ∇ₓfᵀ = vec(A' \ mem.∇ₓf') # independent of (𝑗,𝑘)
    H(λ) = f(s+∇s*λ, p+λ) - F(s+∇s*λ, p+λ)' * A⁻ᵀ∇ₓfᵀ
    return ForwardDiff.hessian(H, zeros(m))
end

"""
    initialize_mem(x, p)

Initializes the memory cache for the F-1 method.
"""
function initialize_mem(F, ∇ₓf, x, p, alg; options...)
    x = copy(x)
    p = copy(p)
    psol = copy(p)
    prob = SteadyStateProblem(F, x, p)
    s = solve(prob, alg; options...).u
    A = factorize(F.jac(s, p))
    ∇ₚF = ForwardDiff.jacobian(p -> F(s, p), p)
    return Mem(s, A, A \ -∇ₚF, ∇ₓf(s, p), p, psol)
end

end


text:
#CppSimData base types & core functions
#-------------------------------------------------------------------------------

#==Main types
===============================================================================#
mutable struct DataReader
	filename::String
	msgbuf::Vector{UInt8}
	namebuf::Vector{UInt8}
	nsig::Int
	npts::Int
end
function DataReader(filename)
	result = DataReader(filename, Array{UInt8}(undef, 1000), Array{UInt8}(undef, 1000), 0, 0)
	result.msgbuf[1] = 0
	result.namebuf[1] = 0
	return result
end

#==Helper types
===============================================================================#
mutable struct CSTORAGE_INFO
   filename::Cstring
   num_sigs::Cint
   num_samples::Cint
end
#Danger: make sure filename does not risk garbage collection
#Solution: Initialized from DataReader (presuming it is referenced during duration of ccall)
CSTORAGE_INFO(r::DataReader) = CSTORAGE_INFO(pointer(r.filename), r.nsig, r.npts)

initialize() = ccall((:initialize, objfile), Cint, ())

function loadsig(filename::String)
	reader = DataReader(filename)
	info = CSTORAGE_INFO(reader)
	if ccall((:loadsig, objfile), Cint, (Ptr{CSTORAGE_INFO}, Ptr{UInt8}),
		pointer_from_objref(info), reader.msgbuf
	) != 0
		msg = unsafe_string(pointer(reader.msgbuf))
		error(msg)
	end
	reader.nsig = info.num_sigs
	reader.npts = info.num_samples
	return reader
end

function lssig(r::DataReader)
	result = String[]
	err = ccall((:reset_cur_sig_count, objfile), Cint, ())
	info = CSTORAGE_INFO(r)
	for i in 1:r.nsig
		if ccall((:lssig, objfile), Cint, (Ptr{CSTORAGE_INFO}, Ptr{UInt8}, Ptr{UInt8}),
			pointer_from_objref(info), r.namebuf, r.msgbuf,
		) != 0
			msg = unsafe_string(pointer(r.msgbuf))
			error(msg)
		end
		signame = unsafe_string(pointer(r.namebuf))
		push!(result, signame)
	end
	return result
end

function evalsig(r::DataReader, signame::String)
	result = Array{Cdouble}(undef, r.npts)
	info = CSTORAGE_INFO(r)
	if ccall((:evalsig, objfile), Cint, (Ptr{CSTORAGE_INFO}, Cstring, Ptr{Cdouble}, Ptr{UInt8}),
		pointer_from_objref(info), signame, result, r.msgbuf,
	) != 0
		msg = unsafe_string(pointer(r.msgbuf))
		error(msg)
	end
	return result
end

#Last line


text:
using BatchOLS
using Base.Test
import ForwardDiff: Dual
using Distributions

rand_Dual(M) = Dual(randn(M+1)...)

rand_Dual(N, M) = [rand_Dual(M) for _ in 1:N]

rand_yX(N, K) = randn(N), randn(N, K)

rand_yX_Dual(N, K, M) = rand_Dual(N, M), randn(N, K)

function alt_ML_βv(y, X)
    β = X \ y
    e = y-X*β
    β, dot(e,e)/size(X, 1)
end

alt_loglikelihood(y, X, β, v) = sum(logpdf.(Normal(0, √v), y.-(X*β)))

@testset "real ML" begin
    for _ in 1:100
        K = rand(2:5)
        N = rand(50:100) + K
        y, X = rand_yX(N, K)
        rhs = BatchOLS.RHS(X)
        β, v = BatchOLS.ML_βv(y, rhs)
        β′, v′ = alt_ML_βv(y, X)
        @test β ≈ β′
        @test v ≈ v
        @inferred BatchOLS.ML_βv(y, rhs)
        @test size(rhs) == size(X)
        @test size(rhs, 1) == size(X, 1)
        @test size(rhs, 2) == size(X, 2)
        @test rhs * β == X * β
    end
end

@testset "dual ML" begin
    for _ in 1:100
        K = rand(2:5)
        N = rand(50:100) + K
        y, X = rand_yX_Dual(N, K, rand(1:3))
        rhs = BatchOLS.RHS(X)
        β, v = BatchOLS.ML_βv(y, rhs)
        β′, v′ = alt_ML_βv(y, X)
        @test β ≈ β′
        @test v ≈ v
        @inferred BatchOLS.ML_βv(y, rhs)
    end
end

@testset "real loglikelihood" begin
    for _ in 1:100
        K = rand(2:5)
        N = rand(50:100) + K
        y, X = rand_yX(N, K)
        rhs = BatchOLS.RHS(X)
        β = randn(K)
        v = (randn()+0.1)^2
        β′ = β + randn(length(β))
        v′ = v + abs(randn()/10)
        ℓ = BatchOLS.loglikelihood(y, rhs, β′, v′)
        ℓ′ = alt_loglikelihood(y, X, β′, v′)
        @test ℓ ≈ ℓ′
        @inferred BatchOLS.loglikelihood(y, rhs, β′, v′)
    end
end


text:
"""
    _sreduceB!(A::AbstractMatrix{T},E::AbstractMatrix{T},B::AbstractMatrix{T},Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                fast = true, withQ = true) -> ρ

Reduce the `n x m` matrix `B` using an orthogonal or unitary similarity transformation `Q1` to the row
compressed form

     BT = Q1'*B = [ B11 ] ρ
                  [  0  ] n-ρ
                     m

where `B11` has full row rank `ρ`. `Q1'*A`, `Q1'*E` and `BT` are returned in `A`, `E` and `B`, respectively.
The performed orthogonal or unitary transformations are accumulated in `Q` if `withQ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `B`.
"""
function _sreduceB!(A::AbstractMatrix{T},E::AbstractMatrix{T},B::AbstractVecOrMat{T},
                    Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                    fast::Bool = true, withQ::Bool = true) where T <: BlasFloat
   n, m = typeof(B) <: AbstractVector ? (length(B),1) : size(B)
   (m == 0 || n == 0) && (return 0)
   mA, nA = size(A)
   ZERO = zero(T)
   if m == 1
      b = view(B,:,1)
      n == 1 && (abs(b[1]) > tol ? (return 1) : (b[1] = ZERO; return 0))
      τ, β = larfg!(b)
      if abs(β) <= tol
         b[:] = zeros(T,n)
         return 0
      else
         larf!('L', b, conj(τ), A)
         larf!('L', b, conj(τ), E)
         withQ && larf!('R', b, τ, Q)
         b[:] = [ fill(β,1); zeros(T,n-1)]
         return 1
      end
end
   if fast
      _, τ, jpvt = LinearAlgebra.LAPACK.geqp3!(B)
      ρ = count(x -> x > tol, abs.(diag(B)))
      T <: Complex ? tran = 'C' : tran = 'T'
      LinearAlgebra.LAPACK.ormqr!('L',tran,B,τ,A)
      LinearAlgebra.LAPACK.ormqr!('L',tran,B,τ,E)
      withQ && LinearAlgebra.LAPACK.ormqr!('R','N',B,τ,Q)
      B[:,:] = [ triu(B[1:ρ,:])[:,invperm(jpvt)]; zeros(T,n-ρ,m) ]
   else
      if n > m
         _, τ = LinearAlgebra.LAPACK.geqrf!(B)
         T <: Complex ? tran = 'C' : tran = 'T'
         LinearAlgebra.LAPACK.ormqr!('L',tran,B,τ,A)
         LinearAlgebra.LAPACK.ormqr!('L',tran,B,τ,E)
         withQ && LinearAlgebra.LAPACK.ormqr!('R','N',B,τ,Q)
         B[:,:] = [ triu(B[1:m,:]); zeros(T,n-m,m) ]
      end
      mn = min(n,m)
      ics = 1:mn
      jcs = 1:m
      SVD = svd(B[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == mn && (return ρ)
      B[ics,jcs] = [ Diagonal(SVD.S[1:ρ])*SVD.Vt[1:ρ,:]; zeros(T,mn-ρ,m) ]
      ρ == 0 && (return ρ)
      withQ && (Q[:,ics] = Q[:,ics]*SVD.U)
      A[ics,:] = SVD.U'*A[ics,:]
      E[ics,:] = SVD.U'*E[ics,:]
   end
   return ρ
end
"""
    _sreduceC!(A::AbstractMatrix{T},E::AbstractMatrix{T},C::AbstractMatrix{T},Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                fast = true, withZ = true) -> ρ

Reduce the `p x n` matrix `C` using an orthogonal or unitary similarity transformation `Z1` to the column
compressed form

     CT = C*Z1 = [ 0  C11 ] p
                  n-ρ  ρ

where `C11` has full column rank `ρ`. `A*Z1`, `E*Z1` and `CT` are returned in `A`, `E` and `C`, respectively.
The performed orthogonal or unitary transformations are accumulated in `Z` if `withZ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `C`.
"""
function _sreduceC!(A::AbstractMatrix{T},E::AbstractMatrix{T},C::AbstractMatrix{T},
                    Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                    fast::Bool = true, withZ::Bool = true) where T <: BlasFloat
   p, n = size(C)
   (p == 0 || n == 0) && (return 0)
   mA, nA = size(A)
   ZERO = zero(T)
   ia = 1:n
   if p == 1
      c = view(C,1,:)
      n == 1 && (abs(c[1]) > tol ? (return 1) : (c[1] = ZERO; return 0))
      τ, β = larfgl!(c)
      if abs(β) <= tol
         c[:] = zeros(T,n)
         return 0
      else
         T <: Complex && (c[:] = conj(c))
         τ = conj(τ)
         larf!('R', c, τ, A)
         larf!('R', c, τ, E)
         withZ && larf!('R', c, τ, Z)
         c[:] = [zeros(T,n-1); fill(β,1)];
         return 1
      end
end
   if fast
      # compute the RQ decomposition with row pivoting
      temp = reverse(copy(transpose(C)),dims=1)
      _, τ, jp = LinearAlgebra.LAPACK.geqp3!(temp)
      ρ = count(x -> x > tol, abs.(diag(temp)))
      np = min(n,p)
      for i = 1:np
         v = reverse(temp[i:n,i]); v[end] = 1;
         T <: Complex && (v = conj(v))
         it = 1:n-i+1
         larf!('R',v, conj(τ[i]), view(A,:,it))
         larf!('R',v, conj(τ[i]), view(E,:,it))
         withZ && larf!('R',v, conj(τ[i]), view(Z,:,it))
      end
      C[:,:] = [ zeros(T,p,n-ρ) reverse(transpose(triu(temp[1:ρ,:])),dims=2)[invperm(jp),:] ]
   else
      if n > p
         # compute the RQ decomposition
         _, τ = LinearAlgebra.LAPACK.gerqf!(C)
         T <: Complex ? tran = 'C' : tran = 'T'
         LinearAlgebra.LAPACK.ormrq!('R',tran,C,τ,A)
         LinearAlgebra.LAPACK.ormrq!('R',tran,C,τ,E)
         withZ && LinearAlgebra.LAPACK.ormrq!('R',tran,C,τ,Z)
         C[:,:] = [ zeros(T,p,n-p) triu(C[1:p,n-p+1:n]) ]
      end
      pn = min(n,p)
      ics = 1:p
      jcs = n-pn+1:n
      SVD = svd(C[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == pn && (return ρ)
      C[ics,jcs] = [ zeros(T,p,pn-ρ) reverse(SVD.U,dims=2)[:,p-ρ+1:end]*Diagonal(reverse(SVD.S[1:ρ])) ]
      ρ == 0 && (return ρ)
      Z1 = reverse(SVD.V,dims=2)
      withZ && (Z[:,jcs] = Z[:,jcs]*Z1)
      A[:,jcs] = A[:,jcs]*Z1
      E[:,jcs] = E[:,jcs]*Z1
   end
   return ρ
end
"""
    _sreduceBA!(n::Int,m::Int,A::AbstractMatrix{T},B::AbstractMatrix{T},C::Union{AbstractMatrix{T},Missing},Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                fast = true, init = true, roff = 0, coff = 0, withQ = true) -> ρ

Reduce for `init = true`, the pair `(A,B)` using an orthogonal or unitary similarity transformation on the matrices `A` and `B` of the form
`H =  Q1'*A*Q1`, `G = Q1'*B`, to the form

     G = [ B11 ]  H = [ A11 A12 ] ρ
         [ 0   ]        B2  A22 ] n-ρ
           m            ρ   n-ρ

where `B11` has full row rank `ρ`. `H`, `G` and `C*Q1` are returned in `A`, `B` and `C`, respectively.
The performed orthogonal or unitary transformations are accumulated in `Q` if `withQ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `B`.

Reduce for `init = false`, the matrix `A` of the form

    A = [ *   *  *  ] roff
        [ 0   B1 A1 ] n
         coff m  n

using an orthogonal or unitary similarity transformation on the submatrices A1 and B1 of the form
H1 =  Q1'*A1*Q1, G1 = Q1'*B1, to the form

                                     [ *   *    *   *   ] roff
     H = diag(I,Q1')*A*diag(I,Q1) =  [ *   B11  A11 A12 ] ρ
                                     [ 0   0    B2  A22 ] n-ρ
                                      coff m    ρ   n-ρ

where `B11` has full row rank `ρ`. `H` and `C*diag(I,Q1)` are returned in `A` and `C`, respectively,
and `B` is unchanged. The performed orthogonal or unitary transformations are accumulated in `Q` if `withQ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `A`.
"""
function _sreduceBA!(n::Int,m::Int,A::AbstractMatrix{T},B::AbstractVecOrMat{T},C::Union{AbstractMatrix{T},Missing},
                     Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                     fast::Bool = true, init::Bool = true, roff::Int = 0, coff::Int = 0, withQ::Bool = true) where T <: BlasFloat
   (m == 0 || n == 0) && (return 0)
   mA, nA = size(A)
   ZERO = zero(T)
   ib = roff+1:roff+n
   ia = 1:roff+n
   if init
      # coff and roff must be zero
      (coff == 0 && roff == 0) || error("coff and roff must be zero at first call")
      ja = 1:n
      B1 = view(B,ib,1:m)
      A1 = view(A,ib,ja)
   else
      (coff+m+n == nA && roff+n == mA) || error("coff and roff must have compatible values with the dimensions of A")
      ja = coff+m+1:coff+m+n
      jb = coff+1:coff+m
      B1 = view(A,ib,jb)
      A1 = view(A,ib,ja)
   end
   if m == 1
      b = view(B1,:,1)
      n == 1 && (abs(b[1]) > tol ? (return 1) : (b[1] = ZERO; return 0))
      τ, β = larfg!(b)
      if abs(β) <= tol
         b[:] = zeros(T,n)
         return 0
      else
         #T <: Complex && (b[:] = conj(b))
         larf!('L', b, conj(τ), A1)
         larf!('R', b, τ, view(A, ia, ja))
         ismissing(C) || larf!('R', b, τ, view(C,:,ja))
         withQ && larf!('R', b, τ, view(Q,:,ib))
         b[:] = [ fill(β,1); zeros(T,n-1)]
         return 1
      end
   end
   if fast
      B1, τ, jpvt = LinearAlgebra.LAPACK.geqp3!(B1)
      ρ = count(x -> x > tol, abs.(diag(B1)))
      T <: Complex ? tran = 'C' : tran = 'T'
      LinearAlgebra.LAPACK.ormqr!('L',tran,B1,τ,A1)
      LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(A,ia,ja))
      withQ && LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(Q,:,ib))
      ismissing(C) || LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(C,:,ja))
      B1[:,:] = [ triu(B1[1:ρ,:])[:,invperm(jpvt)]; zeros(T,n-ρ,m) ]
   else
      if n > m
         B1, τ = LinearAlgebra.LAPACK.geqrf!(B1)
         T <: Complex ? tran = 'C' : tran = 'T'
         LinearAlgebra.LAPACK.ormqr!('L',tran,B1,τ,A1)
         LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(A,ia,ja))
         withQ && LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(Q,:,ib))
         ismissing(C) || LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(C,:,ja))
         B1[:,:] = [ triu(B1[1:m,:]); zeros(T,n-m,m) ]
      end
      mn = min(n,m)
      ics = 1:mn
      jcs = 1:m
      SVD = svd(B1[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == mn && (return ρ)
      B1[ics,jcs] = [ Diagonal(SVD.S[1:ρ])*SVD.Vt[1:ρ,:]; zeros(T,mn-ρ,m) ]
      ρ == 0 && (return ρ)
      ibt = roff+1:roff+mn
      init ? (jt = coff+1:coff+mn) : (jt = coff+m+1:coff+m+mn)
      withQ && (Q[:,ibt] = Q[:,ibt]*SVD.U)
      A[ibt,ja] = SVD.U'*A[ibt,ja]
      A[ia,jt] = A[ia,jt]*SVD.U
      ismissing(C) || (C[:,jt] = C[:,jt]*SVD.U)
   end
   return ρ
end
"""
    _sreduceBA2!(n::Int,m::Int,A::AbstractMatrix{T},B::AbstractMatrix{T},C::Union{AbstractMatrix{T},Missing},
                 Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                 fast = true, coff = 0, withQ = true) -> ρ

Reduce the matrices `B` and `A` of the form

    B = [ *  *  ]       A = [ *    *   A12 ]
        [ B1 B2 ] n         [ *   A21  A22 ] n
          m  k               coff       n

using an orthogonal or unitary transformation of the form `B <- G = diag(I,Q1')*B`,
such that `G` has the form

         [  *   *  ]
     G = [ B11 B12 ] ρ      ,
         [  0  B22 ] n-ρ
            m  k

where `B11` has full row rank `ρ` and the blocks `A12`, `A21` and `A22` of `A` are
updated such that `A12 <- A12*Q1`, `A21 <- Q1'*A21` and `A22 <- Q1'*A22`.
Update  `C <- C*diag(I,Q1)` and `Q <- Q*diag(I,Q1)` if `withQ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `B`.
"""
function _sreduceBA2!(n::Int,m::Int,A::AbstractMatrix{T},B::AbstractMatrix{T},C::Union{AbstractMatrix{T},Missing},
                     Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                     fast::Bool = true, coff::Int = 0, withQ::Bool = true) where T <: BlasFloat
   (m == 0 || n == 0) && (return 0)
   nA, mB = size(B)
   roff = nA - n
   ZERO = zero(T)
   ib = roff+1:nA    # row range of [B1, B2] and [A21 A22]
   ia = 1:nA         # row range of [A12; A22]
   ja = ib           # column rabge of A2 = [A12; A22]
   ja1 = coff+1:nA   # column range of A1 = [A21 A22]
   B1 = view(B,ib,1:m)
   B2mat = (m < mB)  # B2 is present if B has more columns than m
   B2mat && (B2 = view(B,ib,m+1:mB))
   A1 = view(A,ib,ja1)
   A2 = view(A,ia,ja)
   if m == 1
      b = view(B1,:,1)
      n == 1 && (abs(b[1]) > tol ? (return 1) : (b[1] = ZERO; return 0))
      τ, β = larfg!(b)
      if abs(β) <= tol
         b[:] = zeros(T,n)
         return 0
      else
         larf!('L', b, conj(τ), A1)
         B2mat &&  larf!('L', b, conj(τ), B2)
         larf!('R', b, τ, A2)
         ismissing(C) || larf!('R', b, τ, view(C,:,ja))
         withQ && larf!('R', b, τ, view(Q,:,ib))
         b[:] = [ fill(β,1); zeros(T,n-1)]
         return 1
      end
   end
   if fast
      B1, τ, jpvt = LinearAlgebra.LAPACK.geqp3!(B1)
      ρ = count(x -> x > tol, abs.(diag(B1)))
      T <: Complex ? tran = 'C' : tran = 'T'
      LinearAlgebra.LAPACK.ormqr!('L',tran,B1,τ,A1)
      B2mat &&  LinearAlgebra.LAPACK.ormqr!('L', tran, B1, τ, B2)
      LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,A2)
      withQ && LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(Q,:,ib))
      ismissing(C) || LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(C,:,ja))
      B1[:,:] = [ triu(B1[1:ρ,:])[:,invperm(jpvt)]; zeros(T,n-ρ,m) ]
   else
      if n > m
         B1, τ = LinearAlgebra.LAPACK.geqrf!(B1)
         T <: Complex ? tran = 'C' : tran = 'T'
         LinearAlgebra.LAPACK.ormqr!('L',tran,B1,τ,A1)
         B2mat && LinearAlgebra.LAPACK.ormqr!('L',tran,B1,τ,B2)
         LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,A2)
         withQ && LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(Q,:,ib))
         ismissing(C) || LinearAlgebra.LAPACK.ormqr!('R','N',B1,τ,view(C,:,ja))
         B1[:,:] = [ triu(B1[1:m,:]); zeros(T,n-m,m) ]
      end
      mn = min(n,m)
      ics = 1:mn
      jcs = 1:m
      SVD = svd(B1[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == mn && (return ρ)
      B1[ics,jcs] = [ Diagonal(SVD.S[1:ρ])*SVD.Vt[1:ρ,:]; zeros(T,mn-ρ,m) ]
      ρ == 0 && (return ρ)
      ibt = roff+1:roff+mn
      withQ && (Q[:,ibt] = Q[:,ibt]*SVD.U)
      A[ibt,ja1] = SVD.U'*A[ibt,ja1]
      B2mat &&  (B2[ics,:] = SVD.U'*B2[ics,:])
      #jt = coff+1:coff+mn
      #A[ia,jt] = A[ia,jt]*SVD.U
      A[ia,ibt] = A[ia,ibt]*SVD.U
      #ismissing(C) || (C[:,jt] = C[:,jt]*SVD.U)
      ismissing(C) || (C[:,ibt] = C[:,ibt]*SVD.U)
   end
   return ρ
end
"""
    _sreduceAC!(n::Int,p::Int,A::AbstractMatrix{T},C::AbstractMatrix{T},B::Union{AbstractMatrix{T},Missing},
                Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                fast = true, init = true, rtrail = 0, ctrail = 0, withQ = true) -> ρ

Reduce for `init = true`, the pair `(A,C)` using an orthogonal or unitary similarity transformation on the matrices `A` and `C` of the form
`H =  Q1'*A*Q1`, `L = C*Q1`, to the form

     H = [ A11 A12 ] n-ρ    L = [ 0  C11 ] p
         [ C2  A22 ] ρ           n-ρ  ρ
           n-ρ  ρ

where `C11` has full column rank `ρ`. `H`, `L` and `Q1'*B` are returned in `A`, `C` and `B`, respectively.
The performed orthogonal or unitary transformations are accumulated in `Q` if `withQ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `C`.

Reduce for `init = false`, the matrix `A` of the form

        [ A1  *   ] n
    A = [ C1  *   ] p
        [ 0   *   ] rtrail
          n ctrail

using an orthogonal or unitary similarity transformation on the submatrices A1 and C1 of the form
H1 =  Q1'*A1*Q1, L1 = C1*Q1, to the form

                                     [ A11 A12  *   ] n-ρ
                                     [ C2  A22  *   ] ρ
     H = diag(Q1',I)*A*diag(Q1,I) =  [ 0   C11  *   ] p
                                     [ 0   0    *   ] rtrail
                                      n-ρ  ρ  ctrail

where `C11` has full column rank `ρ`. `H` and `diag(Q1',I)*B` are returned in `A` and `B`, respectively,
and `C` is unchanged. The performed orthogonal or unitary transformations are accumulated in `Q` if `withQ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `A`.
"""
function _sreduceAC!(n::Int,p::Int,A::AbstractMatrix{T},C::AbstractMatrix{T},B::Union{AbstractVecOrMat{T},Missing},
                     Q::Union{AbstractMatrix{T},Nothing}, tol::Real;
                     fast::Bool = true, init::Bool = true, rtrail::Int = 0, ctrail::Int = 0, withQ::Bool = true) where T <: BlasFloat
   (p == 0 || n == 0) && (return 0)
   mA, nA = size(A)
   ZERO = zero(T)
   ia = 1:n
   if init
      # coff and roff must be zero
      (ctrail == 0 && rtrail == 0) || error("rtrail and ctrail must be zero at first call")
      ic = 1:p
      C1 = view(C,ic,ia)
      A1 = view(A,ia,ia)
   else
      (ctrail+n == nA && rtrail+n+p == mA) || error("rtrail and ctrail must have compatible values with the dimensions of A")
      ic = n+1:n+p
      C1 = view(A,ic,ia)
      A1 = view(A,ia,ia)
   end
   if p == 1
      c = view(C1,1,ia)
      n == 1 && (abs(c[1]) > tol ? (return 1) : (c[1] = ZERO; return 0))
      τ, β = larfgl!(c)
      if abs(β) <= tol
         c[:] = zeros(T,n)
         return 0
      else
         T <: Complex && (c[:] = conj(c))
         larf!('L', c, τ, view(A, ia, :))
         ismissing(B) || larf!('L', c, τ, view(B, ia, :))
         τ = conj(τ)
         larf!('R', c, τ, A1)
         withQ && larf!('R', c, τ, view(Q,:,ia))
         c[:] = [zeros(T,n-1); fill(β,1)];
         return 1
      end
   end
   if fast
      # compute the RQ decomposition with row pivoting
      temp = reverse(copy(transpose(C1)),dims=1)
      temp, τ, jp = LinearAlgebra.LAPACK.geqp3!(temp)
      ρ = count(x -> x > tol, abs.(diag(temp)))
      np = min(n,p)
      for i = 1:np
         v = reverse(temp[i:n,i]); v[end] = 1;
         T <: Complex && (v = conj(v))
         it = 1:n-i+1
         larf!('L',v, τ[i], view(A,it,:))
         larf!('R',v, conj(τ[i]), view(A,:,it))
         ismissing(B) || larf!('L',v, τ[i], view(B,it,:))
         withQ && larf!('R',v, conj(τ[i]), view(Q,:,it))
      end
      C1[:,:] = [ zeros(T,p,n-ρ) reverse(transpose(triu(temp[1:ρ,:])),dims=2)[invperm(jp),:] ]
   else
      if n > p
         # compute the RQ decomposition
         C1, τ = LinearAlgebra.LAPACK.gerqf!(C1)
         T <: Complex ? tran = 'C' : tran = 'T'
         LinearAlgebra.LAPACK.ormrq!('R',tran,C1,τ,A1)
         LinearAlgebra.LAPACK.ormrq!('L','N',C1,τ,view(A,ia,:))
         withQ && LinearAlgebra.LAPACK.ormrq!('R',tran,C1,τ,view(Q,:,ia))
         ismissing(B) || LinearAlgebra.LAPACK.ormrq!('L','N',C1,τ,view(B,ia,:))
         C1[:,:] = [ zeros(T,p,n-p) triu(C1[1:p,n-p+1:n]) ]
      end
      pn = min(n,p)
      ics = 1:p
      jcs = n-pn+1:n
      SVD = svd(C1[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == pn && (return ρ)
      C1[ics,jcs] = [ zeros(T,p,pn-ρ) reverse(SVD.U,dims=2)[:,p-ρ+1:end]*Diagonal(reverse(SVD.S[1:ρ])) ]
      ρ == 0 && (return ρ)
      Q1 = reverse(SVD.V,dims=2)
      withQ && (Q[:,jcs] = Q[:,jcs]*Q1)
      A[jcs,:] = Q1'*A[jcs,:]
      A[ia,jcs] = A[ia,jcs]*Q1
      ismissing(B) || (B[jcs,:] = Q1'*B[jcs,:])
   end
   return ρ
end
"""
    _sreduceBAE!(n::Int,m::Int,A::AbstractMatrix{T},E::AbstractMatrix{T},B::AbstractMatrix{T},C::Union{AbstractMatrix{T},Missing},
                 Q::Union{AbstractMatrix{T},Nothing}, Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                 fast = true, init = true, roff = 0, coff = 0, withQ = true, withZ = true)

Reduce for `init = true`, the pair `(A-λE,B)`, with `E` upper-triangular, using an orthogonal or unitary
similarity transformations on the matrices `A`, `E` and `B` of the form `At =  Q1'*A*Z1`, `Et =  Q1'*E*Z1`,
`Bt = Q1'*B`, to the form

     Bt = [ B11 ]  At = [ A11 A12 ] ρ     Et = [ E11 E12 ] ρ
          [ 0   ]         B2  A22 ] n-ρ        [  0  E22 ] n-ρ
            m             ρ   n-ρ                 ρ   n-ρ

where `B11` has full row rank `ρ` and `Et` is upper-triangular. `Bt`, `At`, `Et` and `C*Z1` are returned in
`B`, `A`, `E` and `C`, respectively.
The performed orthogonal or unitary transformations are accumulated in `Q` as `Q <- Q*Q1` if `withQ = true`
and in `Z` as `Z <- Z*Z1` if `withZ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `B`.

Reduce for `init = false`, the matrices `A` and `E` of the form

    A = [ *   *  *  ] roff     E = [ *   *  *  ] roff
        [ 0   B1 A1 ] n            [ 0   0  E1 ] n
         coff m  n                  coff m  n

with `E1` upper triangular, using an orthogonal or unitary similarity transformations on the submatrices
`A1`, `E1` and `B1` of the form `At1 =  Q1'*A1*Z1`, `Et1 =  Q1'*E1*Z1`, `Bt1 = Q1'*B1`, to the form

                                      [ *   *    *   *   ] roff
     At = diag(I,Q1')*A*diag(I,Z1) =  [ 0   B11  A11 A12 ] ρ
                                      [ 0   0    B2  A22 ] n-ρ
                                       coff m    ρ   n-ρ

                                      [ *   *    *   *   ] roff
     Et = diag(I,Q1')*A*diag(I,Z1) =  [ 0   0    E11 E12 ] ρ
                                      [ 0   0    0   E22 ] n-ρ
                                       coff m    ρ   n-ρ

where `B11` has full row rank `ρ`, and `E11` and `E22` are upper triangular. `At`, `Et` and `C*diag(I,Z1)`
are returned in `A`, `E` and `C`, respectively, and `B` is unchanged.
The performed orthogonal or unitary transformations are accumulated in `Q` as `Q <- Q*diag(I,Q1)` if `withQ = true`
and in `Z` as `Z <- Z*diag(I,Z1)` if `withZ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `A`.
"""
function _sreduceBAE!(n::Int,m::Int,A::AbstractMatrix{T},E::AbstractMatrix{T},B::AbstractVecOrMat{T},C::Union{AbstractMatrix{T},Missing},
                      Q::Union{AbstractMatrix{T},Nothing}, Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                      fast::Bool = true, init::Bool = true, roff::Int = 0, coff::Int = 0,
                      withQ::Bool = true, withZ::Bool = true) where T <: BlasFloat
   (m == 0 || n == 0) && (return 0)
   mA, nA = size(A)
   ZERO = zero(T)
   ib = roff+1:roff+n
   ia = 1:roff+n
   if init
      # coff and roff must be zero
      (coff == 0 && roff == 0) || error("coff and roff must be zero at first call")
      ja = 1:n
      B1 = view(B,ib,1:m)
      A1 = view(A,ib,ja)
      E1 = view(E,ib,ja)
   else
      (coff+m+n == nA && roff+n == mA) || error("coff and roff must have compatible values with the dimensions of A")
      ja = coff+m+1:coff+m+n
      B1 = view(A,ib,coff+1:coff+m)
      A1 = view(A,ib,ja)
      E1 = view(E,ib,ja)
   end
   if fast
      ρ = 0
      nrm = similar(real(A),m)
      jp = Vector(1:m)
      nm = min(n,m)
      for j = 1:nm
         for l = j:m
            nrm[l] = norm(B1[j:n,l])
         end
         nrmax, ind = findmax(nrm[j:m])
         ind += j-1
         if nrmax <= tol
            break
         else
            ρ += 1
         end
         if ind != j
            (jp[j], jp[ind]) = (jp[ind], jp[j])
            (B1[:,j],B1[:,ind]) = (B1[:,ind],B1[:,j])
         end
         for ii = n:-1:j+1
             iim1 = ii-1
             if B1[ii,j] != ZERO
                G, B1[iim1,j] = givens(B1[iim1,j],B1[ii,j],iim1,ii)
                B1[ii,j] = ZERO
                lmul!(G,view(B1,:,j+1:m))
                lmul!(G,A1)
                lmul!(G,view(E1,:,iim1:n))
                withQ && rmul!(view(Q,:,ib),G')
                G, r = givens(conj(E1[ii,ii]),conj(E1[ii,iim1]),ii,iim1)
                E1[ii,ii] = conj(r)
                E1[ii,iim1] = ZERO
                rmul!(view(E,1:roff+iim1,ja),G')
                withZ && rmul!(view(Z,:,ja),G')
                rmul!(view(A,:,ja),G')
                ismissing(C) || rmul!(view(C,:,ja),G')
             end
         end
      end
      B1[:,:] = [ B1[1:ρ,invperm(jp)]; zeros(T,n-ρ,m) ]
      return ρ
   else
      if n > m
         for j = 1:m
            for ii = n:-1:j+1
               iim1 = ii-1
               if B1[ii,j] != ZERO
                  G, B1[iim1,j] = givens(B1[iim1,j],B1[ii,j],iim1,ii)
                  B1[ii,j] = ZERO
                  lmul!(G,view(B1,:,j+1:m))
                  lmul!(G,A1)
                  lmul!(G,view(E1,:,iim1:n))
                  withQ && rmul!(view(Q,:,ib),G')
                  G, r = givens(conj(E1[ii,ii]),conj(E1[ii,iim1]),ii,iim1)
                  E1[ii,ii] = conj(r)
                  E1[ii,iim1] = ZERO
                  rmul!(view(E,1:roff+iim1,ja),G')
                  withZ && rmul!(view(Z,:,ja),G')
                  rmul!(view(A,:,ja),G')
                  ismissing(C) || rmul!(view(C,:,ja),G')
               end
            end
         end
      end
      mn = min(n,m)
      mn == 0 && (return 0)
      ics = 1:mn
      jcs = 1:m
      SVD = svd(B1[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == mn && (return ρ)
      B1[ics,jcs] = [ Diagonal(SVD.S[1:ρ])*SVD.Vt[1:ρ,:]; zeros(T,mn-ρ,m) ]
      ρ == 0 && (return ρ)
      ibt = roff+1:roff+mn
      withQ && (Q[:,ibt] = Q[:,ibt]*SVD.U)
      E[ibt,ja] = SVD.U'*E[ibt,ja]
      A[ibt,ja] = SVD.U'*A[ibt,ja]
      tau = similar(E,mn)
      init ? (jt1 = 1:mn) : (jt1 = coff+m+1:coff+m+mn)
      E11 = view(E,ibt,jt1)
      LinearAlgebra.LAPACK.gerqf!(E11,tau)
      T <: Complex ? tran = 'C' : tran = 'T'
      LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(A,:,jt1))
      withZ && LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(Z,:,jt1))
      LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(E,1:roff,jt1))
      ismissing(C) || LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(C,:,jt1))
      triu!(E11)
   end
   return ρ
end
"""
    _sreduceBAE2!(n::Int,m::Int,A::AbstractMatrix{T},E::AbstractMatrix{T},B::AbstractMatrix{T},
                  C::Union{AbstractMatrix{T},Missing}, Q::Union{AbstractMatrix{T},Nothing},
                  Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                  fast = true, coff = 0, withQ = true, withZ = true) -> ρ

Reduce the matrices `B`, `A` and the upper-triangular `E` of the form

    B = [ *  *  ]       A - λE = [ *    *        A12-λE12 ]
        [ B1 B2 ] n              [ *   A21-λE21  A22-λE22 ] n
          m  k               coff       n

using orthogonal or unitary transformations `Q1` and `Z1` such that `B <- G = diag(I,Q1')*B`,
`G` has the form

         [  *   *  ]
     G = [ B11 B12 ] ρ      ,
         [  0  B22 ] n-ρ
            m  k

where `B11` has full row rank `ρ`, and the blocks `A12-λE12`, `A21-λE21` and `A22-λE22`
of `A-λE` are updated such that `A12-λE12 <- (A12-λE12)*Z1`, `A21-λE21 <- Q1'*(A21-λE21)`
and `A22-λE22 <- Q1'*(A22-λE22)`.
Update  `C <- C*diag(I,Q1)`, `Q <- Q*diag(I,Q1)` if `withQ = true` and
`Z <- Z*diag(I,Z1)` if `withZ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `B`.
"""
function _sreduceBAE2!(n::Int,m::Int,A::AbstractMatrix{T},E::AbstractMatrix{T},B::AbstractMatrix{T},C::Union{AbstractMatrix{T},Missing},
                      Q::Union{AbstractMatrix{T},Nothing}, Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                      fast::Bool = true, coff::Int = 0, withQ::Bool = true, withZ::Bool = true) where T <: BlasFloat
   (m == 0 || n == 0) && (return 0)
   nA, mB = size(B)
   roff = nA - n
   ZERO = zero(T)
   ib = roff+1:nA    # row range of [B1, B2] and [A21 A22]
   ia = 1:nA         # row range of [A12; A22]
   ja = ib           # column rabge of A2 = [A12; A22]
   ja1 = coff+1:nA   # column range of A1 = [A21 A22]
   B1 = view(B,ib,1:m)
   B2mat = (m < mB)  # B2 is present if B has more columns than m
   B2mat && (B2 = view(B,ib,m+1:mB))
   A1 = view(A,ib,ja1)
   #E1 = view(E,ib,ja1)
   E1 = view(E,ib,ib)
   A2 = view(A,ia,ja)
   E2 = view(E,ia,ja)
   if fast
      ρ = 0
      nrm = similar(real(A),m)
      jp = Vector(1:m)
      nm = min(n,m)
      for j = 1:nm
         for l = j:m
            nrm[l] = norm(B1[j:n,l])
         end
         nrmax, ind = findmax(nrm[j:m])
         ind += j-1
         if nrmax <= tol
            break
         else
            ρ += 1
         end
         if ind != j
            (jp[j], jp[ind]) = (jp[ind], jp[j])
            (B1[:,j],B1[:,ind]) = (B1[:,ind],B1[:,j])
         end
         for ii = n:-1:j+1
             iim1 = ii-1
             if B1[ii,j] != ZERO
                G, B1[iim1,j] = givens(B1[iim1,j],B1[ii,j],iim1,ii)
                B1[ii,j] = ZERO
                lmul!(G,view(B1,:,j+1:m))
                lmul!(G,A1)
                B2mat && lmul!(G,B2)
                lmul!(G,view(E,ib,roff+iim1:nA))
                withQ && rmul!(view(Q,:,ib),G')
                G, r = givens(conj(E1[ii,ii]),conj(E1[ii,iim1]),ii,iim1)
                E1[ii,ii] = conj(r)
                E1[ii,iim1] = ZERO
                rmul!(view(E,1:roff+iim1,ja),G')
                withZ && rmul!(view(Z,:,ja),G')
                rmul!(view(A,:,ja),G')
                ismissing(C) || rmul!(view(C,:,ja),G')
             end
         end
      end
      B1[:,:] = [ B1[1:ρ,invperm(jp)]; zeros(T,n-ρ,m) ]
      return ρ
   else
      if n > m
         for j = 1:m
            for ii = n:-1:j+1
               iim1 = ii-1
               if B1[ii,j] != ZERO
                  G, B1[iim1,j] = givens(B1[iim1,j],B1[ii,j],iim1,ii)
                  B1[ii,j] = ZERO
                  lmul!(G,view(B1,:,j+1:m))
                  lmul!(G,A1)
                  B2mat && lmul!(G,B2)
                  lmul!(G,view(E,ib,roff+iim1:nA))
                  withQ && rmul!(view(Q,:,ib),G')
                  G, r = givens(conj(E1[ii,ii]),conj(E1[ii,iim1]),ii,iim1)
                  E1[ii,ii] = conj(r)
                  E1[ii,iim1] = ZERO
                  rmul!(view(E,1:roff+iim1,ja),G')
                  withZ && rmul!(view(Z,:,ja),G')
                  rmul!(view(A,:,ja),G')
                  ismissing(C) || rmul!(view(C,:,ja),G')
               end
            end
         end
      end
      mn = min(n,m)
      mn == 0 && (return 0)
      ics = 1:mn
      jcs = 1:m
      SVD = svd(B1[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == mn && (return ρ)
      B1[ics,jcs] = [ Diagonal(SVD.S[1:ρ])*SVD.Vt[1:ρ,:]; zeros(T,mn-ρ,m) ]
      ρ == 0 && (return ρ)
      ibt = roff+1:roff+mn
      withQ && (Q[:,ibt] = Q[:,ibt]*SVD.U)
      E[ibt,ja1] = SVD.U'*view(E,ibt,ja1)
      A[ibt,ja1] = SVD.U'*view(A,ibt,ja1)
      B2mat &&  (B2[ibt,:] = SVD.U'*B2[ibt,:])
      tau = similar(E,mn)
      jt1 = coff+1:coff+mn
      E11 = view(E,ibt,jt1)
      LinearAlgebra.LAPACK.gerqf!(E11,tau)
      T <: Complex ? tran = 'C' : tran = 'T'
      LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(A,:,jt1))
      withZ && LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(Z,:,jt1))
      LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(E,1:roff,jt1))
      ismissing(C) || LinearAlgebra.LAPACK.ormrq!('R',tran,E11,tau,view(C,:,jt1))
      triu!(E11)
   end
   return ρ
end
"""
    _sreduceAEC!(n::Int,p::Int,A::AbstractMatrix{T},E::AbstractMatrix{T},C::AbstractMatrix{T},B::Union{AbstractMatrix{T},Missing},
                Q::Union{AbstractMatrix{T},Nothing}, Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                fast = true, init = true, rtrail = 0, ctrail = 0, withQ = true, withZ = true) -> ρ

Reduce for `init = true`, the pair `(A-λE,C)`, with E upper-triangular, using an orthogonal or unitary
similarity transformations on the matrices `A`, `E` and `C` of the form `At =  Q1'*A*Z1`, `Et =  Q1'*E*Z1`,
`Ct = C*Z1`, to the form

     Ct = [ 0  C11 ] p   At = [ A11 A12 ] n-ρ   Et = [ E11 E12 ] n-ρ
           n-ρ  ρ             [ C2  A22 ] ρ          [  0  E22 ] ρ
                                n-ρ  ρ                 n-ρ  ρ

where `C11` has full column rank `ρ` and `Et` is upper-triangular. `Ct`, `At`, `Et` and `Q1'*B` are returned in
`C`, `A`, `E` and `B`, respectively.
The performed orthogonal or unitary transformations are accumulated in `Q` as `Q <- Q*Q1` if `withQ = true`
and in `Z` as `Z <- Z*Z1` if `withZ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `C`.

Reduce for `init = false`, the matrices `A` and `E` of the form

        [ A1  *   ] n               [ E1  *   ] n
    A = [ C1  *   ] p          E =  [ 0   *   ] p
        [ 0   *   ] rtrail          [ 0   *   ] rtrail
          n ctrail

with `E1` upper triangular, using an orthogonal or unitary similarity transformations on the submatrices
`A1`, `E1` and `C1` of the form `At1 =  Q1'*A1*Z1`, `Et1 =  Q1'*E1*Z1`, `Ct1 = C1*Z1`, to the form

                                      [ A11 A12  *   ] n-ρ
                                      [ C2  A22  *   ] ρ
     At = diag(Q1',I)*A*diag(Q1,I) =  [ 0   C11  *   ] p
                                      [ 0   0    *   ] rtrail
                                       n-ρ  ρ  ctrail

                                      [ E11 E12  *   ] n-ρ
                                      [ 0   E22  *   ] ρ
     Et = diag(Q1',I)*A*diag(Q1,I) =  [ 0   0    *   ] p
                                      [ 0   0    *   ] rtrail
                                       n-ρ  ρ  ctrail

where `C11` has full column rank `ρ`, and `E11` and `E22` are upper triangular. `At`, `Et` and `diag(I,Q1')*B`
are returned in `A`, `E` and `B`, respectively, and `C` is unchanged.
The performed orthogonal or unitary transformations are accumulated in `Q` as `Q <- Q*diag(I,Q1)` if `withQ = true`
and in `Z` as `Z <- Z*diag(I,Z1)` if `withZ = true`.
The rank decisions use the absolute tolerance `tol` for the nonzero elements of `A`.
"""
function _sreduceAEC!(n::Int,p::Int,A::AbstractMatrix{T},E::AbstractMatrix{T},C::AbstractMatrix{T},B::Union{AbstractVecOrMat{T},Missing},
                      Q::Union{AbstractMatrix{T},Nothing}, Z::Union{AbstractMatrix{T},Nothing}, tol::Real;
                      fast::Bool = true, init::Bool = true, rtrail::Int = 0, ctrail::Int = 0,
                      withQ::Bool = true, withZ::Bool = true) where T <: BlasFloat
   (p == 0 || n == 0) && (return 0)
   mA, nA = size(A)
   ZERO = zero(T)
   ia = 1:n
   ja = 1:nA
   A1 = view(A,ia,ia)
   E1 = view(E,ia,ia)
   if init
      # coff and roff must be zero
      (ctrail == 0 && rtrail == 0) || error("rtrail and ctrail must be zero at first call")
      C1 = view(C,1:p,ia)
   else
      (ctrail+n == nA && rtrail+n+p == mA) || error("rtrail and ctrail must have compatible values with the dimensions of A")
      C1 = view(A,n+1:n+p,ia)
   end
   if fast
      ρ = 0
      nrm = similar(real(A),p)
      jp = Vector(1:p)
      np = min(n,p)
      for i = 1:np
         ii = p-i+1
         for l = 1:ii
            nrm[l] = norm(C1[l,1:n-i+1])
         end
         nrmax, ind = findmax(nrm[1:ii])
         if nrmax <= tol
            break
         else
            ρ += 1
         end
         if ind != ii
            (jp[ii], jp[ind]) = (jp[ind], jp[ii])
            (C1[ii,:],C1[ind,:]) = (C1[ind,:],C1[ii,:])
         end
         for jj = 1:n-i
             jjp1 = jj+1
             if C1[ii,jj] != ZERO
                G, r = givens(conj(C1[ii,jjp1]),conj(C1[ii,jj]),jjp1,jj)
                C1[ii,jjp1] = conj(r)
                C1[ii,jj] = ZERO
                rmul!(view(C1,1:ii-1,:),G')
                rmul!(A1,G')
                rmul!(view(E1,1:jjp1,ia),G')
                withZ && rmul!(view(Z,:,ia),G')
                G, E[jj,jj] = givens(E[jj,jj],E[jjp1,jj],jj,jjp1)
                E[jjp1,jj] = ZERO
                lmul!(G,view(E,ia,jjp1:nA))
                withQ && rmul!(view(Q,:,ia),G')
                lmul!(G,view(A,ia,ja))
                ismissing(B) || lmul!(G,view(B,ia,:))
             end
         end
      end
      C1[:,1:n] = [ zeros(T,p,n-ρ)  C1[invperm(jp),n-ρ+1:n]];
   else
      if n > p
         for i = 1:p
             ii = p-i+1
             for jj = 1:n-i
                jjp1 = jj+1
                if C1[ii,jj] != ZERO
                   G, r = givens(conj(C1[ii,jjp1]),conj(C1[ii,jj]),jjp1,jj)
                   C1[ii,jjp1] = conj(r)
                   C1[ii,jj] = ZERO
                   rmul!(view(C1,1:ii-1,:),G')
                   rmul!(A1,G')
                   rmul!(view(E,1:jjp1,ia),G')
                   withZ && rmul!(view(Z,:,ia),G')
                   G, E[jj,jj] = givens(E[jj,jj],E[jjp1,jj],jj,jjp1)
                   E[jjp1,jj] = ZERO
                   lmul!(G,view(E,ia,jjp1:nA))
                   withQ && rmul!(view(Q,:,ia),G')
                   lmul!(G,view(A,ia,ja))
                   ismissing(B) || lmul!(G,view(B,ia,:))
                end
             end
         end
      end
      pn = min(n,p)
      pn == 0 && (return 0)
      ics = 1:p
      jcs = n-pn+1:n
      SVD = svd(C1[ics,jcs], full = true)
      ρ = count(x -> x > tol, SVD.S)
      ρ == pn && (return ρ)
      Q1 = reverse(SVD.U,dims=2)
      C1[ics,jcs] = [ zeros(T,p,pn-ρ) Q1[:,p-ρ+1:end]*Diagonal(reverse(SVD.S[1:ρ])) ]
      ρ == 0 && (return ρ)
      Z1 = reverse(SVD.V,dims=2)
      jt = n-pn+1:n
      withZ && (Z[:,jt] = Z[:,jt]*Z1)
      A[ia,jt] = A[ia,jt]*Z1
      E[ia,jt] = E[ia,jt]*Z1    # more efficient computation possible
      jt1 = n+1:nA
      tau = similar(E,pn)
      E22 = view(E,jt,jt)
      LinearAlgebra.LAPACK.geqrf!(E22,tau)
      T <: Complex ? tran = 'C' : tran = 'T'
      LinearAlgebra.LAPACK.ormqr!('L',tran,E22,tau,view(A,jt,ja))
      withQ && LinearAlgebra.LAPACK.ormqr!('R','N',E22,tau,view(Q,:,jt))
      LinearAlgebra.LAPACK.ormqr!('L',tran,E22,tau,view(E,jt,jt1))
      ismissing(B) || LinearAlgebra.LAPACK.ormqr!('L',tran,E22,tau,view(B,jt,:))
      triu!(E22)
   end
   return ρ
end