text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

struct StorageAccountsApi <: SwaggerApi
    client::Swagger.Client
end

"""

Checks that the storage account name is valid and is not already in use.
Param: accountName::StorageAccountCheckNameAvailabilityParameters (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: CheckNameAvailabilityResult
"""
function _swaggerinternal_storageAccountsCheckNameAvailability(_api::StorageAccountsApi, accountName, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("api_version", "storageAccountsCheckNameAvailability", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsCheckNameAvailability", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", CheckNameAvailabilityResult, "/subscriptions/{subscriptionId}/providers/Microsoft.Storage/checkNameAvailability", ["azure_auth"], accountName)
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsCheckNameAvailability(_api::StorageAccountsApi, accountName, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsCheckNameAvailability(_api, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsCheckNameAvailability(_api::StorageAccountsApi, response_stream::Channel, accountName, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsCheckNameAvailability(_api, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Asynchronously creates a new storage account with the specified parameters. If an account is already created and a subsequent create request is issued with different properties, the account properties will be updated. If an account is already created and a subsequent create or update request is issued with the exact same set of properties, the request will succeed.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: parameters::StorageAccountCreateParameters (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: StorageAccount
"""
function _swaggerinternal_storageAccountsCreate(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsCreate", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsCreate", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsCreate", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsCreate", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsCreate", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsCreate", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "PUT", StorageAccount, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsCreate(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsCreate(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsCreate(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsCreate(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Deletes a storage account in Microsoft Azure.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: Nothing
"""
function _swaggerinternal_storageAccountsDelete(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsDelete", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsDelete", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsDelete", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsDelete", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsDelete", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsDelete", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "DELETE", Nothing, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsDelete(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsDelete(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsDelete(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsDelete(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Failover request can be triggered for a storage account in case of availability issues. The failover occurs from the storage account's primary cluster to secondary cluster for RA-GRS accounts. The secondary cluster will become primary after failover.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: Nothing
"""
function _swaggerinternal_storageAccountsFailover(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsFailover", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsFailover", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsFailover", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsFailover", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsFailover", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsFailover", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", Nothing, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/failover", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsFailover(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsFailover(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsFailover(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsFailover(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Returns the properties for the specified storage account including but not limited to name, SKU name, location, and account status. The ListKeys operation should be used to retrieve storage keys.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: expand::String
Return: StorageAccount
"""
function _swaggerinternal_storageAccountsGetProperties(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; expand=nothing, _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsGetProperties", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsGetProperties", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsGetProperties", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsGetProperties", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsGetProperties", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsGetProperties", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", StorageAccount, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_param(_ctx.query, "\$expand", expand)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsGetProperties(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; expand=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsGetProperties(_api, resourceGroupName, accountName, api_version, subscriptionId; expand=expand, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsGetProperties(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; expand=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsGetProperties(_api, resourceGroupName, accountName, api_version, subscriptionId; expand=expand, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Lists all the storage accounts available under the subscription. Note that storage keys are not returned; use the ListKeys operation for this.
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: StorageAccountListResult
"""
function _swaggerinternal_storageAccountsList(_api::StorageAccountsApi, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("api_version", "storageAccountsList", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsList", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", StorageAccountListResult, "/subscriptions/{subscriptionId}/providers/Microsoft.Storage/storageAccounts", ["azure_auth"])
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsList(_api::StorageAccountsApi, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsList(_api, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsList(_api::StorageAccountsApi, response_stream::Channel, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsList(_api, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

List SAS credentials of a storage account.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: parameters::AccountSasParameters (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: ListAccountSasResponse
"""
function _swaggerinternal_storageAccountsListAccountSAS(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsListAccountSAS", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsListAccountSAS", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsListAccountSAS", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsListAccountSAS", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsListAccountSAS", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsListAccountSAS", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", ListAccountSasResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/ListAccountSas", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsListAccountSAS(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListAccountSAS(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsListAccountSAS(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListAccountSAS(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Lists all the storage accounts available under the given resource group. Note that storage keys are not returned; use the ListKeys operation for this.
Param: resourceGroupName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: StorageAccountListResult
"""
function _swaggerinternal_storageAccountsListByResourceGroup(_api::StorageAccountsApi, resourceGroupName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsListByResourceGroup", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsListByResourceGroup", :minLength, resourceGroupName, 1)

    Swagger.validate_param("api_version", "storageAccountsListByResourceGroup", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsListByResourceGroup", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", StorageAccountListResult, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsListByResourceGroup(_api::StorageAccountsApi, resourceGroupName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListByResourceGroup(_api, resourceGroupName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsListByResourceGroup(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListByResourceGroup(_api, resourceGroupName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Lists the access keys or Kerberos keys (if active directory enabled) for the specified storage account.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: expand::String
Return: StorageAccountListKeysResult
"""
function _swaggerinternal_storageAccountsListKeys(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; expand=nothing, _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsListKeys", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsListKeys", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsListKeys", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsListKeys", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsListKeys", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsListKeys", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", StorageAccountListKeysResult, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/listKeys", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_param(_ctx.query, "\$expand", expand)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsListKeys(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; expand=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListKeys(_api, resourceGroupName, accountName, api_version, subscriptionId; expand=expand, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsListKeys(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; expand=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListKeys(_api, resourceGroupName, accountName, api_version, subscriptionId; expand=expand, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

List service SAS credentials of a specific resource.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: parameters::ServiceSasParameters (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: ListServiceSasResponse
"""
function _swaggerinternal_storageAccountsListServiceSAS(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsListServiceSAS", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsListServiceSAS", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsListServiceSAS", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsListServiceSAS", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsListServiceSAS", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsListServiceSAS", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", ListServiceSasResponse, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/ListServiceSas", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsListServiceSAS(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListServiceSAS(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsListServiceSAS(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsListServiceSAS(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Regenerates one of the access keys or Kerberos keys for the specified storage account.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: regenerateKey::StorageAccountRegenerateKeyParameters (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: StorageAccountListKeysResult
"""
function _swaggerinternal_storageAccountsRegenerateKey(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, regenerateKey, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsRegenerateKey", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsRegenerateKey", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsRegenerateKey", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsRegenerateKey", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsRegenerateKey", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsRegenerateKey", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", StorageAccountListKeysResult, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/regenerateKey", ["azure_auth"], regenerateKey)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsRegenerateKey(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, regenerateKey, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsRegenerateKey(_api, resourceGroupName, accountName, regenerateKey, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsRegenerateKey(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, regenerateKey, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsRegenerateKey(_api, resourceGroupName, accountName, regenerateKey, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Restore blobs in the specified blob ranges
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: parameters::BlobRestoreParameters (required)
Return: BlobRestoreStatus
"""
function _swaggerinternal_storageAccountsRestoreBlobRanges(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, parameters; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsRestoreBlobRanges", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsRestoreBlobRanges", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsRestoreBlobRanges", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsRestoreBlobRanges", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsRestoreBlobRanges", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsRestoreBlobRanges", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", BlobRestoreStatus, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/restoreBlobRanges", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsRestoreBlobRanges(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, parameters; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsRestoreBlobRanges(_api, resourceGroupName, accountName, api_version, subscriptionId, parameters; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsRestoreBlobRanges(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, parameters; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsRestoreBlobRanges(_api, resourceGroupName, accountName, api_version, subscriptionId, parameters; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Revoke user delegation keys.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: Nothing
"""
function _swaggerinternal_storageAccountsRevokeUserDelegationKeys(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsRevokeUserDelegationKeys", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsRevokeUserDelegationKeys", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsRevokeUserDelegationKeys", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsRevokeUserDelegationKeys", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsRevokeUserDelegationKeys", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsRevokeUserDelegationKeys", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "POST", Nothing, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/revokeUserDelegationKeys", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsRevokeUserDelegationKeys(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsRevokeUserDelegationKeys(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsRevokeUserDelegationKeys(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsRevokeUserDelegationKeys(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

The update operation can be used to update the SKU, encryption, access tier, or tags for a storage account. It can also be used to map the account to a custom domain. Only one custom domain is supported per storage account; the replacement/change of custom domain is not supported. In order to replace an old custom domain, the old value must be cleared/unregistered before a new value can be set. The update of multiple properties is supported. This call does not change the storage keys for the account. If you want to change the storage account keys, use the regenerate keys operation. The location and name of the storage account cannot be changed after creation.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: parameters::StorageAccountUpdateParameters (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: StorageAccount
"""
function _swaggerinternal_storageAccountsUpdate(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "storageAccountsUpdate", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "storageAccountsUpdate", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "storageAccountsUpdate", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "storageAccountsUpdate", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "storageAccountsUpdate", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "storageAccountsUpdate", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "PATCH", StorageAccount, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}", ["azure_auth"], parameters)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function storageAccountsUpdate(_api::StorageAccountsApi, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsUpdate(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function storageAccountsUpdate(_api::StorageAccountsApi, response_stream::Channel, resourceGroupName::String, accountName::String, parameters, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_storageAccountsUpdate(_api, resourceGroupName, accountName, parameters, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

export storageAccountsCheckNameAvailability, storageAccountsCreate, storageAccountsDelete, storageAccountsFailover, storageAccountsGetProperties, storageAccountsList, storageAccountsListAccountSAS, storageAccountsListByResourceGroup, storageAccountsListKeys, storageAccountsListServiceSAS, storageAccountsRegenerateKey, storageAccountsRestoreBlobRanges, storageAccountsRevokeUserDelegationKeys, storageAccountsUpdate


text:
using Documenter
using NodesAndModes

makedocs(
    sitename = "NodesAndModes.jl",
    repo = "https://github.com/jlchan/NodesAndModes.jl",
    modules=[NodesAndModes],
    pages = [
        "Home" => "index.md",
        "Index" => "function_index.md",
        "Authors" => "authors.md"
    ]
)

deploydocs(
    repo = "github.com/jlchan/NodesAndModes.jl",
)

ext: jl


text:
function Base.show(io::IO, self::Node)
    _show(io, self, class(self))
end
_show(io::IO, self::Node, ::Type{<:Any}) = print(io, "$(Meta.parse("($(args(self)[1])) $(data(self)) ($(args(self)[2]))"))")

_show(io::IO, self::Node, ::Type{<:AAtom}) = print(io, data(self))

_show(io::IO, self::Node, ::Type{<:ACall}) = print(io, "$(Meta.parse("$(args(self)[1])($(args(self)[2]))"))")

_show(io::IO, self::Node, ::Type{<:AConditionSet}) = print(io, "$(Meta.parse("ConditionSet($(args(self)[1]), $(args(self)[2]))"))")

_show(io::IO, self::Node, ::Type{<:ANot}) = print(io, "$(Meta.parse("$(data(self))($(args(self)[1]))"))")


text:
using BenchmarkTools, FastGroupBy, Base.Threads
import FastGroupBy.load_bits

# This seems to be the fastest counter; see benchmark src/which_is_the_fastest_UInt_histogram.jl
# creates a histogram of each of 16bits that make up the 64bit arrays
function hist_uint(bits, htype::Type{T} = UInt) where T <: Unsigned
    hist = zeros(T, 1<<16, 4, nthreads())
    @threads for j = 1:length(bits)
        for i = 0:3
            @inbounds hist[1 + Int((bits[j] >> (i << 4)) & 0xffff), i+1, threadid()] += 1
        end
    end
    @threads for j in 1:4
        for i = 2:nthreads()
           @inbounds hist[:, j, 1] .+= hist[:, j, i]
        end
    end
    hist[:,:,1]
end

function paradissort2!(svec::Vector{String}, rev = false, counter_type::Type{CT} = UInt) where CT <: Unsigned
    len = maximum(sizeof,sizeof(svec))
    skipbytes = 0

    bits = load_bits.(UInt, svec, skipbytes)
    hist = hist_uint(bits, counter_type)
    skipbytes += 8
    len -= 8

    while len > 0
        bits = load_bits.(UInt, svec, skipbytes)
        hist = hist_uint(bits, counter_type)
        skipbytes += 8
        len -= 8
    end
    hist
end

# this is provided for timing control
# DO NOT DELETE
function paradissort2_single!(svec::Vector{String}, rev = false)
    len = maximum(sizeof,sizeof(svec))
    skipbytes = 0

    bits = load_bits.(UInt, svec)

    # build a histogram of the first 8 bytes
    hist = zeros(UInt64, 1<<16, 4)
    hist_hot = zeros(UInt8, 1<<16)
    @inbounds for i in 0:3
        for x1 in bits
            idx = 1+ Int((x1 >> (i << 4)) & 0xffff)
            hist_hot[idx] += UInt8(1)
            if hist_hot[idx] == 0
                hist[idx, i+1] += (1<<8)
            end
        end
        for idx in 1:(1<<16)
            hist[idx,i+1] += hist_hot[idx]
        end
        fill!(hist_hot, zero(UInt8))
    end
    skipbytes += 8
    len -= 8

    while len > 0
        bits = load_bits.(UInt, svec)
        hist = zeros(UInt64, 1<<16, 4)
        hist_hot = zeros(UInt8, 1<<16)
        @inbounds for i in 0:3
            for x1 in bits
                idx = 1+ Int((x1 >> (i << 4)) & 0xffff)
                hist_hot[idx] += UInt8(1)
                if hist_hot[idx] == 0
                    hist[idx, i+1] += (1<<8)
                end
            end
            for idx in 1:(1<<16)
                hist[idx,i+1] += hist_hot[idx]
            end
            fill!(hist_hot, zero(UInt8))
        end
        skipbytes += 8
        len -= 8
    end

    # the gap at this point is 1.75 seconds

    # use the histogram

    hist
end

N = 100_000_000
K = 100
svec = rand(["id".*dec.(i,10) for i = 1:N÷K], N);

using BenchmarkTools
gc()
@btime paradissort2!($svec);
gc()
@btime paradissort2_single!($svec);
# the gap is 1.7 seconds at this point


text:
@with_kw mutable struct DTrustRegionData{T <: Real}
    Q::T = 1e10
    Q̃::T = 1e10
    θ::T = -1e10
    Δ::T = 1.0
    cΔ::Int = 0
    timestamp::Int = 1
    incubent::Int = 1
    iterations::Int = 0
    major_iterations::Int = 0
    minor_iterations::Int = 0
end

@with_kw mutable struct DTrustRegionParameters{T <: Real}
    κ::T = 0.6
    τ::T = 1e-6
    γ::T = 1e-4
    Δ = 1.0
    Δ̅::T = 1000.0
    bundle::Int = 1
    log::Bool = true
    autotune::Bool = false
end

"""
    DTrustRegion

Functor object for the distributed trust-region L-shaped algorithm. Create by supplying `:tr` to the `LShapedSolver` factory function and then pass to a `StochasticPrograms.jl` model, assuming there are available worker cores.

...
# Algorithm parameters
- `κ::Real = 0.6`: Amount of cutting planes, relative to the total number of scenarios, required to generate a new iterate in master procedure.
- `τ::Real = 1e-6`: Relative tolerance for convergence checks.
- `γ::T` = 1e-4: Relative tolerance for deciding if a minor iterate should be accepted as a new major iterate.
- `Δ::Real = 1.0`: Initial size of ∞-norm trust-region.
- `Δ̅::Real = 1000.0`: Maximum size of ∞-norm trust-region.
- `bundle::Int = 1`: Amount of cutting planes in bundle. A value of 1 corresponds to a multicut algorithm and a value of at least the number of scenarios yields the classical L-shaped algorithm.
- `log::Bool = true`: Specifices if L-shaped procedure should be logged on standard output or not.
- `autotune::Bool = false`: If `true`, heuristic methods are used to set `Δ̅` and `Δ̅` based on the initial decision.
...
"""
struct DTrustRegion{F, T <: Real, A <: AbstractVector, SP <: StochasticProgram, M <: LQSolver, S <: LQSolver} <: AbstractLShapedSolver{F,T,A,M,S}
    stochasticprogram::SP
    solverdata::DTrustRegionData{T}

    # Master
    mastersolver::M
    mastervector::A
    c::A
    x::A
    Q_history::A

    # Subproblems
    nscenarios::Int
    subobjectives::Vector{A}
    finished::Vector{Int}

    # Workers
    subworkers::Vector{SubWorker{F,T,A,S}}
    work::Vector{Work}
    decisions::Decisions{A}
    cutqueue::CutQueue{T}
    active_workers::Vector{Future}

    # Trust region
    ξ::A
    incubents::Vector{Int}
    Q̃_history::A
    Δ_history::A

    # Cuts
    θs::A
    cuts::Vector{SparseHyperPlane{T}}
    θ_history::A

    # Params
    parameters::DTrustRegionParameters{T}
    progress::ProgressThresh{T}

    @implement_trait DTrustRegion TR
    @implement_trait DTrustRegion Parallel

    function (::Type{DTrustRegion})(stochasticprogram::StochasticProgram, ξ₀::AbstractVector, mastersolver::MPB.AbstractMathProgSolver, subsolver::SubSolver, F::Bool; kw...)
        if nworkers() == 1
            @warn "There are no worker processes, defaulting to serial version of algorithm"
            return TrustRegion(stochasticprogram, ξ₀, mastersolver, get_solver(subsolver); kw...)
        end
        first_stage = StochasticPrograms.get_stage_one(stochasticprogram)
        length(ξ₀) != first_stage.numCols && error("Incorrect length of starting guess, has ", length(ξ₀), " should be ", first_stage.numCols)

        T = promote_type(eltype(ξ₀), Float32)
        c_ = convert(AbstractVector{T}, JuMP.prepAffObjective(first_stage))
        c_ *= first_stage.objSense == :Min ? 1 : -1
        ξ₀_ = convert(AbstractVector{T}, copy(ξ₀))
        x₀_ = convert(AbstractVector{T}, copy(ξ₀))
        mastervector = convert(AbstractVector{T}, copy(ξ₀))
        A = typeof(ξ₀_)
        SP = typeof(stochasticprogram)
        msolver = LQSolver(first_stage, mastersolver)
        M = typeof(msolver)
        solver_instance = get_solver(subsolver)
        S = LQSolver{typeof(MPB.LinearQuadraticModel(solver_instance)),typeof(solver_instance)}
        n = StochasticPrograms.nscenarios(stochasticprogram)

        lshaped = new{F,T,A,SP,M,S}(stochasticprogram,
                                    DTrustRegionData{T}(),
                                    msolver,
                                    mastervector,
                                    c_,
                                    x₀_,
                                    A(),
                                    n,
                                    Vector{A}(),
                                    Vector{Int}(),
                                    Vector{SubWorker{F,T,A,S}}(undef,nworkers()),
                                    Vector{Work}(undef,nworkers()),
                                    RemoteChannel(() -> DecisionChannel(Dict{Int,A}())),
                                    RemoteChannel(() -> Channel{QCut{T}}(4*nworkers()*n)),
                                    Vector{Future}(undef,nworkers()),
                                    ξ₀_,
                                    Vector{Inf}(),
                                    A(),
                                    A(),
                                    A(),
                                    Vector{SparseHyperPlane{T}}(),
                                    A(),
                                    DTrustRegionParameters{T}(;kw...),
                                    ProgressThresh(1.0, "Distributed TR L-Shaped Gap "))
        # Initialize solver
        init!(lshaped, subsolver)
        return lshaped
    end
end
DTrustRegion(stochasticprogram::StochasticProgram, mastersolver::MPB.AbstractMathProgSolver, subsolver::SubSolver, checkfeas::Bool; kw...) = DTrustRegion(stochasticprogram, rand(decision_length(stochasticprogram)), mastersolver, subsolver, checkfeas; kw...)

function (lshaped::DTrustRegion)()
    # Reset timer
    lshaped.progress.tfirst = lshaped.progress.tlast = time()
    # Start workers
    init_workers!(lshaped)
    # Start procedure
    while true
        status = iterate!(lshaped)
        if status != :Valid
            close_workers!(lshaped)
            return status
        end
    end
end

@implement_traitfn function log_regularization!(lshaped::DTrustRegion, TR)
    @unpack Q̃,Δ,incubent = lshaped.solverdata
    push!(lshaped.Q̃_history,Q̃)
    push!(lshaped.Δ_history,Δ)
    push!(lshaped.incubents,incubent)
end

@implement_traitfn function log_regularization!(lshaped::DTrustRegion, t::Integer, TR)
    @unpack Q̃,Δ,incubent = lshaped.solverdata
    lshaped.Q̃_history[t] = Q̃
    lshaped.Δ_history[t] = Δ
    lshaped.incubents[t] = incubent
end

@implement_traitfn function enlarge_trustregion!(lshaped::DTrustRegion, TR)
    @unpack Q,θ = lshaped.solverdata
    @unpack τ,Δ̅ = lshaped.parameters
    t = lshaped.solverdata.timestamp
    lshaped.solverdata.incubent = t
    Δ̃ = lshaped.Δ_history[t]
    ξ = t > 1 ? fetch(lshaped.decisions,lshaped.incubents[t]) : lshaped.ξ
    Q̃ = t > 1 ? lshaped.Q̃_history[lshaped.incubents[t]] : lshaped.solverdata.Q̃
    if Q̃ - Q >= 0.5*(Q̃-θ) && abs(norm(ξ-lshaped.x,Inf) - Δ̃) <= τ
        # Enlarge the trust-region radius
        lshaped.solverdata.Δ = max(lshaped.solverdata.Δ,min(Δ̅,2*Δ̃))
        return true
    else
        return false
    end
end

@implement_traitfn function reduce_trustregion!(lshaped::DTrustRegion, TR)
    @unpack Q,θ = lshaped.solverdata
    t = lshaped.solverdata.timestamp
    Δ̃ = lshaped.Δ_history[t]
    Q̃ = t > 1 ? lshaped.Q̃_history[lshaped.incubents[t]] : lshaped.solverdata.Q̃
    ρ = min(1,Δ̃)*(Q-Q̃)/(Q̃-θ)
    if ρ > 0
        lshaped.solverdata.cΔ += 1
    end
    if ρ > 3 || (lshaped.solverdata.cΔ >= 3 && 1 < ρ <= 3)
        # Reduce the trust-region radius
        lshaped.solverdata.cΔ = 0
        lshaped.solverdata.Δ = min(lshaped.solverdata.Δ,(1/min(ρ,4))*Δ̃)
        return true
    else
        return false
    end
end


text:
"Abstract Material Type (can be all)"
abstract type AbstractMaterial end

"Abstract water type"
abstract type AbstractWater <: AbstractMaterial end
"Abstract soil type"
abstract type AbstractSoil  <: AbstractMaterial end

"Pure liquid water"
struct LiquidPureWater <: AbstractWater end

"Salty liquid water"
Base.@kwdef struct LiquidSaltWater{FT} <: AbstractWater
    "Salinity in `[PSU]`"
    S::FT = FT(10)
end

"Pure Ice"
struct PureIce <: AbstractWater end

"Soil MW properties"
Base.@kwdef struct SoilMW{FT} <: AbstractSoil
    "Sand Fraction ∈ [0,1]"
    sand_frac::FT  = FT(0.2)
    "Clay Fraction ∈ [0,1]"
    clay_frac::FT  = FT(0.1)
    "Volumetric water content ∈ [0,1]"
    mᵥ::FT = FT(0.35)
    "Bulk density, `g/cm³` (typical value is 1.7 gcm³)"
    ρ::FT          = FT(1.7)
end


text:
"""
    BoundaryConditions

A set of functions that apply Dirichlet and Neumann boundary conditions
at the top and bottom of the domains of a grid, given a state vector.
"""
module BoundaryConditions

using ..Grids, ..StateVecs
export Dirichlet!, Neumann!, Top, Bottom

abstract type BoundaryLocation end

"""
    Top<:BoundaryLocation

Location to apply boundary condition
"""
struct Top<:BoundaryLocation end

"""
    Bottom<:BoundaryLocation

Location to apply boundary condition
"""
struct Bottom<:BoundaryLocation end

"""
    Dirichlet!(sv::StateVec, name::Symbol, val, grid, ::Bottom, i_sd=1)

Apply Dirichlet boundary conditions at the bottom of the domain
"""
function Dirichlet!(sv::StateVec, name::Symbol, val, grid, ::Bottom, i_sd=1)
  e = grid.n_ghost
  sv[name, e, i_sd] = 2*val - sv[name, e+1, i_sd]
end

"""
    Dirichlet!(sv::StateVec, name::Symbol, val, grid, ::Top, i_sd=1)

Apply Dirichlet boundary conditions at the bottom of the domain
"""
function Dirichlet!(sv::StateVec, name::Symbol, val, grid, ::Top, i_sd=1)
  e = grid.n_elem - (grid.n_ghost-1)
  sv[name, e, i_sd] = 2*val - sv[name, e-1, i_sd]
end

"""
    Neumann!(sv::StateVec, name::Symbol, val, grid, ::Bottom, i_sd=1)

Apply Neumann boundary conditions at the bottom of the domain
"""
function Neumann!(sv::StateVec, name::Symbol, val, grid, ::Bottom, i_sd=1)
  e = grid.n_ghost
  sv[name, e, i_sd] = sv[name, e+1, i_sd] - grid.dz*val
end

"""
    Neumann!(sv::StateVec, name::Symbol, val, grid, ::Top, i_sd=1)

Apply Neumann boundary conditions at the bottom of the domain
"""
function Neumann!(sv::StateVec, name::Symbol, val, grid, ::Top, i_sd=1)
  e = grid.n_elem - (grid.n_ghost-1)
  sv[name, e, i_sd] = sv[name, e-1, i_sd] + grid.dz*val
end

end

max_stars_repo_name: christophernhill-dev/CLIMA

max_issues_repo_name: christophernhill-dev/CLIMA

max_forks_repo_name: christophernhill-dev/CLIMA


text:
export NwtdirectionLDLt, NewtonLDLtAbs

# extremely slow compared to other way to compute Newton direction
# probably bunchkaufman factorization or the computing of eigenvalues?

"""Computes the Newton direction using the LDLᵗ factorization"""
function NwtdirectionLDLt(H,g;verbose::Bool=false)
    # L = Array{Float64}(2)
    # D = Array{Float64}(2)
    global L = Array{Float64}(undef, 2)
    global D = Array{Float64}(undef, 2)
    global pp = Array{Int}(undef, 1)
    global ρ = Float64
    global ncomp = Int64

    try
        (L, D, pp, rho, ncomp) = ldlt_symm(H,'p')
        # LBL = bunchkaufman(H)
    catch
 	println("*******   Problem in LDLt")
        #res = PDataLDLt()
        #res.OK = false
        #return res
        return (NaN, NaN, NaN, Inf, false, true, :fail)
    end
    # LBL = bunchkaufman(Symmetric(H, :L))
    # D = LBL.D
    # L = LBL.L
    # pp = LBL.p

    # A[pp,pp] = P*A*P' =  L*D*L'

    if true in isnan.(D)
 	println("*******   Problem in D from LDLt: NaN")
        println(" cond (H) = $(cond(H))")
        #res = PDataLDLt()
        #res.OK = false
        #return res
        return (NaN, NaN, NaN, Inf, false, true, :fail)
    end

    # Δ, Q = eig(D)
    Δ, Q = eigen(Symmetric(D))

    ϵ2 =  1.0e-8
    Γ = max.(abs.(Δ),ϵ2)

    # Ad = P'*L*Q*Δ*Q'*L'*Pd =    -g
    # replace Δ by Γ to ensure positive definiteness
    d̃ = L\g[pp]
    d̂ = L'\ (Q*(Q'*d̃ ./ Γ))
    d = - d̂[invperm(pp)]

    return d
end

function NewtonLDLtAbs(nlp          :: AbstractNLPModel,
                       nlp_stop     :: NLPStopping;
                       verbose      :: Bool=false,
                       verboseLS    :: Bool = false,
                       kwargs...)
    return  Newton(nlp,
                   nlp_stop;
                   verbose = verbose,
                   verboseLS = verboseLS,
                   Nwtdirection = NwtdirectionLDLt,
                   hessian_rep = hessian_dense,
                   kwargs...)
end


text:
#=
    stride_preserving_index(::Type{T}) -> StaticBool

Returns `True` if strides between each element can still be derived when indexing with an
instance of type `T`.
=#
stride_preserving_index(::Type{T}) where {T<:AbstractRange} = True()
stride_preserving_index(::Type{T}) where {T<:Int} = True()
stride_preserving_index(::Type{T}) where {T} = False()
function stride_preserving_index(::Type{T}) where {N,T<:Tuple{Vararg{Any,N}}}
    if all(eachop(_stride_preserving_index, nstatic(Val(N)), T))
        return True()
    else
        return False()
    end
end
function _stride_preserving_index(::Type{T}, i::StaticInt) where {T}
    return stride_preserving_index(_get_tuple(T, i))
end

"""
    known_offsets(::Type{T}[, dim]) -> Tuple

Returns a tuple of offset values known at compile time. If the offset of a given axis is
not known at compile time `nothing` is returned its position.
"""
known_offsets(x, dim) = known_offsets(typeof(x), dim)
known_offsets(::Type{T}, dim) where {T} = known_offsets(T, to_dims(T, dim))
function known_offsets(::Type{T}, dim::Integer) where {T}
    if ndims(T) < dim
        return 1
    else
        return known_offsets(T)[dim]
    end
end

known_offsets(x) = known_offsets(typeof(x))
function known_offsets(::Type{T}) where {T}
    return eachop(_known_offsets, nstatic(Val(ndims(T))), axes_types(T))
end
_known_offsets(::Type{T}, dim::StaticInt) where {T} = known_first(_get_tuple(T, dim))

"""
    offsets(A[, dim]) -> Tuple

Returns offsets of indices with respect to 0. If values are known at compile time,
it should return them as `Static` numbers.
For example, if `A isa Base.Matrix`, `offsets(A) === (StaticInt(1), StaticInt(1))`.
"""
@inline offsets(x, i) = static_first(indices(x, i))
offsets(::Tuple) = (One(),)
offsets(x) = eachop(_offsets, nstatic(Val(ndims(x))), x)
function _offsets(x::X, dim::StaticInt{D}) where {X,D}
    start = known_first(axes_types(X, dim))
    if start === nothing
        return first(axes(x, dim))
    else
        return static(start)
    end
end

"""
    known_offset1(x) -> Union{Int,Nothing}

Returns the linear offset of array `x` if known at compile time.
"""
known_offset1(x) = known_offset1(typeof(x))
known_offset1(::Type{T}) where {T} = _known_offset1(has_parent(T), T)
_known_offset1(::True, ::Type{T}) where {T} = known_offset1(parent_type(T))
_known_offset1(::False, ::Type{T}) where {T} = 1

"""
    offset1(x) -> Integer

Returns the offset of the linear indices for `x`.
"""
@inline function offset1(x::X) where {X}
    o1 = known_offset1(X)
    if o1 === nothing
        return firstinde(x)
    else
        return static(o1)
    end
end

"""
    contiguous_axis(::Type{T}) -> StaticInt{N}

Returns the axis of an array of type `T` containing contiguous data.
If no axis is contiguous, it returns `StaticInt{-1}`.
If unknown, it returns `nothing`.
"""
contiguous_axis(x) = contiguous_axis(typeof(x))
function contiguous_axis(::Type{T}) where {T}
    if parent_type(T) <: T
        return nothing
    else
        return contiguous_axis(parent_type(T))
    end
end
contiguous_axis(::Type{<:Array}) = One()
contiguous_axis(::Type{<:BitArray}) = One()
contiguous_axis(::Type{<:AbstractRange}) = One()
contiguous_axis(::Type{<:Tuple}) = One()
function contiguous_axis(::Type{T}) where {T<:VecAdjTrans}
    c = contiguous_axis(parent_type(T))
    if c === nothing
        return nothing
    elseif c === One()
        return StaticInt{2}()
    else
        return -One()
    end
end
function contiguous_axis(::Type{T}) where {T<:MatAdjTrans}
    c = contiguous_axis(parent_type(T))
    if c === nothing
        return nothing
    elseif isone(-c)
        return c
    else
        return StaticInt(3) - c
    end
end
function contiguous_axis(::Type{T}) where {T<:PermutedDimsArray}
    c = contiguous_axis(parent_type(T))
    if c === nothing
        return nothing
    elseif isone(-c)
        return c
    else
        return from_parent_dims(T, c)
    end
end
function contiguous_axis(::Type{T}) where {T<:SubArray}
    return _contiguous_axis(T, contiguous_axis(parent_type(T)))
end

_contiguous_axis(::Type{A}, ::Nothing) where {T,N,P,I,A<:SubArray{T,N,P,I}} = nothing
_contiguous_axis(::Type{A}, c::StaticInt{-1}) where {T,N,P,I,A<:SubArray{T,N,P,I}} = c
function _contiguous_axis(::Type{A}, c::StaticInt{C}) where {T,N,P,I,A<:SubArray{T,N,P,I},C}
    if _get_tuple(I, c) <: AbstractUnitRange
        return from_parent_dims(A)[C]
    elseif _get_tuple(I, c) <: AbstractArray
        return -One()
    elseif _get_tuple(I, c) <: Integer
        return -One()
    else
        return nothing
    end
end

# contiguous_if_one(::StaticInt{1}) = StaticInt{1}()
# contiguous_if_one(::Any) = StaticInt{-1}()
function contiguous_axis(::Type{R}) where {T,N,S,A<:Array{S},R<:ReinterpretArray{T,N,S,A}}
    if isbitstype(S)
        return One()
    else
        return nothing
    end
end

"""
    contiguous_axis_indicator(::Type{T}) -> Tuple{Vararg{StaticBool}}

Returns a tuple boolean `Val`s indicating whether that axis is contiguous.
"""
function contiguous_axis_indicator(::Type{A}) where {D,A<:AbstractArray{<:Any,D}}
    return contiguous_axis_indicator(contiguous_axis(A), Val(D))
end
contiguous_axis_indicator(::A) where {A<:AbstractArray} = contiguous_axis_indicator(A)
contiguous_axis_indicator(::Nothing, ::Val) = nothing
function contiguous_axis_indicator(c::StaticInt{N}, dim::Val{D}) where {N,D}
    return map(i -> eq(c, i), nstatic(dim))
end

function rank_to_sortperm(R::Tuple{Vararg{StaticInt,N}}) where {N}
    sp = ntuple(zero, Val{N}())
    r = ntuple(n -> sum(R[n] .≥ R), Val{N}())
    @inbounds for n = 1:N
        sp = Base.setindex(sp, n, r[n])
    end
    return sp
end

stride_rank(x) = stride_rank(typeof(x))
function stride_rank(::Type{T}) where {T}
    if parent_type(T) <: T
        return nothing
    else
        return stride_rank(parent_type(T))
    end
end
stride_rank(::Type{Array{T,N}}) where {T,N} = nstatic(Val(N))
stride_rank(::Type{BitArray{N}}) where {N} = nstatic(Val(N))
stride_rank(::Type{<:AbstractRange}) = (One(),)
stride_rank(::Type{<:Tuple}) = (One(),)

stride_rank(::Type{T}) where {T<:VecAdjTrans} = (StaticInt(2), StaticInt(1))
stride_rank(::Type{T}) where {T<:MatAdjTrans} = _stride_rank(T, stride_rank(parent_type(T)))
_stride_rank(::Type{T}, ::Nothing) where {T<:MatAdjTrans} = nothing
function _stride_rank(::Type{T}, rank) where {T<:MatAdjTrans}
    return (getfield(rank, 2), getfield(rank, 1))
end

function stride_rank(::Type{T},) where {T<:PermutedDimsArray}
    return _stride_rank(T, stride_rank(parent_type(T)))
end
_stride_rank(::Type{T}, ::Nothing) where {T<:PermutedDimsArray} = nothing
_stride_rank(::Type{T}, r) where {T<:PermutedDimsArray} = permute(r, to_parent_dims(T))

stride_rank(::Type{T}) where {T<:SubArray} = _stride_rank(T, stride_rank(parent_type(T)))
_stride_rank(::Any, ::Any) = nothing
_stride_rank(::Type{T}, r::Tuple) where {T<:SubArray} = permute(r, to_parent_dims(T))

stride_rank(x, i) = stride_rank(x)[i]
function stride_rank(::Type{R}) where {T,N,S,A<:Array{S},R<:Base.ReinterpretArray{T,N,S,A}}
    return nstatic(Val(N))
end
if VERSION ≥ v"1.6.0-DEV.1581"
  @inline function stride_rank(::Type{A}) where {NB, NA, B <: AbstractArray{<:Any,NB},A<: Base.ReinterpretArray{<:Any, NA, <:Any, B, true}}
    _stride_rank_reinterpret(stride_rank(B), gt(StaticInt{NB}(), StaticInt{NA}()))
  end
  @inline _stride_rank_reinterpret(sr, ::False) = (One(), map(Base.Fix2(+,One()),sr)...)
  @inline _stride_rank_reinterpret(sr::Tuple{One,Vararg}, ::True) = map(Base.Fix2(-,One()), tail(sr))
  # if the leading dim's `stride_rank` is not one, then that means the individual elements are split across an axis, which ArrayInterface
  # doesn't currently have a means of representing.
  @inline function contiguous_axis(::Type{A}) where {NB, NA, B <: AbstractArray{<:Any,NB},A<: Base.ReinterpretArray{<:Any, NA, <:Any, B, true}}
    _reinterpret_contiguous_axis(stride_rank(B), dense_dims(B), contiguous_axis(B), gt(StaticInt{NB}(), StaticInt{NA}()))
  end
  @inline _reinterpret_contiguous_axis(::Any, ::Any, ::Any, ::False) = One()
  @inline _reinterpret_contiguous_axis(::Any, ::Any, ::Any, ::True) = Zero()
  @generated function _reinterpret_contiguous_axis(t::Tuple{One,Vararg{StaticInt,N}}, d::Tuple{True,Vararg{StaticBool,N}}, ::One, ::True) where {N}
    for n in 1:N
      if t.parameters[n+1].parameters[1] === 2
        if d.parameters[n+1] === True
          return :(StaticInt{$n}())
        else
          return :(Zero())
        end
      end
    end
    :(Zero())
  end
end

function stride_rank(::Type{Base.ReshapedArray{T, N, P, Tuple{Vararg{Base.SignedMultiplicativeInverse{Int},M}}}}) where {T,N,P,M}
    _reshaped_striderank(is_column_major(P), Val{N}(), Val{M}())
end
_reshaped_striderank(::True, ::Val{N}, ::Val{0}) where {N} = nstatic(Val(N))
_reshaped_striderank(_, __, ___) = nothing

"""
    contiguous_batch_size(::Type{T}) -> StaticInt{N}

Returns the Base.size of contiguous batches if `!isone(stride_rank(T, contiguous_axis(T)))`.
If `isone(stride_rank(T, contiguous_axis(T)))`, then it will return `StaticInt{0}()`.
If `contiguous_axis(T) == -1`, it will return `StaticInt{-1}()`.
If unknown, it will return `nothing`.
"""
contiguous_batch_size(x) = contiguous_batch_size(typeof(x))
contiguous_batch_size(::Type{T}) where {T} = _contiguous_batch_size(contiguous_axis(T), stride_rank(T))
_contiguous_batch_size(_, __) = nothing
function _contiguous_batch_size(::StaticInt{D}, ::R) where {D,R<:Tuple}
    if R.parameters[D].parameters[1] === 1
        return Zero()
    else
        return nothing
    end
end

contiguous_batch_size(::Type{Array{T,N}}) where {T,N} = Zero()
contiguous_batch_size(::Type{BitArray{N}}) where {N} = Zero()
contiguous_batch_size(::Type{<:AbstractRange}) = Zero()
contiguous_batch_size(::Type{<:Tuple}) = Zero()
function contiguous_batch_size(::Type{T}) where {T<:Union{Transpose,Adjoint}}
    return contiguous_batch_size(parent_type(T))
end
function contiguous_batch_size(::Type{T}) where {T<:PermutedDimsArray}
    return contiguous_batch_size(parent_type(T))
end
function contiguous_batch_size(::Type{S}) where {N,NP,T,A<:AbstractArray{T,NP},I,S<:SubArray{T,N,A,I}}
    return _contiguous_batch_size(S, contiguous_batch_size(A), contiguous_axis(A))
end
_contiguous_batch_size(::Any, ::Any, ::Any) = nothing
function _contiguous_batch_size(::Type{<:SubArray{T,N,A,I}}, b::StaticInt{B}, c::StaticInt{C}) where {T,N,A,I,B,C}
    if I.parameters[C] <: AbstractUnitRange
        return b
    else
        return -One()
    end
end
contiguous_batch_size(::Type{<:Base.ReinterpretArray{T,N,S,A}}) where {T,N,S,A} = Zero()

"""
    is_column_major(A) -> True/False

Returns `Val{true}` if elements of `A` are stored in column major order. Otherwise returns `Val{false}`.
"""
is_column_major(A) = is_column_major(stride_rank(A), contiguous_batch_size(A))
is_column_major(sr::Nothing, cbs) = False()
is_column_major(sr::R, cbs) where {R} = _is_column_major(sr, cbs)
is_column_major(::AbstractRange) = False()

# cbs > 0
_is_column_major(sr::R, cbs::StaticInt) where {R} = False()
# cbs <= 0
_is_column_major(sr::R, cbs::Union{StaticInt{0},StaticInt{-1}}) where {R} = is_increasing(sr)

"""
    dense_dims(::Type{T}) -> NTuple{N,Bool}

Returns a tuple of indicators for whether each axis is dense.
An axis `i` of array `A` is dense if `stride(A, i) * Base.size(A, i) == stride(A, j)` where `stride_rank(A)[i] + 1 == stride_rank(A)[j]`.
"""
dense_dims(x) = dense_dims(typeof(x))
function dense_dims(::Type{T}) where {T}
    if parent_type(T) <: T
        return nothing
    else
        return dense_dims(parent_type(T))
    end
end
_all_dense(::Val{N}) where {N} = ntuple(_ -> True(), Val{N}())

dense_dims(::Type{Array{T,N}}) where {T,N} = _all_dense(Val{N}())
dense_dims(::Type{BitArray{N}}) where {N} = _all_dense(Val{N}())
dense_dims(::Type{<:AbstractRange}) = (True(),)
dense_dims(::Type{<:Tuple}) = (True(),)
function dense_dims(::Type{T}) where {T<:VecAdjTrans}
    dense = dense_dims(parent_type(T))
    if dense === nothing
        return nothing
    else
        return (True(), first(dense))
    end
end
function dense_dims(::Type{T}) where {T<:MatAdjTrans}
    dense = dense_dims(parent_type(T))
    if dense === nothing
        return nothing
    else
        return (last(dense), first(dense))
    end
end
function dense_dims(::Type{T}) where {T<:PermutedDimsArray}
    dense = dense_dims(parent_type(T))
    if dense === nothing
        return nothing
    else
        return permute(dense, to_parent_dims(T))
    end
end
function dense_dims(::Type{S}) where {N,NP,T,A<:AbstractArray{T,NP},I,S<:SubArray{T,N,A,I}}
    return _dense_dims(S, dense_dims(A), Val(stride_rank(A)))
end
if VERSION ≥ v"1.6.0-DEV.1581"
    @inline function dense_dims(::Type{A}) where {NB, NA, B <: AbstractArray{<:Any,NB},A<: Base.ReinterpretArray{<:Any, NA, <:Any, B, true}}
        ddb = dense_dims(B)
        IfElse.ifelse(Static.le(StaticInt(NB), StaticInt(NA)), (True(), ddb...), Base.tail(ddb))
    end
end

_dense_dims(::Type{S}, ::Nothing, ::Val{R}) where {R,N,NP,T,A<:AbstractArray{T,NP},I,S<:SubArray{T,N,A,I}} = nothing
@generated function _dense_dims(
    ::Type{S},
    ::D,
    ::Val{R},
) where {D,R,N,NP,T,A<:AbstractArray{T,NP},I,S<:SubArray{T,N,A,I}}
    still_dense = true
    sp = rank_to_sortperm(R)
    densev = Vector{Bool}(undef, NP)
    for np = 1:NP
        spₙ = sp[np]
        if still_dense
            still_dense = D.parameters[spₙ] <: True
        end
        densev[spₙ] = still_dense
        # a dim not being complete makes later dims not dense
        still_dense &= (I.parameters[spₙ] <: Base.Slice)::Bool
    end
    dense_tup = Expr(:tuple)
    for np = 1:NP
        Iₙₚ = I.parameters[np]
        if Iₙₚ <: AbstractUnitRange
            if densev[np]
                push!(dense_tup.args, :(True()))
            else
                push!(dense_tup.args, :(False()))
            end
        elseif Iₙₚ <: AbstractVector
            push!(dense_tup.args, :(False()))
        end
    end
    # If n != N, then an axis was indexed by something other than an integer or `AbstractUnitRange`, so we return `nothing`.
    if length(dense_tup.args) === N
        return dense_tup
    else
        return nothing
    end
end

function dense_dims(::Type{Base.ReshapedArray{T, N, P, Tuple{Vararg{Base.SignedMultiplicativeInverse{Int},M}}}}) where {T,N,P,M}
    return _reshaped_dense_dims(dense_dims(P), is_column_major(P), Val{N}(), Val{M}())
end
_reshaped_dense_dims(_, __, ___, ____) = nothing
function _reshaped_dense_dims(dense::D, ::True, ::Val{N}, ::Val{0}) where {D,N}
    if all(dense)
        return _all_dense(Val{N}())
    else
        return nothing
    end
end

"""
    known_strides(::Type{T}[, dim]) -> Tuple

Returns the strides of array `A` known at compile time. Any strides that are not known at
compile time are represented by `nothing`.
"""
known_strides(x, dim) = known_strides(typeof(x), dim)
known_strides(::Type{T}, dim) where {T} = known_strides(T, to_dims(T, dim))
function known_strides(::Type{T}, dim::Integer) where {T}
    # see https://github.com/JuliaLang/julia/blob/6468dcb04ea2947f43a11f556da9a5588de512a0/base/reinterpretarray.jl#L148
    if ndims(T) < dim
        return known_length(T)
    else
        return known_strides(T)[dim]
    end
end

known_strides(x) = known_strides(typeof(x))
known_strides(::Type{T}) where {T<:Vector} = (1,)
function known_strides(::Type{T}) where {T<:MatAdjTrans}
    return permute(known_strides(parent_type(T)), to_parent_dims(T))
end
@inline function known_strides(::Type{T}) where {T<:VecAdjTrans}
    strd = first(known_strides(parent_type(T)))
    return (strd, strd)
end
@inline function known_strides(::Type{T}) where {T<:PermutedDimsArray}
    return permute(known_strides(parent_type(T)), to_parent_dims(T))
end
@inline function known_strides(::Type{T}) where {T<:SubArray}
    return permute(known_strides(parent_type(T)), to_parent_dims(T))
end
function known_strides(::Type{T}) where {T}
    if ndims(T) === 1
        return (1,)
    else
        return size_to_strides(known_size(T), 1)
    end
end

"""
    strides(A) -> Tuple

Returns the strides of array `A`. If any strides are known at compile time,
these should be returned as `Static` numbers. For example:
```julia
julia> A = rand(3,4);

julia> ArrayInterface.strides(A)
(static(1), 3)

Additionally, the behavior differs from `Base.strides` for adjoint vectors:

julia> x = rand(5);

julia> ArrayInterface.strides(x')
(static(1), static(1))

This is to support the pattern of using just the first stride for linear indexing, `x[i]`,
while still producing correct behavior when using valid cartesian indices, such as `x[1,i]`.
```
"""
@inline strides(A::Vector{<:Any}) = (StaticInt(1),)
@inline strides(A::Array{<:Any,N}) where {N} = (StaticInt(1), Base.tail(Base.strides(A))...)
function strides(x)
    if defines_strides(x)
        return size_to_strides(size(x), One())
    else
        return Base.strides(x)
    end
end
@inline bmap(f::F, t::Tuple{}, x::Number) where {F} = ()
@inline bmap(f::F, t::Tuple{T}, x::Number) where {F, T} = (f(first(t),x), )
@inline bmap(f::F, t::Tuple, x::Number) where {F} = (f(first(t),x), bmap(f, Base.tail(t), x)...)
if VERSION ≥ v"1.6.0-DEV.1581"
  @inline @inline function strides(A::Base.ReinterpretArray{R, N, T, B, true}) where {R,N,T,B}
    P = strides(parent(A))
    if sizeof(R) == sizeof(T)
      P
    elseif sizeof(R) > sizeof(T)
      x = Base.tail(P)
      fx = first(x)
      if fx isa Int
        (One(), bmap(Base.sdiv_int, Base.tail(x), fx)...)
      else
        (One(), bmap(÷, Base.tail(x), fx)...)
      end
    else
      (One(), bmap(*, P, StaticInt(sizeof(T)) ÷ StaticInt(sizeof(R)))...)
    end
  end
end
#@inline strides(A) = _strides(A, Base.strides(A), contiguous_axis(A))

strides(::AbstractRange) = (One(),)
function strides(x::VecAdjTrans)
    st = first(strides(parent(x)))
    return (st, st)
end
@inline strides(B::MatAdjTrans) = permute(strides(parent(B)), to_parent_dims(B))
@inline strides(B::PermutedDimsArray) = permute(strides(parent(B)), to_parent_dims(B))

getmul(x::Tuple, y::Tuple, ::StaticInt{i}) where {i} = getfield(x, i) * getfield(y, i)
function strides(A::SubArray)
    return eachop(getmul, to_parent_dims(A), map(maybe_static_step, A.indices), strides(parent(A)))
end

maybe_static_step(x::AbstractRange) = static_step(x)
maybe_static_step(_) = nothing

@generated function size_to_strides(sz::S, init) where {N,S<:Tuple{Vararg{Any,N}}}
    out = Expr(:block, Expr(:meta, :inline))
    t = Expr(:tuple, :init)
    prev = :init
    i = 1
    while i <= (N - 1)
        if S.parameters[i] <: Nothing || (i > 1 &&  t.args[i - 1] === :nothing)
            push!(t.args, :nothing)
        else
            next = Symbol(:val_, i)
            push!(out.args, :($next = $prev * getfield(sz, $i)))
            push!(t.args, next)
            prev = next
        end
        i += 1
    end
    push!(out.args, t)
    return out
end

strides(a, dim) = strides(a, to_dims(a, dim))
function strides(a::A, dim::Integer) where {A}
    if parent_type(A) <: A
        return Base.stride(a, Int(dim))
    else
        return strides(parent(a), to_parent_dims(A, dim))
    end
end

@inline stride(A::AbstractArray, ::StaticInt{N}) where {N} = strides(A)[N]
@inline stride(A::AbstractArray, ::Val{N}) where {N} = strides(A)[N]
stride(A, i) = Base.stride(A, i) # for type stability


text:
include("FindIntersectingPoint.jl")
include("MakeModel.jl")
include("MakeP_bar.jl")
include("MinimalT2.jl")
include("Vert.jl")
include("LMMPSolver.jl")


text:
# License for this file: MIT (expat)
# Copyright 2017-2018, DLR Institute of System Dynamics and Control
#
# This file is part of module
#   Modia3D.ContactDetectionMPR (Modia3D/contactDetection/ContactDetectionMPR/_module.jl)
#

const Point = SVector{3,Float64}

const i16max = Int64(typemax(Int16))
const i32max = Int64(typemax(Int32))

pack16(i1::Integer, i2::Integer) = Int64(i1) + i16max*Int64(i2)

function pack(i1::Integer, i2::Integer,i3::Integer,i4::Integer)
   @assert(i1 >= 0 && i1 <= typemax(Int16))
   @assert(i2 >= 0 && i2 <= typemax(Int16))
   @assert(i3 >= 0 && i3 <= typemax(Int16))
   @assert(i4 >= 0 && i4 <= typemax(Int16))
   return pack16(i1,i2) + i32max*pack16(i3,i4)
end

function unpack16(i::Int64)
  i1 = rem(i,i16max)
  i2 = div(i-i1, i16max)
  return (i1,i2)
end

function unpack32(i::Int64)
  i1 = rem(i,i32max)
  i2 = div(i-i1, i32max)
  return (i1,i2)
end

function unpack(i::Int64)
  @assert(i >= 0 && i <= typemax(Int64))
  (tmp1,tmp2) = unpack32(i)
  (i1,i2) = unpack16(tmp1)
  (i3,i4) = unpack16(tmp2)
  return (i1,i2,i3,i4)
end

# Functions to test properties of portal

"""
    (onPortal, b21, b31) = isPointOnPortal(r4,r1,r2,r3) - Determine whether point r4 is on the portal triangle

- onPortal = true, if r4 is on the portal r1,r2,r3 and false if it is outside of the portal.

- b21, b31 are the barycentric coordinates such that r4 = r1 + b21*(r2-r1) + b31*(r3-r1).
  If r4 is on the portal, b21 >= 0, b31 >= 0 and b21+b31 <= 1
"""
function isPointOnPortal(r4::SVector{3,Float64}, r1::SVector{3,Float64}, r2::SVector{3,Float64}, r3::SVector{3,Float64})
   r21 = r2 - r1
   r31 = r3 - r1
   r41 = r4 - r1

   # Determine normal to triangle (and test that r4 is on the triangle plane)
   n = cross(r21,r31)
   e = normalize(n)
   #@assert(abs(dot(e,r41)) <= 1e-10)

   # Determine normals to r21 and e, as well as r31 and e
   n21 = cross(r21,e)
   n31 = cross(r31,e)

   # Determine barycentric coordinates b21, b31:
   #   n21*(r41 = b21*r21 + b31*r31)    ->  n21*r21 = 0
   #   n31*(r41 = b21*r21 + b31*r31)    ->  n31*r31 = 0
   b21 = dot(n31,r41) / dot(n31,r21)
   b31 = dot(n21,r41) / dot(n21,r31)

   # Determine whether r4 is on the triangle
   onPortal = b21 >= 0.0 && b31 >= 0.0 && b21 + b31 <= 1.0

   return (onPortal, b21, b31)
end

"""
    (r, onPortal, b21, b31) = isNormalRayOnPortal(r1,r2,r3) - Determine whether normal ray through origin is on the portal triangle

- r: Intersection of normal ray through origin with portal plane
- onPortal: r is on the portal triangle
"""
function isNormalRayOnPortal(r1::SVector{3,Float64}, r2::SVector{3,Float64}, r3::SVector{3,Float64})
   # Determine unit normal on portal
   n = cross(r2-r1, r3-r1)
   e = normalize(n)

   # Determine intersection of normal ray through origin with portal plane
   #   Ray:   r = lambda*e
   #   Plane: e*(r-r1) = 0
   #   Intersection point:  e*(lambda*e - r1) = 0 -> lambda = e*r1
   lambda = dot(e,r1)
   r4 = lambda*e

   # Determine whether r is on the portal triangle
   (onPortal, b21, b31) = isPointOnPortal(r4,r1,r2,r3)

   return (onPortal, b21, b31, r4)
end

"""
    (r, onPortal, b21, b31) = isr0RayOnPortal(r0,r1,r2,r3) - Determine whether ray from r0 through origin is on the portal triangle

- r: Intersection of ray through origin with portal plane
- onPortal: r is on the portal triangle
"""
function isr0RayOnPortal(r0::SVector{3,Float64}, r1::SVector{3,Float64}, r2::SVector{3,Float64}, r3::SVector{3,Float64})
   # Determine unit normal on portal
   n = cross(r2-r1, r3-r1)
   e = normalize(n)

   # Determine intersection point of ray with portal plane
   #   Ray:   r = lambda*r0
   #   Plane: e*(r-r1) = 0
   #   Intersection point:  e*(lambda*r0 - r1) = 0 -> lambda = e*r1/(e*r0)
   lambda = dot(e,r1)/dot(e,r0)
   r4 = lambda*r0   # intersection point

   # Determine whether intersection point is on portal
   (onPortal, b21, b31) = isPointOnPortal(r4,r1,r2,r3)

   return (onPortal, b21, b31, r4)
end

"""
    Test functions
"""
function testIntersectionWithPortal()
   # Portal, parallel to x-y plane
   r1 = SVector( 1.0,0.0,2.0)
   r2 = SVector( 0.0,1.0,2.0)
   r3 = SVector(-1.0,-0.1,2.0)

   # Check r0 ray on portal
   r0 = SVector(0.0,0.0,-1.0)
   (onPortal, b21, b31, r4) = isr0RayOnPortal(r0,r1,r2,r3)
   println("onPortal = ", onPortal, ", b21 = ", b21, ", b31 = ", b31, ", r4 = ", r4)

   # Check r0 ray not on portal
   r0 = SVector(2.0,0.0,-1.0)
   (onPortal, b21, b31, r4) = isr0RayOnPortal(r0,r1,r2,r3)
   println("onPortal = ", onPortal, ", b21 = ", b21, ", b31 = ", b31, ", r4 = ", r4)

   # Check normal ray on portal
   (onPortal, b21, b31, r4) = isNormalRayOnPortal(r1,r2,r3)
   println("onPortal = ", onPortal, ", b21 = ", b21, ", b31 = ", b31, ", r4 = ", r4)

end

"""
    (r4,d4) = signedDistanceToLineSegment(r1,r2,r21,e,r4_old=nothing,d4_old=0.0) - Determine closest point on line

- r4: Point on line r1 + lambda*r21 that is closest to the origin with 0<=lambda<=1, or r4_old, if it is closer.
- d4: Signed distance of r4 to the origin or d4_old if abs(d4_old) < norm(r1 + lambda*r21)
"""
function signedDistanceToLineSegment(r1::SVector{3,Float64}, r2::SVector{3,Float64}, r21::SVector{3,Float64}, e::SVector{3,Float64},
                                     r4_old::Union{SVector{3,Float64},NOTHING}=nothing, d4_old::Float64=0.0)
    # r = r1 + lambda*r21  (0 <= lambda <= 1)
    #
    # The closest point to the line is point r4, such that r21*r4 = 0
    # -> r21*(r1+lambda*r12) = 0 -> lambda = -r21*r1/(r21*r21)
    lambda = -dot(r21,r1) / dot(r21,r21)
    r4 = lambda < 0.0 ? r1 : (lambda > 1.0 ? r2 : r1 + lambda*r21)
    d4 = norm(r4)
    if dot(r4,e) > 0.0
       d4 = -d4
    end
    if typeof(r4_old) == NOTHING || abs(d4) <= abs(d4_old)
       return (r4,d4)
    else
       return (r4_old, d4_old)
    end
end

"""
    (r4,d) = signedDistanceToPortal(r0,r1,r2,r3) - Determine closest point on portal

- r4: Point on triangle portal r1, r2, r3 that is closest to the origin.
- d : Signed distance of r4 to the origin: d > 0: origin is outside of tetrahedron r0,r1,r2,r3, otherwise inside.
"""
function signedDistanceToPortal(r0::SVector{3,Float64}, r1::SVector{3,Float64}, r2::SVector{3,Float64}, r3::SVector{3,Float64})
   # Determine unit normal on portal
   r21 = r2 - r1
   r31 = r3 - r1
   n   = cross(r21, r31)
   e   = normalize(n)
   if dot(r0,e) > 0.0
      e = -e
   end

   # Determine intersection of normal ray through origin with portal plane
   #   Ray:   r = lambda*e
   #   Plane: e*(r-r1) = 0
   #   Intersection point:  e*(lambda*e - r1) = 0 -> lambda = e*r1
   lambda = dot(e,r1)
   r4 = lambda*e

   # Determine whether r4 is on the portal triangle by computing its barycentric coordinates
   r41 = r4 - r1
   n21 = cross(r21,e)
   n31 = cross(r31,e)

   # Determine barycentric coordinates b21, b31:
   #   n21*(r41 = b21*r21 + b31*r31)    ->  n21*r21 = 0
   #   n31*(r41 = b21*r21 + b31*r31)    ->  n31*r31 = 0
   b21 = dot(n31,r41) / dot(n31,r21)
   b31 = dot(n21,r41) / dot(n21,r31)

   # Determine whether r4 is on the triangle
   onPortal = b21 >= 0.0 && b31 >= 0.0 && b21 + b31 <= 1.0
   if onPortal
      return (r4, -lambda)
   else
      # println("\n... Contact point outside portal (distance might be not precise)")
   end

   # The closest point is on one of the edges.
   (r4,d4) = signedDistanceToLineSegment(r1,r2,r21  ,e)
   (r4,d4) = signedDistanceToLineSegment(r1,r3,r31  ,e,r4,d4)
   (r4,d4) = signedDistanceToLineSegment(r2,r3,r3-r2,e,r4,d4)

   return (r4,d4)
end

"""
    Test functions
"""
function testSignedDistanceToPortal()
   # Portal, parallel to x-y plane
   r0 = SVector( 0.0,0.0,-2.0)
   r1 = SVector( 1.0,0.0,2.0)
   r2 = SVector( 0.0,1.0,2.0)
   r3 = SVector(-1.0,-0.1,2.0)
   (r4,d4) = signedDistanceToPortal(r0,r1,r2,r3)
   println("r4 = ", r4, ", d4 = ", d4)

   r3 = SVector(2.0,1.5,2.0)
   (r4,d4) = signedDistanceToPortal(r0,r1,r2,r3)
   println("r4 = ", r4, ", d4 = ", d4)

   r0 = SVector(0.0,0.0,+4.0)
   r2 = SVector(0.5,1.0,2.0)
   r3 = SVector(0.5,0.0,2.0)
   (r4,d4) = signedDistanceToPortal(r0,r1,r2,r3)
   println("r4 = ", r4, ", d4 = ", d4)
end

# testSignedDistanceToPortal()


text:
using Images, Colors

function filter_sobel(image::AbstractMatrix, direction::String="-")

  tipo = typeof(image)

  try
    image = convert(Array{Float32}, image)
  catch er
    error(er,"\nProblem converting the image into an array.\n")
  end

  if direction=="-"
    mask_x = [-1 0 1; -2 0 2; -1 0 1];
    mask_y = [1 2 1; 0 0 0; -1 -2 -1];

    Gx = imfilter(image,centered(mask_x));
    Gy = imfilter(image,centered(mask_y));

    d1 = size(Gx);
    d2 = size(Gy);

    if d1!=d2
      error("Problem during the function's execution.")
    end

    img_final = zeros(d1[1],d1[2])

    for i = 1:d1[1]
      for j = 1:d1[2]

        img_final[i,j] = sqrt((Gx[i,j]^2)+(Gy[i,j]^2))

        if img_final[i,j]>1.0
          img_final[i,j]=1.0;
        elseif img_final[i,j]<0.0
          img_final[i,j]=0.0;
        end

      end
    end
elseif direction=="W->E"
    mask = [1 2 1; 0 0 0; -1 -2 -1];
    img_final = imfilter(image,centered(mask));
    d1 = size(img_final);
    for i = 1:d1[1]
      for j = 1:d1[2]
        if img_final[i,j]>1.0
          img_final[i,j]=1.0;
        elseif img_final[i,j]<0.0
          img_final[i,j]=0.0;
        end
      end
    end
elseif direction=="N->S"
    mask = [-1 0 1; -2 0 2; -1 0 1];
    img_final = imfilter(image,centered(mask));
    d1 = size(img_final);
    for i = 1:d1[1]
      for j = 1:d1[2]
        if img_final[i,j]>1.0
          img_final[i,j]=1.0;
        elseif img_final[i,j]<0.0
          img_final[i,j]=0.0;
        end
      end
    end
  elseif direction=="NW->SE"
    mask = [0 1 2; -1 0 1; -2 -1 0];
    img_final = imfilter(image,centered(mask));
    d1 = size(img_final);
    for i = 1:d1[1]
      for j = 1:d1[2]
        if img_final[i,j]>1.0
          img_final[i,j]=1.0;
        elseif img_final[i,j]<0.0
          img_final[i,j]=0.0;
        end
      end
    end
  elseif direction=="SW->NE"
    mask = [-2 -1 0; -1 0 1; 0 1 2];
    img_final = imfilter(image,centered(mask));
    d1 = size(img_final);
    for i = 1:d1[1]
      for j = 1:d1[2]
        if img_final[i,j]>1.0
          img_final[i,j]=1.0;
        elseif img_final[i,j]<0.0
          img_final[i,j]=0.0;
        end
      end
    end
  else
    error("The position value must be an Text variable. Available values are: W->E, N->S, NW->SE, and SW->NE. This is not a mandatory field.\n");
  end

  img_final = convert(tipo, img_final)
  return(img_final)
end


text:
export DistModel, feed!, fix!

const DistVocabulary = Dict{Symbol, Vector{Float64}}

mutable struct DistModel <: Model
    config::TextConfig
    tokens::DistVocabulary
    sizes::Vector{Int}
    initial_dist::Vector{Float64}
    m::Int
    n::Int
end

const EMPTY_TOKEN_DIST = Int[]

"""
    fit(::Type{DistModel}, config::TextConfig, corpus, y; nclasses=0, weights=nothing, fix=true)

Creates a DistModel object using the specified `corpus` (an array of strings or an array of arrays of strings);
and its associated labels `y`. Optional parameters:
- `nclasses`: the number of classes
- `weights`: It has three different kind of values
   - an array of `nclasses` floating point to scale the value of each bin in the computed histogram.
   - the keyword :balance` that indicates that `weights` must try to compensate the unbalance among classes
   - nothing: let the computed histogram untouched
- `fix`: if true, it stores the empirical probabilities instead of frequencies
"""
function fit(::Type{DistModel}, config::TextConfig, corpus, y; nclasses=0, weights=:balance, fix=false, smooth::Real=0)
    if nclasses == 0
        nclasses = unique(y) |> length
    end
    smooth = fill(convert(Float64, smooth), nclasses)
    counters = zeros(Int, nclasses)
    model = DistModel(config, DistVocabulary(), counters, smooth, 0, 0)
    feed!(model, corpus, y)
    if weights == :balance
        s = sum(model.sizes)
        weights = [s / x  for x in model.sizes]
    end

    if weights !== nothing
        normalize!(model, weights)
    end

    if fix
        fix!(model)
    end

    model
end

"""
    feed!(model::DistModel, corpus, y)

DistModel objects support for incremental feed if `fix!` method is not called on `fit`
"""
function feed!(model::DistModel, corpus, y)
    config = model.config
    nclasses = length(model.sizes)

    for (klass, text) in zip(y, corpus)
        for token in tokenize(config, text)
            token_dist = get(model.tokens, token, EMPTY_TOKEN_DIST)
            if length(token_dist) == 0
                token_dist = copy(model.initial_dist)
                model.tokens[token] = token_dist
            end
            token_dist[klass] += 1
        end

        model.sizes[klass] += 1
    end

    model.n += length(corpus)
    model.m = length(model.tokens)
    model
end

"""
    normalize!(model::DistModel, weights)

Multiply weights in each histogram, e.g., looking for compensating unbalance
"""
function normalize!(model::DistModel, weights)
    nclasses = length(model.sizes)

    for (token, hist) in model.tokens
        for i in 1:nclasses
            hist[i] *= weights[i]
        end
    end
end

"""
    fix!(model::DistModel)

Replaces frequencies by empirical probabilities in the model
"""
function fix!(model::DistModel)
    nclasses = length(model.sizes)

    for (token, dist) in model.tokens
        s = sum(dist)
        for i in 1:nclasses
            dist[i] /= s
        end

    end

    model
end