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

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

function _swaggerinternal_createSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", IoK8sApiSettingsV1alpha1PodPreset, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

create a PodPreset
Param: namespace::String (required)
Param: body::IoK8sApiSettingsV1alpha1PodPreset (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Return: IoK8sApiSettingsV1alpha1PodPreset
"""
function createSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_createSettingsV1alpha1NamespacedPodPreset(_api, namespace, body; pretty=pretty, dryRun=dryRun, fieldManager=fieldManager, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function createSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_createSettingsV1alpha1NamespacedPodPreset(_api, namespace, body; pretty=pretty, dryRun=dryRun, fieldManager=fieldManager, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_deleteSettingsV1alpha1CollectionNamespacedPodPreset(_api::SettingsV1alpha1Api, namespace::String; pretty=nothing, allowWatchBookmarks=nothing, body=nothing, __continue__=nothing, dryRun=nothing, fieldSelector=nothing, gracePeriodSeconds=nothing, labelSelector=nothing, limit=nothing, orphanDependents=nothing, propagationPolicy=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "DELETE", IoK8sApimachineryPkgApisMetaV1Status, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "allowWatchBookmarks", allowWatchBookmarks)  # type Bool
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "gracePeriodSeconds", gracePeriodSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "orphanDependents", orphanDependents)  # type Bool
    Swagger.set_param(_ctx.query, "propagationPolicy", propagationPolicy)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

delete collection of PodPreset
Param: namespace::String (required)
Param: pretty::String
Param: allowWatchBookmarks::Bool
Param: body::IoK8sApimachineryPkgApisMetaV1DeleteOptions
Param: __continue__::String
Param: dryRun::String
Param: fieldSelector::String
Param: gracePeriodSeconds::Int32
Param: labelSelector::String
Param: limit::Int32
Param: orphanDependents::Bool
Param: propagationPolicy::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1Status
"""
function deleteSettingsV1alpha1CollectionNamespacedPodPreset(_api::SettingsV1alpha1Api, namespace::String; pretty=nothing, allowWatchBookmarks=nothing, body=nothing, __continue__=nothing, dryRun=nothing, fieldSelector=nothing, gracePeriodSeconds=nothing, labelSelector=nothing, limit=nothing, orphanDependents=nothing, propagationPolicy=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deleteSettingsV1alpha1CollectionNamespacedPodPreset(_api, namespace; pretty=pretty, allowWatchBookmarks=allowWatchBookmarks, body=body, __continue__=__continue__, dryRun=dryRun, fieldSelector=fieldSelector, gracePeriodSeconds=gracePeriodSeconds, labelSelector=labelSelector, limit=limit, orphanDependents=orphanDependents, propagationPolicy=propagationPolicy, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deleteSettingsV1alpha1CollectionNamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, namespace::String; pretty=nothing, allowWatchBookmarks=nothing, body=nothing, __continue__=nothing, dryRun=nothing, fieldSelector=nothing, gracePeriodSeconds=nothing, labelSelector=nothing, limit=nothing, orphanDependents=nothing, propagationPolicy=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deleteSettingsV1alpha1CollectionNamespacedPodPreset(_api, namespace; pretty=pretty, allowWatchBookmarks=allowWatchBookmarks, body=body, __continue__=__continue__, dryRun=dryRun, fieldSelector=fieldSelector, gracePeriodSeconds=gracePeriodSeconds, labelSelector=labelSelector, limit=limit, orphanDependents=orphanDependents, propagationPolicy=propagationPolicy, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_deleteSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String; pretty=nothing, body=nothing, dryRun=nothing, gracePeriodSeconds=nothing, orphanDependents=nothing, propagationPolicy=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "DELETE", IoK8sApimachineryPkgApisMetaV1Status, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets/{name}", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "gracePeriodSeconds", gracePeriodSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "orphanDependents", orphanDependents)  # type Bool
    Swagger.set_param(_ctx.query, "propagationPolicy", propagationPolicy)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

delete a PodPreset
Param: name::String (required)
Param: namespace::String (required)
Param: pretty::String
Param: body::IoK8sApimachineryPkgApisMetaV1DeleteOptions
Param: dryRun::String
Param: gracePeriodSeconds::Int32
Param: orphanDependents::Bool
Param: propagationPolicy::String
Return: IoK8sApimachineryPkgApisMetaV1Status
"""
function deleteSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String; pretty=nothing, body=nothing, dryRun=nothing, gracePeriodSeconds=nothing, orphanDependents=nothing, propagationPolicy=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deleteSettingsV1alpha1NamespacedPodPreset(_api, name, namespace; pretty=pretty, body=body, dryRun=dryRun, gracePeriodSeconds=gracePeriodSeconds, orphanDependents=orphanDependents, propagationPolicy=propagationPolicy, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function deleteSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, name::String, namespace::String; pretty=nothing, body=nothing, dryRun=nothing, gracePeriodSeconds=nothing, orphanDependents=nothing, propagationPolicy=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_deleteSettingsV1alpha1NamespacedPodPreset(_api, name, namespace; pretty=pretty, body=body, dryRun=dryRun, gracePeriodSeconds=gracePeriodSeconds, orphanDependents=orphanDependents, propagationPolicy=propagationPolicy, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_getSettingsV1alpha1APIResources(_api::SettingsV1alpha1Api; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1APIResourceList, "/apis/settings.k8s.io/v1alpha1/", ["BearerToken"])
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"] : [_mediaType])
    return _ctx
end

"""

get available resources
Return: IoK8sApimachineryPkgApisMetaV1APIResourceList
"""
function getSettingsV1alpha1APIResources(_api::SettingsV1alpha1Api; _mediaType=nothing)
    _ctx = _swaggerinternal_getSettingsV1alpha1APIResources(_api; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function getSettingsV1alpha1APIResources(_api::SettingsV1alpha1Api, response_stream::Channel; _mediaType=nothing)
    _ctx = _swaggerinternal_getSettingsV1alpha1APIResources(_api; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_listSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, namespace::String; pretty=nothing, allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiSettingsV1alpha1PodPresetList, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets", ["BearerToken"])
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "allowWatchBookmarks", allowWatchBookmarks)  # type Bool
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

list or watch objects of kind PodPreset
Param: namespace::String (required)
Param: pretty::String
Param: allowWatchBookmarks::Bool
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApiSettingsV1alpha1PodPresetList
"""
function listSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, namespace::String; pretty=nothing, allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_listSettingsV1alpha1NamespacedPodPreset(_api, namespace; pretty=pretty, allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function listSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, namespace::String; pretty=nothing, allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_listSettingsV1alpha1NamespacedPodPreset(_api, namespace; pretty=pretty, allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_listSettingsV1alpha1PodPresetForAllNamespaces(_api::SettingsV1alpha1Api; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiSettingsV1alpha1PodPresetList, "/apis/settings.k8s.io/v1alpha1/podpresets", ["BearerToken"])
    Swagger.set_param(_ctx.query, "allowWatchBookmarks", allowWatchBookmarks)  # type Bool
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

list or watch objects of kind PodPreset
Param: allowWatchBookmarks::Bool
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApiSettingsV1alpha1PodPresetList
"""
function listSettingsV1alpha1PodPresetForAllNamespaces(_api::SettingsV1alpha1Api; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_listSettingsV1alpha1PodPresetForAllNamespaces(_api; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function listSettingsV1alpha1PodPresetForAllNamespaces(_api::SettingsV1alpha1Api, response_stream::Channel; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_listSettingsV1alpha1PodPresetForAllNamespaces(_api; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_patchSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, force=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PATCH", IoK8sApiSettingsV1alpha1PodPreset, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets/{name}", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_param(_ctx.query, "force", force)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json-patch+json", "application/merge-patch+json", "application/strategic-merge-patch+json", "application/apply-patch+yaml"] : [_mediaType])
    return _ctx
end

"""

partially update the specified PodPreset
Param: name::String (required)
Param: namespace::String (required)
Param: body::IoK8sApimachineryPkgApisMetaV1Patch (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Param: force::Bool
Return: IoK8sApiSettingsV1alpha1PodPreset
"""
function patchSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, force=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_patchSettingsV1alpha1NamespacedPodPreset(_api, name, namespace, body; pretty=pretty, dryRun=dryRun, fieldManager=fieldManager, force=force, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function patchSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, force=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_patchSettingsV1alpha1NamespacedPodPreset(_api, name, namespace, body; pretty=pretty, dryRun=dryRun, fieldManager=fieldManager, force=force, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_readSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String; pretty=nothing, exact=nothing, __export__=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiSettingsV1alpha1PodPreset, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets/{name}", ["BearerToken"])
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "exact", exact)  # type Bool
    Swagger.set_param(_ctx.query, "export", __export__)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

read the specified PodPreset
Param: name::String (required)
Param: namespace::String (required)
Param: pretty::String
Param: exact::Bool
Param: __export__::Bool
Return: IoK8sApiSettingsV1alpha1PodPreset
"""
function readSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String; pretty=nothing, exact=nothing, __export__=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_readSettingsV1alpha1NamespacedPodPreset(_api, name, namespace; pretty=pretty, exact=exact, __export__=__export__, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function readSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, name::String, namespace::String; pretty=nothing, exact=nothing, __export__=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_readSettingsV1alpha1NamespacedPodPreset(_api, name, namespace; pretty=pretty, exact=exact, __export__=__export__, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_replaceSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PUT", IoK8sApiSettingsV1alpha1PodPreset, "/apis/settings.k8s.io/v1alpha1/namespaces/{namespace}/podpresets/{name}", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

replace the specified PodPreset
Param: name::String (required)
Param: namespace::String (required)
Param: body::IoK8sApiSettingsV1alpha1PodPreset (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Return: IoK8sApiSettingsV1alpha1PodPreset
"""
function replaceSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_replaceSettingsV1alpha1NamespacedPodPreset(_api, name, namespace, body; pretty=pretty, dryRun=dryRun, fieldManager=fieldManager, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function replaceSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_replaceSettingsV1alpha1NamespacedPodPreset(_api, name, namespace, body; pretty=pretty, dryRun=dryRun, fieldManager=fieldManager, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_watchSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1WatchEvent, "/apis/settings.k8s.io/v1alpha1/watch/namespaces/{namespace}/podpresets/{name}", ["BearerToken"])
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "allowWatchBookmarks", allowWatchBookmarks)  # type Bool
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

watch changes to an object of kind PodPreset. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.
Param: name::String (required)
Param: namespace::String (required)
Param: allowWatchBookmarks::Bool
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1WatchEvent
"""
function watchSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, name::String, namespace::String; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_watchSettingsV1alpha1NamespacedPodPreset(_api, name, namespace; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function watchSettingsV1alpha1NamespacedPodPreset(_api::SettingsV1alpha1Api, response_stream::Channel, name::String, namespace::String; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_watchSettingsV1alpha1NamespacedPodPreset(_api, name, namespace; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_watchSettingsV1alpha1NamespacedPodPresetList(_api::SettingsV1alpha1Api, namespace::String; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1WatchEvent, "/apis/settings.k8s.io/v1alpha1/watch/namespaces/{namespace}/podpresets", ["BearerToken"])
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "allowWatchBookmarks", allowWatchBookmarks)  # type Bool
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

watch individual changes to a list of PodPreset. deprecated: use the 'watch' parameter with a list operation instead.
Param: namespace::String (required)
Param: allowWatchBookmarks::Bool
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1WatchEvent
"""
function watchSettingsV1alpha1NamespacedPodPresetList(_api::SettingsV1alpha1Api, namespace::String; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_watchSettingsV1alpha1NamespacedPodPresetList(_api, namespace; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function watchSettingsV1alpha1NamespacedPodPresetList(_api::SettingsV1alpha1Api, response_stream::Channel, namespace::String; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_watchSettingsV1alpha1NamespacedPodPresetList(_api, namespace; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

function _swaggerinternal_watchSettingsV1alpha1PodPresetListForAllNamespaces(_api::SettingsV1alpha1Api; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1WatchEvent, "/apis/settings.k8s.io/v1alpha1/watch/podpresets", ["BearerToken"])
    Swagger.set_param(_ctx.query, "allowWatchBookmarks", allowWatchBookmarks)  # type Bool
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    return _ctx
end

"""

watch individual changes to a list of PodPreset. deprecated: use the 'watch' parameter with a list operation instead.
Param: allowWatchBookmarks::Bool
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1WatchEvent
"""
function watchSettingsV1alpha1PodPresetListForAllNamespaces(_api::SettingsV1alpha1Api; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_watchSettingsV1alpha1PodPresetListForAllNamespaces(_api; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function watchSettingsV1alpha1PodPresetListForAllNamespaces(_api::SettingsV1alpha1Api, response_stream::Channel; allowWatchBookmarks=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = _swaggerinternal_watchSettingsV1alpha1PodPresetListForAllNamespaces(_api; allowWatchBookmarks=allowWatchBookmarks, __continue__=__continue__, fieldSelector=fieldSelector, labelSelector=labelSelector, limit=limit, pretty=pretty, resourceVersion=resourceVersion, timeoutSeconds=timeoutSeconds, watch=watch, _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

export createSettingsV1alpha1NamespacedPodPreset, deleteSettingsV1alpha1CollectionNamespacedPodPreset, deleteSettingsV1alpha1NamespacedPodPreset, getSettingsV1alpha1APIResources, listSettingsV1alpha1NamespacedPodPreset, listSettingsV1alpha1PodPresetForAllNamespaces, patchSettingsV1alpha1NamespacedPodPreset, readSettingsV1alpha1NamespacedPodPreset, replaceSettingsV1alpha1NamespacedPodPreset, watchSettingsV1alpha1NamespacedPodPreset, watchSettingsV1alpha1NamespacedPodPresetList, watchSettingsV1alpha1PodPresetListForAllNamespaces


text:
module AMP

using ASE
using JuLIP
using PyCall

function __init__()
    py"""
    import math
    import numpy as np
    import itertools

    from amp import Amp
    from amp.descriptor.gaussian import Gaussian, make_symmetry_functions
    from amp.descriptor.gaussian import NeighborlistCalculator, FingerprintCalculator
    from amp.descriptor.gaussian import calculate_G2, calculate_G4, Cosine

    import ase
    from ase.build import bulk
    from ase.neighborlist import neighbor_list
    from ase.data import atomic_numbers, chemical_symbols

    PI = 3.14159265359
    unit_BOHR = 0.52917721067

    # DEFINITONS FOR SUPPORT FUNCTIONS
    # Of course these are slower functions than the ones in Fortran library
    # -- ahem -- but these versions are not in the Fortran library unless they
    #   are also added to the library and Fortran library is compiled in AENET/AEPY.
    def dict2cutoff(dct):
        return globals()[dct['name']](**dct['kwargs'])

    class Cosine(object):
        def __init__(self, Rc):

            self.Rc = Rc

        def __call__(self, Rij):
            if isinstance(Rij, np.ndarray):
                rtn = np.zeros(len(Rij), dtype=np.float)
                cutoff_indices = Rij > self.Rc
                rtn[np.invert(cutoff_indices)] =  0.5 * (np.cos(
                    np.pi * Rij[np.invert(cutoff_indices)] / self.Rc) + 1.)
                return rtn
            else:
                if Rij > self.Rc:
                    return 0.
                else:
                    return 0.5 * (np.cos(np.pi * Rij / self.Rc) + 1.)

        def prime(self, Rij):
            if isinstance(Rij, np.ndarray):
                rtn = np.zeros(len(Rij), dtype=np.float)
                cutoff_indices = Rij > self.Rc
                rtn[np.invert(cutoff_indices)] =  -0.5 * np.pi / self.Rc * np.sin(
                        np.pi * Rij[np.invert(cutoff_indices)] / self.Rc)
                return rtn
            else:
                if Rij > self.Rc:
                    return 0.
                else:
                    return -0.5 * np.pi / self.Rc * np.sin(np.pi * Rij / self.Rc)

        def todict(self):
            return {'name': 'Cosine',
                    'kwargs': {'Rc': self.Rc}}

        def __repr__(self):
            return ('<Cosine cutoff with Rc=%.3f from amp.descriptor.cutoffs>'
                    % self.Rc)

    class Tanhyper3(object):
        def __init__(self, Rc):

            self.Rc = Rc

        def __call__(self, Rij):
            if isinstance(Rij, np.ndarray):
                rtn = np.zeros(len(Rij), dtype=np.float)
                cutoff_indices = Rij > self.Rc
                rtn[np.invert(cutoff_indices)] = (np.tanh(1. - (Rij[np.invert(cutoff_indices)] / self.Rc)))**3
                return rtn
            else:
                if Rij > self.Rc:
                    return 0.
                else:
                    return (np.tanh(1. - (Rij / self.Rc)))**3

        def prime(self, Rij):
            if Rij > self.Rc:
                return 0.
            else:
                in_tanh = np.tanh(1. - (Rij / self.Rc))
                return 3. * in_tanh**2 * (1. - in_tanh)**2

        def todict(self):
            return {'name': 'Tanhyper3',
                    'kwargs': {'Rc': self.Rc}}

        def __repr__(self):
            return ('<Tanhyper3 cutoff with Rc=%.3f from DescriptorZoo>'
                    % self.Rc)

    def get_fingerprint(self, index, symbol,
                        neighborsymbols, neighborpositions):
        Ri = self.atoms[index].position

        num_symmetries = len(self.globals.Gs[symbol])
        neighbornumbers = [atomic_numbers[s] for s in neighborsymbols]
        fingerprint = [None] * num_symmetries

        for count in range(num_symmetries):
            G = self.globals.Gs[symbol][count]

            if G['type'] == 'G2':
                ridge = calculate_G2(neighbornumbers, neighborsymbols,
                                     neighborpositions,
                                     G['element'], G['eta'], G['Rs'],
                                     G['Rc'], Ri, self.fortran)
            elif G['type'] == 'G4':
                ridge = calculate_G4(neighbornumbers, neighborsymbols,
                                     neighborpositions,
                                     G['elements'], G['gamma'],
                                     G['zeta'], G['eta'], G['Rc'],
                                     Ri, self.fortran)
            else:
                raise NotImplementedError('Unknown G type: %s' % G['type'])
            fingerprint[count] = ridge

        return symbol, fingerprint

    def _get_key_values_list(data, find_key=None):
        rv = []
        founds = []
        for item in data:
            found = False
            if isinstance(item, list):
                found, item = _get_key_values_list(item, find_key=find_key)
            elif isinstance(item, dict):
                found, item = _get_key_values_dict(item, find_key=find_key)
            if found:
                founds.append(found)
                rv.extend(item)
        if True in founds:
            found = True
        return found, list(set(rv))

    def _get_key_values_dict(data, find_key=None):
        rv = []
        founds = {}
        for key, value in data.items():
            found = False
            founds[key] = False
            if isinstance(value, list):
                found, value = _get_key_values_list(value, find_key=find_key)
            elif isinstance(value, dict):
                found, value = _get_key_values_dict(value, find_key=find_key)
            if found or find_key == key:
                founds[key] = True
            if founds[key]:
                if isinstance(value, list):
                    rv.extend(value)
                else:
                    rv.append(value)
        if True in founds.values():
            found = True
        return found, list(set(rv))

    def calc_desc(ase_database, desc, Gs, max_cutoff=None):
        out_desc = []
        found, cutoffs_list = _get_key_values_dict(Gs, find_key='Rc')
        if found:
            max_cutoff = np.max(np.asarray(cutoffs_list))
        else:
            if max_cutoff is None:
                max_cutoff = 12.
        for count, ase_structure in enumerate(ase_database):
            # Calculate descriptor
            amp_desc = []
            desc_struct = {1:ase_structure}
            amp_out = amp_gaussian_desc_calc(desc, desc_struct)
            for amp_item in amp_out[0]:
                amp_desc.append(amp_item[1])
            rtn_desc = np.array(amp_desc)
            out_desc.append({
                'descriptors' : rtn_desc,
                'symbols'     : ase_structure.get_chemical_symbols()
                })
        return out_desc

    def set_symmetry_functions(elements, gtype, etas, Rs=None, Rc_scale=None,
                               zetas=None, gammas=None, Rc=None):
        if Rc is None:
            Rc = 6.5
        if isinstance(Rc, (float, int)):
            set_cutoff_type = Cosine(Rc)
            Rc = set_cutoff_type.todict()

        if gtype == 'G2':
            G = [{'type': 'G2',
                  'element': element,
                  'eta': float(eta),
                  'Rs' : float(Rs[ei]),
                  'Rc_scale': Rc_scale[ei],
                  'offset' : float(Rs[ei]),
                  'cutoff': Rc,
                  'Rc': Rc}
                 if Rs is not None and Rc_scale is not None
                 else
                 {'type': 'G2',
                  'element': element,
                  'eta': float(eta),
                  'Rs' : float(Rs[ei]),
                  'Rc_scale': False,
                  'offset' : float(Rs[ei]),
                  'cutoff': Rc,
                  'Rc': Rc}
                 if Rs is not None
                 else
                 {'type': 'G2',
                  'element': element,
                  'eta': float(eta),
                  'Rs' : 0.,
                  'Rc_scale': Rc_scale[ei],
                  'offset' : 0.,
                  'cutoff': Rc,
                  'Rc': Rc}
                 if Rc_scale is not None
                 else
                 {'type': 'G2',
                  'element': element,
                  'eta': float(eta),
                  'Rs' : 0.,
                  'Rc_scale': False,
                  'offset' : 0.,
                  'cutoff': Rc,
                  'Rc': Rc}
                 for ei, eta in enumerate(etas)
                 for element in elements]
            return G
        elif gtype == 'G4' or gtype == 'G5':
            G = []
            for eta in etas:
                for zeta in zetas:
                    for gamma in gammas:
                        for i1, el1 in enumerate(elements):
                            for el2 in elements[i1:]:
                                els = sorted([el1, el2])
                                G.append({'type': gtype,
                                          'elements': els,
                                          'eta': float(eta),
                                          'gamma': float(gamma),
                                          'zeta': int(zeta),
                                          'cutoff': Rc,
                                          'Rc': Rc})
            return G
        raise NotImplementedError('Unknown type: {}.'.format(gtype))

    # DEFINITONS FOR SUPPORT FUNCTIONS
    def amp_fingerprint_calc(atom, image, f_calc, n_list):
        symbol = atom.symbol
        index = atom.index
        neighborindices, neighboroffsets = n_list[index]
        neighborsymbols = [image[_].symbol for _ in neighborindices]
        neighborpositions = \
            [image.positions[neighbor] + np.dot(offset, image.cell)
                for (neighbor, offset) in zip(neighborindices,
                                              neighboroffsets)]
        return f_calc.get_fingerprint(
            index, symbol, neighborsymbols, neighborpositions)

    def amp_gaussian_desc_calc(amp_desc, image_dict, desc_atoms=[]):
        allfingerprints = []
        for key, image in image_dict.items():
            n_calc = NeighborlistCalculator(cutoff=amp_desc.parameters.cutoff['kwargs']['Rc'])
            neighborlist = {key: n_calc.calculate(image, key)}
            f_calc = FingerprintCalculator(neighborlist=neighborlist,
                                           Gs=amp_desc.parameters.Gs,
                                           cutoff=amp_desc.parameters.cutoff,
                                           fortran=amp_desc.fortran)
            f_calc.atoms = image
            fingerprints = []
            if desc_atoms:
                for atom_index in desc_atoms:
                    fingerprints.append(amp_fingerprint_calc(image[atom_index], image, f_calc, neighborlist[key]))
            else:
                for atom in image:
                    fingerprints.append(amp_fingerprint_calc(atom, image, f_calc, neighborlist[key]))

            allfingerprints.append(fingerprints)
        return allfingerprints

    def generate_sf_parameters(n=5, cutoff=6.0, bohr=False):
        #Default values: cutoff = 6.0
        # n = 5 , number of intervals
        if bohr:
            cutoff = cutoff/unit_BOHR
        m = np.array(range(n+1), dtype=np.float32)
        n_pow = n**(m/n)
        eta_m = (n_pow/cutoff)**2
        R_s = cutoff/n_pow
        eta_s = np.zeros(n+1)
        for mi in range(n):
            eta_s[mi] = 1. / (R_s[n - mi] - R_s[n - mi - 1])**2
        # !!!!!! FLIPED eta_s ARRAY HERE !!!! REVERSED ORDER NOT MENTIONED IN PAPER !!!!!!
        # ALSO R_s and eta_s shifted !!! NOT MENTIONED AGAIN !!!
        return eta_m, R_s[1:n+1], np.flip(eta_s[0:n],0)

    def get_all_symbols(ase_data):
        symbols = []
        all_elements = list(itertools.chain.from_iterable([
            m.numbers for m in ase_data]))  # flattened list of all proton numbers Z in the dataset
        numbers = np.unique(all_elements)
        bins = np.bincount(all_elements)
        fractions = np.asarray([float(bin_num)/float(len(all_elements)) for bin_num in bins])
        for num in numbers:
            symbols.append(chemical_symbols[num])
        return symbols, numbers, fractions

    def set_SF_paramaters_of_AMP_G(typeslist, G_types=None, G_settings=None, G_cutoff_types=None,
                                   Behler2011=False, cuttype='Tanhyper3', AMPformat=True):
        if Behler2011:
            max_cutoff = 6.5
            sympack = [
                np.array([   9.,  100.,  200.,  350.,  600., 1000., 2000., 4000.]),
                np.array([   1.,   1.,   1.,   1.,  30.,  30.,  30.,  30.,  80.,  80.,  80.,  80.,
                           150., 150., 150., 150., 150., 150., 150., 150., 250., 250., 250., 250.,
                           250., 250., 250., 250., 450., 450., 450., 450., 450., 450., 450., 450.,
                           800., 800., 800., 800., 800., 800., 800.]),
                np.array([  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,
                            -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,
                            -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,  -1.,   1.,
                            -1.,   1.,  -1.,   1.,  -1.,   1.,   1.]),
                np.array([   1.,   1.,   2.,   2.,   1.,   1.,   2.,   2.,   1.,   1.,   2.,   2.,
                             1.,   1.,   2.,   2.,   4.,   4.,  16.,  16.,   1.,   1.,   2.,   2.,
                             4.,   4.,  16.,  16.,   1.,   1.,   2.,   2.,   4.,   4.,  16.,  16.,
                             1.,   1.,   2.,   2.,   4.,   4.,  16.])
                ]
            if AMPformat:
                amp_r_etas = (max_cutoff*max_cutoff)*sympack[0]/(10000.0*unit_BOHR*unit_BOHR)
                amp_a_etas = (max_cutoff*max_cutoff)*sympack[1]/(10000.0*unit_BOHR*unit_BOHR)
            else:
                amp_r_etas = sympack[0]/(10000.0*unit_BOHR*unit_BOHR)
                amp_a_etas = sympack[1]/(10000.0*unit_BOHR*unit_BOHR)
            G = set_symmetry_functions(elements=typeslist, gtype='G2',
                                       etas=amp_r_etas)
            ang_typ = 'G4'
            for ang_i in range(len(amp_a_etas)):
                G += set_symmetry_functions(elements=typeslist, gtype=ang_typ,
                                            etas=[amp_a_etas[ang_i]],
                                            zetas=[sympack[3][ang_i]],
                                            gammas=[sympack[2][ang_i]])

        else:
            if G_types is None:
                rad_typ = 'G2'
                ang_typ = 'G4'
                G_types = [rad_typ, rad_typ, rad_typ,
                           rad_typ, rad_typ, rad_typ,
                           ang_typ,
                           ang_typ]
            if G_settings is None:
                G_settings = [[8, 4., 0], [8, 8., 0], [8, 12., 0],
                              [8, 4., 1], [8, 8., 1], [8, 12., 1],
                              [8, 4., [1, 2, 4, 8, 16], [-1.0, 1.0]],
                              [8, 8., [1, 2, 4],        [-1.0, 1.0]]]
            if G_cutoff_types is None:
                G_cutoff_types = [cuttype, cuttype, cuttype,
                                  cuttype, cuttype, cuttype,
                                  cuttype, cuttype]

            G = None

            max_cutoff = 0.0
            for Gi, Gtyp in enumerate(G_types):
                cutf = G_settings[Gi][1]*unit_BOHR
                if cutf > max_cutoff:
                    max_cutoff = cutf
                set_eta_m, set_R_s, set_eta_s = generate_sf_parameters(n=G_settings[Gi][0],
                                                                       cutoff=cutf)
                if 'Tanhyper3' in G_cutoff_types[Gi]:
                    set_cutoff_type = Tanhyper3(cutf)
                    set_cutoff = set_cutoff_type.todict()
                else:
                    set_cutoff = cutf

                Gadd = None
                if 'G2' in Gtyp:
                    if G_settings[Gi][2] < 1:
                        Gadd = set_symmetry_functions(elements=typeslist, gtype='G2',
                                                      etas=set_eta_m, Rc=set_cutoff)
                    else:
                        Gadd = set_symmetry_functions(elements=typeslist, gtype='G2',
                                                      etas=set_eta_s,
                                                      Rs=set_R_s,
                                                      Rc=set_cutoff)
                elif 'G4' in Gtyp:
                        Gadd = set_symmetry_functions(elements=typeslist, gtype=Gtyp,
                                                      etas=set_eta_m,
                                                      zetas=G_settings[Gi][2],
                                                      gammas=G_settings[Gi][3],
                                                      Rc=set_cutoff)
                else:
                    print('WARNING! Unrecognized SF G type : ',Gtyp)

                if G is None:
                    G = Gadd
                else:
                    G += Gadd

        # Use same descriptor setting for all species
        GG = {}
        for element in typeslist:
            if G is not None:
                GG.update({element : G})

        return GG, max_cutoff

    def AMP_ACSF_desc(at,
                      Behler2011=False,
                      AMPformat=False,
                      cut_type='Tanhyper3'):
        all_syms, all_nums, all_fractions = get_all_symbols([at])

        G, max_cutoff = set_SF_paramaters_of_AMP_G(all_syms, Behler2011=Behler2011,
                                                   cuttype=cut_type, AMPformat=AMPformat)

        desc = Gaussian(cutoff=max_cutoff, Gs=G, elements=all_syms, fortran=True)
        fingerprints = calc_desc([at], desc, G)
        return fingerprints[0]['descriptors']
    """
end

# To Do: add it as a package
#amppy = pyimport("AMP.py")

export amp_acsf

function amp_acsf(at; Behler2011=true, AMPformat=true, cuttype="Cosine")
    atom_struct = ASEAtoms(at)
    # Calculate descriptor
    acsf_desc = py"AMP_ACSF_desc"(atom_struct.po,
                                  Behler2011=Behler2011,
                                  AMPformat=AMPformat,
                                  cut_type=cuttype)
    return acsf_desc
end

end # module

ext: jl


text:
using Test
using SimpleTools

sq = Dict{Int,Int}()
for k=1:10
    sq[k] = k*k
end

rt = Dict{Int,Int}()
for k=1:10
    rt[k*k] = k
end

f = rt*sq
for k=1:10
    @test f[k] == k
end

y1 = [x^2 for x=0:.01:3]
y2 = [mod(y,1) for y in y1 ]
make_continuous!(y2,1)
@test y1==y2

@test flush_print(23,5) == "   23"
@test flush_print(23,5,false) == "23   "


text:
using Test
using ImageIO
using FileIO: File, DataFormat, Stream, @format_str
using ImageCore: N0f8, RGB, Gray, RGBA, GrayA

tmpdir = mktempdir()
Threads.nthreads() <= 1 && @info "Threads.nthreads() = $(Threads.nthreads()), multithread tests will be disabled"
@testset "ImageIO" begin

    @testset "PNGs" begin
        if Threads.nthreads() > 1 ## NOTE: This test must go first, to test that PNGFiles loading behaves correctly
            @testset "Threaded save" begin
                # test that loading of PNGFiles happens sequentially and doesn't segfault
                img = rand(UInt8, 10, 10)
                Threads.@threads for i in 1:Threads.nthreads()
                    f = File{DataFormat{:PNG}}(joinpath(tmpdir, "test_fpath_$i.png"))
                    ImageIO.save(f, img)
                end
            end
        end
        for typ in [UInt8, N0f8, Gray{N0f8}, RGB{N0f8}] # TODO: Fix 0:1, Gray{Float64}, RGB{Float64} in PNGFiles
            @testset "$typ PNG" begin
                img = rand(typ, 10, 10)
                f = File{DataFormat{:PNG}}(joinpath(tmpdir, "test_fpath.png"))
                ImageIO.save(f, img)
                img_saveload = ImageIO.load(f)
                if typ == UInt8
                    @test all(img .== reinterpret(UInt8, img_saveload))
                else
                    @test img == img_saveload
                end
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
                img_saveload = ImageIO.load(f; gamma=1.0)
                if typ == UInt8
                    @test all(img .== reinterpret(UInt8, img_saveload))
                else
                    @test img == img_saveload
                end

                open(io->ImageIO.save(Stream{format"PNG"}(io), img, permute_horizontal=false), joinpath(tmpdir, "test_io.png"), "w")
                img_saveload = open(io->ImageIO.load(Stream{format"PNG"}(io)), joinpath(tmpdir, "test_io.png"))
                if typ == UInt8
                    @test all(img .== reinterpret(UInt8, img_saveload))
                else
                    @test img == img_saveload
                end
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)

                ImageIO.save(f, img; compression_level=1)
                img_saveload = ImageIO.load(f)
                if typ == UInt8
                    @test all(img .== reinterpret(UInt8, img_saveload))
                else
                    @test img == img_saveload
                end
            end
        end
    end

    @testset "Portable bitmap" begin
        @testset "Bicolor pbm" begin
            img = rand(0:1, 10, 10)
            for fmt in (format"PBMBinary", format"PBMText")
                f = File{fmt}(joinpath(tmpdir, "test_fpath.pbm"))
                ImageIO.save(f, img)
                img_saveload = ImageIO.load(f)
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)

                open(io->ImageIO.save(Stream{fmt}(io), img), joinpath(tmpdir, "test_io.pbm"), "w")
                img_saveload = open(io->ImageIO.load(Stream{fmt}(io)), joinpath(tmpdir, "test_io.pbm"))
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
            end
        end

        @testset "Gray pgm" begin
            img = rand(N0f8, 10, 10)
            for fmt in (format"PGMBinary", format"PGMText")
                f = File{fmt}(joinpath(tmpdir, "test_fpath.pgm"))
                ImageIO.save(f, img)
                img_saveload = ImageIO.load(f)
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)

                open(io->ImageIO.save(Stream{fmt}(io), img), joinpath(tmpdir, "test_io.pgm"), "w")
                img_saveload = open(io->ImageIO.load(Stream{fmt}(io)), joinpath(tmpdir, "test_io.pgm"))
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
            end
        end

        @testset "Color ppm" begin
            img = rand(RGB{N0f8}, 10, 10)
            for fmt in (format"PPMBinary", format"PPMText")
                f = File{fmt}(joinpath(tmpdir, "test_fpath.ppm"))
                ImageIO.save(f, img)
                img_saveload = ImageIO.load(f)
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)

                open(io->ImageIO.save(Stream{fmt}(io), img), joinpath(tmpdir, "test_io.ppm"), "w")
                img_saveload = open(io->ImageIO.load(Stream{fmt}(io)), joinpath(tmpdir, "test_io.ppm"))
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
            end
        end
    end

    @testset "TIFF" begin
        for typ in [Gray{N0f8}, Gray{Float64}, RGB{N0f8}, RGB{Float64}] # TODO: Add UInt8, N0f8 support in TiffImages
            @testset "$typ TIFF" begin
                img = rand(typ, 10, 10)
                f = File{format"TIFF"}(joinpath(tmpdir, "test_fpath.tiff"))
                ImageIO.save(f, img)
                img_saveload = ImageIO.load(f)
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
                img_saveload = ImageIO.load(f; mmap=true)
                @test img == reshape(img_saveload, size(img))

                open(io->ImageIO.save(Stream{format"TIFF"}(io), img), joinpath(tmpdir, "test_io.tiff"), "w")
                img_saveload = open(io->ImageIO.load(Stream{format"TIFF"}(io)), joinpath(tmpdir, "test_io.tiff"))
                @test img == img_saveload
                @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
            end
        end
    end

    @testset "EXR" begin
        for typ in [RGBA{Float16}, RGB{Float16}, GrayA{Float16}, Gray{Float16}]
            img = rand(typ, 10, 10)
            f = File{format"EXR"}(joinpath(tmpdir, "test_fpath.exr"))
            ImageIO.save(f, img)
            img_saveload = ImageIO.load(f)
            @test img == img_saveload
            @test typeof(img_saveload) == ImageIO.canonical_type(f, img_saveload)
        end
    end
end


text:
module AutoGrad_gpu

import AutoGrad: addto!, addtoindex!, matches, ungetindex, Sparse, zeroslike
import LinearAlgebra: axpy!
import Base: +, -, copyto!
import CUDA, Knet, AutoGrad
using CUDA: CuArray, CuPtr, functional
using Knet.KnetArrays: DevArray, KnetArray, Cptr, cuallocator
using Knet.LibKnet8: @knet8
using Knet.CuArrays: cuarrays
using AutoGrad: AutoGrad, Sparse, recording, Result, Node, Tape, Value, Arg, value, set_gc_function
using Base.Broadcast: Broadcasted

include("addto.jl")
include("convert.jl")
include("getindex.jl")
include("sparse.jl")

include("priorityqueue.jl") # using DataStructures: DataStructures, PriorityQueue, dequeue!, dequeue_pair! # peek
include("gcnode.jl")
include("gcnode_kptr.jl")

end


text:
using LinearAlgebra

@testset "OrthoMADS" begin
    @testset "Initialisation" begin

        #Check that constructor defaults to parametric type Float64
        _om = OrthoMADS()
        om = OrthoMADS{Float64,Int64}()

        @test isdefined(om, :t)
        @test isdefined(om, :tmax)
        @test isdefined(om, :t₀)
        @test _om.l == om.l
        @test _om.init_run == om.init_run
        @test _om.Δᵖmin == om.Δᵖmin
        @test om.l == 0
        @test om.init_run == false
        @test om.Δᵖmin == 1.0

        primes = [
           2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
           67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
           139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
           223, 227, 229
        ]

        for N = 1:50
            om = OrthoMADS{Float64,Int64}()
            @test om.init_run == false
            DS.init_orthomads(N, om)
            @test om.t == om.tmax == om.t₀ == primes[N]
            @test om.init_run == true
        end
    end

    @testset "MeshUpdate" begin
        @testset "mesh index, l" begin
            #=
            Combining rules from progressive barrier and OrthoMADS, the index
            should decrement on a successful iteration (dominating), stay the same
            on an improving iteration, and increment on a failure.
            =#
            p = DSProblem(4;poll=OrthoMADS())
            m = p.config.mesh
            o = p.config.poll
           @test m.l == 0
            DS.MeshUpdate!(p, DS.Dominating)
            @test m.l == -1
            DS.MeshUpdate!(p, DS.Dominating)
            @test m.l == -2

            DS.MeshUpdate!(p, DS.Improving)
            @test m.l == -2
            DS.MeshUpdate!(p, DS.Improving)
            @test m.l == -2

            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.l == -1
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.l == 0

            DS.MeshUpdate!(p, DS.Improving)
            @test m.l == 0
            DS.MeshUpdate!(p, DS.Improving)
            @test m.l == 0

            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.l == 1
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.l == 2

            DS.MeshUpdate!(p, DS.Improving)
            @test m.l == 2
            DS.MeshUpdate!(p, DS.Improving)
            @test m.l == 2
        end

        @testset "poll size parameter, Δᵖ" begin
            p = DSProblem(4;poll=OrthoMADS())
            m = p.config.mesh
            o = p.config.poll

            #= Should evaluate to 2^-l =#
            @test m.Δᵖ == 1.0
            DS.MeshUpdate!(p, DS.Dominating)
            @test m.Δᵖ == 2.0
            DS.MeshUpdate!(p, DS.Dominating)
            @test m.Δᵖ == 4.0

            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵖ == 4.0
            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵖ == 4.0

            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵖ == 2.0
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵖ == 1.0

            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵖ == 1.0
            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵖ == 1.0

            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵖ == 0.5
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵖ == 0.25
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵖ == 0.125
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵖ == 0.0625

            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵖ == 0.0625
            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵖ == 0.0625
        end
        @testset "mesh size parameter, Δᵐ" begin
            p = DSProblem(4;poll=OrthoMADS())
            m = p.config.mesh
            o = p.config.poll

            #= Should evaluate to min(1, 4^(-l)) =#
            @test m.Δᵐ == 1.0
            DS.MeshUpdate!(p, DS.Dominating)
            @test m.Δᵐ == 1.0
            DS.MeshUpdate!(p, DS.Dominating)
            @test m.Δᵐ == 1.0

            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵐ == 1.0
            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵐ == 1.0

            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵐ == 1.0
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵐ == 1.0

            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵐ == 1.0
            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵐ == 1.0

            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵐ == 0.25
            DS.MeshUpdate!(p, DS.Unsuccessful)
            @test m.Δᵐ == 0.0625

            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵐ == 0.0625
            DS.MeshUpdate!(p, DS.Improving)
            @test m.Δᵐ == 0.0625
        end
        @testset "Halton index, tᵐ" begin
            N = 4
            p = DSProblem(N; poll=OrthoMADS())
            m = p.config.mesh
            o = p.config.poll
            DS.init_orthomads(N, o)

            #=
            If the poll size is the smallest so far then:
                t = l + t₀
            Otherwise:
                t = 1 + tmax

            tmax is the largest considered value of t
            t₀ stays constant
            =#
            @test o.t == 7 #the 4th prime number
            @test o.tmax == 7
            @test o.t₀ == 7

            #Successful (dominating) iterations cause a decrease in poll size
            #Improving iterations cause no change in poll size
            #Failed iterations cause an increase in poll size

            DS.MeshUpdate!(p, DS.Improving)
            #Poll size remains the same, ∴ t = tmax = 7 + 1
            @test o.t == 8
            @test o.tmax == 8
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Dominating)
            #Poll size increases, ∴ t = tmax + 1 =  8 + 1 = 9
            @test o.t == 9
            @test o.tmax == 9
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Dominating)
            #Poll size increases, ∴ t = tmax + 1 = 9 + 1 = 10
            @test o.t == 10
            @test o.tmax == 10
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Improving)
            #Poll size remains the same, ∴ t = tmax + 1 = 10 + 1 = 11
            @test o.t == 11
            @test o.tmax == 11
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Unsuccessful)
            #Poll decreases but is larger than min, ∴ t = tmax + 1 = 11 + 1 = 12
            @test o.t == 12
            @test o.tmax == 12
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Unsuccessful)
            #Poll decreases but is larger than min, ∴ t = tmax + 1 = 12 + 1 = 13
            @test o.t == 13
            @test o.tmax == 13
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Unsuccessful)
            #Poll decreases to new min, and l=1, ∴ t = l + t₀ = 8
            @test o.t == 8
            @test o.tmax == 13
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Unsuccessful)
            #Poll decreases to new min, and l=2, ∴ t = l + t₀ = 9
            @test o.t == 9
            @test o.tmax == 13
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Improving)
            #Poll remains the same, ∴ t = tmax + 1 = 13 + 1 = 14
            @test o.t == 14
            @test o.tmax == 14
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Unsuccessful)
            #Poll decreases to new min, and l=3, ∴ t = l + t₀ = 10
            @test o.t == 10
            @test o.tmax == 14
            @test o.t₀ == 7

            DS.MeshUpdate!(p, DS.Unsuccessful)
            DS.MeshUpdate!(p, DS.Unsuccessful)
            DS.MeshUpdate!(p, DS.Unsuccessful)
            DS.MeshUpdate!(p, DS.Unsuccessful)
            DS.MeshUpdate!(p, DS.Unsuccessful)
            #Multiple decreases will update tmax
            @test o.t == 15
            @test o.tmax == 15
            @test o.t₀ == 7
        end
    end

    @testset "HaltonCoefficient" begin
        #=
        Finds the coefficients (a) of the base p expansion of t:

        t = sum(a*p^r) for r = [0,∞]
        =#
        p=2
        @test DS.HaltonCoefficient(p, 0) == []
        @test DS.HaltonCoefficient(p, 1) == [1]
        @test DS.HaltonCoefficient(p, 2) == [0, 1]
        @test DS.HaltonCoefficient(p, 3) == [1, 1]
        @test DS.HaltonCoefficient(p, 4) == [0, 0, 1]
        @test DS.HaltonCoefficient(p, 5) == [1, 0, 1]
        @test DS.HaltonCoefficient(p, 6) == [0, 1, 1]
        @test DS.HaltonCoefficient(p, 7) == [1, 1, 1]
        @test DS.HaltonCoefficient(p, 8) == [0, 0, 0, 1]
        @test DS.HaltonCoefficient(p, 9) == [1, 0, 0, 1]

        p=3
        @test DS.HaltonCoefficient(p, 0) == []
        @test DS.HaltonCoefficient(p, 1) == [1]
        @test DS.HaltonCoefficient(p, 2) == [2]
        @test DS.HaltonCoefficient(p, 3) == [0, 1]
        @test DS.HaltonCoefficient(p, 4) == [1, 1]
        @test DS.HaltonCoefficient(p, 5) == [2, 1]
        @test DS.HaltonCoefficient(p, 6) == [0, 2]
        @test DS.HaltonCoefficient(p, 7) == [1, 2]
        @test DS.HaltonCoefficient(p, 8) == [2, 2]
        @test DS.HaltonCoefficient(p, 9) == [0, 0, 1]

        p=5
        @test DS.HaltonCoefficient(p, 0) == []
        @test DS.HaltonCoefficient(p, 1) == [1]
        @test DS.HaltonCoefficient(p, 2) == [2]
        @test DS.HaltonCoefficient(p, 3) == [3]
        @test DS.HaltonCoefficient(p, 4) == [4]
        @test DS.HaltonCoefficient(p, 5) == [0, 1]
        @test DS.HaltonCoefficient(p, 6) == [1, 1]
        @test DS.HaltonCoefficient(p, 7) == [2, 1]
        @test DS.HaltonCoefficient(p, 8) == [3, 1]
        @test DS.HaltonCoefficient(p, 9) == [4, 1]

        p=7
        @test DS.HaltonCoefficient(p, 0) == []
        @test DS.HaltonCoefficient(p, 1) == [1]
        @test DS.HaltonCoefficient(p, 2) == [2]
        @test DS.HaltonCoefficient(p, 3) == [3]
        @test DS.HaltonCoefficient(p, 4) == [4]
        @test DS.HaltonCoefficient(p, 5) == [5]
        @test DS.HaltonCoefficient(p, 6) == [6]
        @test DS.HaltonCoefficient(p, 7) == [0, 1]
        @test DS.HaltonCoefficient(p, 8) == [1, 1]
        @test DS.HaltonCoefficient(p, 9) == [2, 1]
    end

    @testset "HalonEntry" begin
        #=
        Calculates the radical inverse function in base p:

        u = sum(a/p^(1+r)) for r=[0,∞]
        =#

        t = 0
        @test DS.HaltonEntry(2, t) ≈ 0
        @test DS.HaltonEntry(3, t) ≈ 0
        @test DS.HaltonEntry(5, t) ≈ 0
        @test DS.HaltonEntry(7, t) ≈ 0

        t = 1
        @test DS.HaltonEntry(2, t) ≈ 1/2
        @test DS.HaltonEntry(3, t) ≈ 1/3
        @test DS.HaltonEntry(5, t) ≈ 1/5
        @test DS.HaltonEntry(7, t) ≈ 1/7

        t = 2
        @test DS.HaltonEntry(2, t) ≈ 1/4
        @test DS.HaltonEntry(3, t) ≈ 2/3
        @test DS.HaltonEntry(5, t) ≈ 2/5
        @test DS.HaltonEntry(7, t) ≈ 2/7

        t = 3
        @test DS.HaltonEntry(2, t) ≈ 3/4
        @test DS.HaltonEntry(3, t) ≈ 1/9
        @test DS.HaltonEntry(5, t) ≈ 3/5
        @test DS.HaltonEntry(7, t) ≈ 3/7

        t = 4
        @test DS.HaltonEntry(2, t) ≈ 1/8
        @test DS.HaltonEntry(3, t) ≈ 4/9
        @test DS.HaltonEntry(5, t) ≈ 4/5
        @test DS.HaltonEntry(7, t) ≈ 4/7

        t = 5
        @test DS.HaltonEntry(2, t) ≈ 5/8
        @test DS.HaltonEntry(3, t) ≈ 7/9
        @test DS.HaltonEntry(5, t) ≈ 1/25
        @test DS.HaltonEntry(7, t) ≈ 5/7

        t = 6
        @test DS.HaltonEntry(2, t) ≈ 3/8
        @test DS.HaltonEntry(3, t) ≈ 2/9
        @test DS.HaltonEntry(5, t) ≈ 6/25
        @test DS.HaltonEntry(7, t) ≈ 6/7

        t = 7
        @test DS.HaltonEntry(2, t) ≈ 7/8
        @test DS.HaltonEntry(3, t) ≈ 5/9
        @test DS.HaltonEntry(5, t) ≈ 11/25
        @test DS.HaltonEntry(7, t) ≈ 1/49
    end

    @testset "Halton" begin
        N = 4

        t = 0
        @test DS.Halton(N, t) ≈ [0,0,0,0]

        t = 1
        @test DS.Halton(N, t) ≈ [1/2,1/3,1/5,1/7]

        t = 2
        @test DS.Halton(N, t) ≈ [1/4,2/3,2/5,2/7]

        t = 3
        @test DS.Halton(N, t) ≈ [3/4,1/9,3/5,3/7]

        t = 4
        @test DS.Halton(N, t) ≈ [1/8,4/9,4/5,4/7]

        t = 5
        @test DS.Halton(N, t) ≈ [5/8,7/9,1/25,5/7]

        t = 6
        @test DS.Halton(N, t) ≈ [3/8, 2/9, 6/25, 6/7]

        t = 7
        @test DS.Halton(N, t) ≈ [7/8, 5/9, 11/25, 1/49]
    end

    @testset "AdjustedHaltonFamily" begin
        f = DS.AdjustedHaltonFamily([1/2, 1/3, 1/5, 1/7])
        @test f(0) ≈ [0.0,0.0,0.0,0.0]
        @test f(1) ≈ [0.0,0.0,-1.0,-1.0]
        @test f(2) ≈ [0.0,-1.0,-1.0,-1.0]
        @test f(3) ≈ [0.0,-1.0,-2.0,-2.0]
        @test f(4) ≈ [0.0,-1.0,-2.0,-3.0]
        @test f(5) ≈ [0.0,-2.0,-3.0,-4.0]
    end

    @testset "argmax" begin
        #General argmax test, due to the rounding also done, being within 0.1 is more than
        #sufficient
        f(x) = 0.1x^2 + x
        @test isapprox(DS.argmax(0, f, 6), 4.2195444, atol=0.1)

        g = DS.AdjustedHaltonFamily([7/8,5/9,11/25,1/49])
        @test isapprox(DS.argmax(0.0, x->norm(g(x)), 1.0), 0.8, atol = 0.1)

        h = DS.AdjustedHaltonFamily([7/16,22/27,22/25,2/49])
        @test isapprox(DS.argmax(5.156854249492381, x->norm(h(x)), 11.313708498984761), 11.5, atol = 0.1)
    end

    @testset "AdjustedHalton" begin
        N = 4

        t = 7; l = 0
        @test DS.AdjustedHalton([7/8,5/9,11/25,1/49], N, l) == [0.0, 0.0, 0.0, -1.0]

        t = 8; l = 1
        @test DS.AdjustedHalton([1/16,8/9,16/25,8/49], N, l) == [-1.0, 1.0, 0.0, 0.0]

        t = 9; l = 2
        @test DS.AdjustedHalton([9/16,1/27,21/25,15/49], N, l) == [0.0, -1.0, 1.0, -1.0]

        t = 10; l = 3
        @test DS.AdjustedHalton([5/16,10/27,2/25,22/49], N, l) == [-1.0, -1.0, -2.0, 0.0]

        t = 11; l = 4
        @test DS.AdjustedHalton([13/16,19/27,7/25,29/49], N, l) == [2.0, 2.0, -2.0, 1.0]

        t = 12; l = 5
        @test DS.AdjustedHalton([3/16,4/27,12/25,36/49], N, l) == [-3.0, -4.0, 0.0, 2.0]

        t = 13; l = 6
        @test DS.AdjustedHalton([11/16,13/27,17/25,43/49], N, l) == [3.0, 0.0, 3.0, 6.0]

        t = 14; l = 7
        @test DS.AdjustedHalton([7/16,22/27,22/25,2/49], N, l) == [-1.0, 5.0, 6.0, -8.0]
    end

    @testset "Householder Transform" begin
        @test DS.HouseholderTransform([-1.0, 5.0, 6.0, -8.0]) ≈
											[124.0   10.0   12.0  -16.0;
											  10.0   76.0  -60.0   80.0;
											  12.0  -60.0   54.0   96.0;
											 -16.0   80.0   96.0   -2.0]
    end

	@testset "Basis Generation" begin
        N = 4
        DS.GenerateOMBasis(N, 7, 0)  ==  [1 0 0 0;    0 1 0 0; 0 0 1 0; 0 0 0 -1]
        DS.GenerateOMBasis(N, 8, 1)  ==  [0 2 0 0;    2 0 0 0; 0 0 2 0; 0 0 0 2]
        DS.GenerateOMBasis(N, 9, 2)  ==  [3 0 0 0;    0 1 2 -2; 0 2 1 2; 0 -2 2 1]
        DS.GenerateOMBasis(N, 10, 3) == [4 -2 -4 0; -2 4 -4 0; -4 -4 -4 0; 0 0 0 6]
        DS.GenerateOMBasis(N, 11, 4) == [5 -8 8 -4; -8 5 8 -4; 8 8 5 4; -4 -4 4 11]
        DS.GenerateOMBasis(N, 12, 5) == [11 -24 0 12; -24 -3 0 16; 0 0 29 0; 12 16 0 21]
        DS.GenerateOMBasis(N, 13, 6) == [36 0 -18 -36; 0 54 0 0; -18 0 36 -36; -36 0 -36 -18]
        DS.GenerateOMBasis(N, 14, 7) == [124 10 12 -16; 10 76 -60 80; 12 -60 54 96; -16 80 96 -2]
	end

    @testset "GenerateDirections" begin
        p = DSProblem(4; poll=OrthoMADS())
        #Initially t=7, l=0
        D = [1 0 0 0 -1 0 0 0; 0 1 0 0 0 -1 0 0; 0 0 1 0 0 0 -1 0; 0 0 0 -1 0 0 0 1]
        @test DS.GenerateDirections(p) == D
        @test DS.GenerateDirections(p, p.config.poll) == D
        @test DS.GenerateDirections(4, p.config.poll) == D
    end
end


text:
module color
include("__text_utils.jl")
include("_ansi.jl")

export NamedColor, BitColor, RGBColor, get_color

# ----------------------------- types definition ----------------------------- #
"""
    AbstractColor

Abstract color type.
"""
abstract type AbstractColor end

Base.show(io::IO, color::AbstractColor) = print(io, "$(typeof(color))('$(color.color)')")

struct NamedColor <: AbstractColor
    color::String
end

struct BitColor <: AbstractColor
    color::String
end

struct RGBColor <: AbstractColor
    color::String
    r::Int
    g::Int
    b::Int
end
function RGBColor(s::AbstractString)
    r, g, b = _rgb(s)
    if r < 1
        r *= 255
        g *= 255
        b *= 255
    end
    return RGBColor(s, (Int64 ∘ round)(r), (Int64 ∘ round)(g), (Int64 ∘ round)(b))
end

# --------------------------------- is color? -------------------------------- #
"""
    _rgb(numbertype, txt)

Tries to parse r,g,b out of a string based on number type.
"""
_rgb(number_type, txt) = begin
    rgb = split(remove_brackets(nospaces(txt)), ",")
    r = parse(number_type, rgb[1])
    g = parse(number_type, rgb[2])
    b = parse(number_type, rgb[3])
    return r, g, b
end

"""
    _rgb(numbertype, txt)

Tries to parse r,g,b out of a string.
"""
function _rgb(txt)
    try
        r, g, b = _rgb(Float64, txt)

    catch
        return _rgb(Int64, txt)
    end
end

"""
    is_named_color(string::AbstractString)::Bool

Check if a string represents a named color.
"""
function is_named_color(string::AbstractString)::Bool
    if string ∈ NAMED_COLORS || string ∈ COLORS_16b
        return true
    else
        return false
    end
end

"""
    is_rgb_color(string::AbstractString)::Bool

Check if a string represents a RGB color.
"""
function is_rgb_color(string::AbstractString)::Bool
    try
        _rgb(string)
        return true
    catch
        return false
    end
end

"""
    is_hex_color(string::AbstractString)::Bool

Check if a string represents a hex color.
"""
function is_hex_color(string::AbstractString)::Bool
    stripped = nospaces(string)
    l = length(stripped)
    if stripped[1] == '#' && l == 7
        return true
    else
        return false
    end
end

"""
    is_color(string::AbstractString)::Bool

Check if a string represents color information, of any type.
"""
function is_color(string::AbstractString)::Bool

    is_named = is_named_color(string)
    is_rgb = is_rgb_color(string)
    is_hex = is_hex_color(string)

    return is_named || is_rgb || is_hex
end

"""
    is_background(string::AbstractString)::Bool

Check if a string represents background color information, of any type.
"""
function is_background(string::AbstractString)::Bool
    stripped = nospaces(string)
    if length(stripped) < 3
        return false
    else
        return stripped[1:3] == "on_" && is_color(stripped[4:end])
    end
end

# --------------------------------- get color -------------------------------- #
"""
    hex2rgb(hex::AbstractString)

Converts a string hex color code to a RGB color
"""
function hex2rgb(hex::AbstractString)::RGBColor
    to_int(h::AbstractString) = parse(Int, h, base = 16)
    r, g, b = [to_int(hex[i:i+1]) for i in (2, 4, 6)]
    return RGBColor("($r, $g, $b)", r, g, b)
end

"""
    get_color(string::AbstractString; bg=false)::AbstractColor

Extract a color type from a string with color information.
"""
function get_color(string::AbstractString; bg = false)::AbstractColor
    if bg
        string = nospaces(string)[4:end]
    end

    if is_named_color(string)
        if string ∈ COLORS_16b
            idx = findfirst((c) -> c == string, COLORS_16b)[1]
            return BitColor(COLORS_16b[idx])
        else
            idx = findfirst((c) -> c == string, NAMED_COLORS)[1]
            return NamedColor(NAMED_COLORS[idx])
        end
    elseif is_rgb_color(string)
        return RGBColor(string)
    else
        # convert hex to rgb
        return hex2rgb(string)
    end
end

end


text:
# cartesianspace.jl
#
# Defines the immutable CartesianSpace for denoting a self-dual vector space. CartesianSpace is completely
# characterised by its dimension. Tensors with CartesianSpace as index space cannot distinguish between
# covariant and contravariant indices. This allows to easily wrap standard Arrays.

# CartesianSpace:
#----------------
immutable CartesianSpace <: EuclideanSpace{ℝ}
    d::Int
end
^(::Type{ℝ},d::Int) = CartesianSpace(d)

# Corresponding methods:
dim(V::CartesianSpace) = V.d
cnumber(V::CartesianSpace) = CartesianSpace(1)
cnumber(::Type{CartesianSpace}) = CartesianSpace(1)
iscnumber(V::CartesianSpace) = dim(V)==1

# Show methods
Base.show(io::IO, V::CartesianSpace) = print(io, "ℝ^$(V.d)")

# direct sum of CartesianSpace vector spaces
directsum(V1::CartesianSpace, V2::CartesianSpace) = CartesianSpace(V1.d+V2.d)

# fusing CartesianSpace: return Bool indicating whether V1 and V2 can be fused to V
fuse(V1::CartesianSpace,V2::CartesianSpace,V::CartesianSpace) = dim(V1)*dim(V2)==dim(V)

# # basis and basisvector
# typealias CartesianBasisVector BasisVector{CartesianSpace,Int} # use integer from 1 to dim as identifier
# typealias CartesianBasis Basis{CartesianSpace}
#
# Base.length(B::CartesianBasis) = dim(space(B))
# Base.start(B::CartesianBasis) = 1
# Base.next(B::CartesianBasis, state::Int) = (CartesianBasisVector(space(B),state),state+1)
# Base.done(B::CartesianBasis, state::Int) = state>length(B)
#
# Base.to_index(b::CartesianBasisVector) = b.identifier


text:
"""
    gtstats(vcffile, [out=devnull])

Calculate genotype statistics for each marker with GT field in a VCF file.

# Input
- `vcffile`: VCF file, ending with .vcf or .vcf.gz
- `out`: output file name or IOStream. Default is `out=devnull` (no output).
One line with 15 tab-delimited fiels is written per marker to `out`:
    - 1-8)  VCF fixed fields (CHROM, POS, ID, REF, ALT, QUAL, FILT, INFO)
    -   9)  Missing genotype count
    -  10)  Missing genotype frequency
    -  11)  ALT allele count
    -  12)  ALT allele frequency
    -  13)  Minor allele count             (REF allele vs ALT alleles)
    -  14)  Minor allele frequency         (REF allele vs ALT alleles)
    -  15)  HWE P-value                    (REF allele vs ALT alleles)

# Output
- `records`: number of records in the input VCF file
- `samples`: number of individuals in the input VCF file
- `lines`  : number of lines written to `out`; equivalently number of markers
    with GT field
- `missings_by_sample`: number of missing genotypes in each sample
- `missings_by_record`: number of missing genotypes in each record (marker)
- `maf_by_record`: minor allele frequency in each record (marker)
- `minorallele_by_record`: a Boolean vector indicating the minor allele in each
    record (marker). `minorallele_by_record[i]=true` means the minor allele is
    the REF allele for marker `i`; `minorallele_by_record[i]=false` means the
    minor allele is the ALT allele for marker `i`
"""
function gtstats(vcffile::AbstractString, out::IO=devnull)
    # open VCF file
    reader = VCF.Reader(openvcf(vcffile, "r"))
    # set up progress bar
    records = nrecords(vcffile)
    out == stdout || (pbar = ProgressMeter.Progress(records, 1))
    # allocate ouput arrays
    samples = nsamples(reader)
    missings_by_sample = zeros(Int, samples)
    missings_by_record = Int[]
    maf_by_record = Float64[]
    minorallele_by_record = Bool[]
    sizehint!(maf_by_record, records)
    sizehint!(missings_by_record, records)
    sizehint!(minorallele_by_record, records)
    # loop over records
    records = lines = 0
    for record in reader
        records += 1
        # if no "GT" field, skip this record
        VCF.findgenokey(record, "GT") == nothing && continue
        # calcuate summary statistics
        lines += 1
        n00, n01, n11, n0, n1, altfreq, reffreq, missings,
            minorallele, maf, hwepval = gtstats(record, missings_by_sample)
        missfreq = missings / (n0 + n1)
        altfreq  = n0 / (n0 + n1)
        minoralleles = minorallele ? n0 : n1
        push!(missings_by_record, missings)
        push!(maf_by_record, maf)
        push!(minorallele_by_record, minorallele)
        # output
        nbytes = record.format[1][1] - 2
        unsafe_write(out, pointer(record.data), nbytes)
        print(out, '\t', missings, '\t', missfreq, '\t', n0, '\t',
        altfreq, '\t', minoralleles, '\t', maf, '\t', hwepval, '\n')
        # update progress bar
        out == stdout || ProgressMeter.update!(pbar, records)
    end
    close(out); close(reader)
    return records, samples, lines, missings_by_sample, missings_by_record,
        maf_by_record, minorallele_by_record
end

function gtstats(vcffile::AbstractString, out::AbstractString)
    ofile = endswith(out, ".gz") ? GzipCompressorStream(open(out, "w")) : open(out, "w")
    gtstats(vcffile, ofile)
end

"""
    gtstats(record, [missings_by_sample=nothing])

Calculate genotype statistics for a VCF record with GT field.

# Input
- `record`: a VCF record
- `missings_by_sample`: accumulator of missings by sample, `missings_by_sample[i]` is incremented by 1 if `i`-th individual has missing genotype in this record

# Output
- `n00`: number of homozygote ALT/ALT or ALT|ALT
- `n01`: number of heterozygote REF/ALT or REF|ALT
- `n11`: number of homozygote REF/REF or REF|REF
- `n0`: number of ALT alleles
- `n1`: number of REF alleles
- `altfreq`: proportion of ALT alleles
- `reffreq`: proportion of REF alleles
- `missings`: number of missing genotypes
- `minorallele`: minor allele: `false` (ALT allele) or `true` (REF allele)
- `maf`: minor allele frequency
- `hwepval`: Hardy-Weinberg p-value
"""
function gtstats(
    record::VCF.Record,
    missings_by_sample::Union{Vector,Nothing}=nothing
    )
    # n11: number of homozygote REF/REF or REF|REF
    # n00: number of homozygote ALT/ALT or ALT|ALT
    # n01: number of heterozygote REF/ALT or REF|ALT
    missings = n00 = n10 = n11 = 0
    gtkey = VCF.findgenokey(record, "GT")
    for i in 1:lastindex(record.genotype)
        geno = record.genotype[i]
        # dropped field or "." => 0x2e
        if gtkey > lastindex(geno) || record.data[geno[gtkey]] == [0x2e]
            missings += 1
            missings_by_sample == nothing || (missings_by_sample[i] += 1)
        else
            # "0" => 0x30, "1" => 0x31
            if record.data[geno[gtkey][1]] == 0x30
                n00 += record.data[geno[gtkey][3]] == 0x30 ? 1 : 0
            elseif record.data[geno[gtkey][1]] == 0x31
                n11 += record.data[geno[gtkey][3]] == 0x31 ? 1 : 0
            end
        end
    end
    n01         = length(record.genotype) - missings - n00 - n11
    n0          = n01 + 2n00
    n1          = n01 + 2n11
    altfreq     = n0 / (n0 + n1)
    reffreq     = n1 / (n0 + n1)
    minorallele = n1 < n0
    maf         = n0 < n1 ? altfreq : reffreq
    hwepval     = hwe(n00, n01, n11)
    return n00, n01, n11, n0, n1, altfreq, reffreq, missings,
        minorallele, maf, hwepval
end

"""
    hwe(n00, n01, n11)

Hardy-Weinberg equilibrium test.
"""
function hwe(n00::Integer, n01::Integer, n11::Integer)
    n = n00 + n01 + n11
    n == 0 && return 1.0
    p0 = (n01 + 2n00) / 2n
    (p0 ≤ 0.0 || p0 ≥ 1.0) && return 1.0
    p1 = 1 - p0
    # Pearson's Chi-squared test
    e00 = n * p0 * p0
    e01 = 2n * p0 * p1
    e11 = n * p1 * p1
    ts = (n00 - e00)^2 / e00 + (n01 - e01)^2 / e01 + (n11 - e11)^2 / e11
    pval = ccdf(Chi(1), ts)
    # TODO Fisher exact test
    return pval
end

"""
    countvcflines(vcffile)

Count the number of lines in a VCF (`.vcf` or `.vcf.gz`) file.
"""
function countvcflines(vcffile::AbstractString)
    countlines(openvcf(vcffile, "r"))
end

"""
    openvcf(vcffile, [mode = "r"])

Open VCF file (`.vcf` or `.vcf.gz`) and return an IO stream.
"""
function openvcf(vcffile::AbstractString, mode::AbstractString="r")
    if endswith(vcffile, ".vcf")
        return open(vcffile, mode)
    elseif endswith(vcffile, ".vcf.gz") && mode == "r"
        return GzipDecompressorStream(open(vcffile, mode))
    elseif endswith(vcffile, ".vcf.gz") && mode ∈ ["w", "a"]
        return GzipCompressorStream(open(vcffile, mode))
    elseif endswith(vcffile, ".vcf.gz") && mode ∉ ["r", "w", "a"]
        throw(ArgumentError("mode can only be r, w, or a for vcf.gz file"))
    else
        throw(ArgumentError("vcffile name should end with vcf or vcf.gz"))
    end
end

"""
    nrecords(vcffile)

Number of records (markers) in a VCF file. Each record is a row.
"""
function nrecords(vcffile::AbstractString)
    reader = VCF.Reader(openvcf(vcffile, "r"))
    records = countvcflines(vcffile) - length(VCF.header(reader)) - 1
    close(reader)
    records
end

function nsamples(reader::VCF.Reader)
    length(VCF.header(reader).sampleID)
end

"""
    nsamples(vcffile)

Number of samples (individuals) in a VCF file.
"""
function nsamples(vcffile::AbstractString)
    reader = VCF.Reader(openvcf(vcffile, "r"))
    samples = length(VCF.header(reader).sampleID)
    close(reader)
    samples
end


text:
"""
    VerticallyStretchedLatLonGrid{TX, TY, TZ} <: AbstractGrid{TX, TY, TZ}
A horizontally curvilinear vertically strecthed grid with topology {TX, TY, TZ}
"""
struct VerticallyStretchedLatLonGrid{TX, TY, TZ, R, A1, A2, A3} <: AbstractGrid{TX, TY, TZ}
    # corrdinates at cell centers, unit: degree
    xC::R
    yC::R
    # corrdinates at cell centers, unit: meter
    zC::A1
    # corrdinates at cell faces, unit: degree
    xF::R
    yF::R
    # corrdinates at cell faces, unit: meter
    zF::A1
    # grid spacing, unit: degree
    Δx::Float64
    Δy::Float64
    # grid spacing from center to center, unit: meter
    dxC::A2
    dyC::A2
    dzC::A1
    # grid spacing from face to face, unit: meter
    dxF::A2
    dyF::A2
    dzF::A1
    # areas and volume, unit: m² or m³
    Ax::A3
    Ay::A3
    Az::A2
    Vol::A3
    # number of grid points
    Nx::Int
    Ny::Int
    Nz::Int
    # number of halo points
    Hx::Int
    Hy::Int
    Hz::Int
end

"""
    LoadVerticallyStretchedLatLonGrid(; grid_info, size, lat, lon,
                                        halo=(2,2,2))
Creats a `VerticallyStretchedLatLonGrid` struct with `size = (Nx, Ny, Nz)` grid points.

Keyword Arguments (Required)
============================
- `grid_info` : A NamedTuple contains external grid information (e.g. from MITgcm), please
                    refer to documentation for the required format.
- `size` : A tuple prescribing the number of grid points.
                `size` is a 3-tuple no matter for 3D, 2D, or 1D model.
- `lat` : A 2-tuple specifying the startind and ending points in latitudinal direction.
                Possible values are from -80 (80S) to 80 (80N).
- `lon` : A 2-tuple specifying the startind and ending points in longitudinal direction.
                Possible values are from -180 (180W) to 180 (180E).

Keyword Arguments (Optional)
============================
- `halo` : A tuple of integers that specifies the size of the halo region of cells
                surrounding the physical interior for each direction.
                `halo` is a 3-tuple no matter for 3D, 2D, or 1D model.
                At least 2 halo points are needed for DST3FL advection scheme.
"""
function LoadVerticallyStretchedLatLonGrid(;grid_info, size, lat, lon, halo=(2,2,2))
    Nx, Ny, Nz = size
    Hx, Hy, Hz = halo
    lat₁, lat₂ = lat
    lon₁, lon₂ = lon

    @assert -180 <= lon₁ < lon₂ <= 180
    @assert -80 <= lat₁ < lat₂ <= 80

    TX = lon₁ == -180 && lon₂ == 180 ? Periodic : Bounded
    TY = Bounded
    TZ = Bounded

    Δx = (lon₂ - lon₁) / Nx
    Δy = (lat₂ - lat₁) / Ny

    xF = range(lon₁ - Hx * Δx, lon₁ + (Nx + Hx - 1) * Δx, length = Nx + 2 * Hx)
    yF = range(lat₁ - Hy * Δy, lat₁ + (Ny + Hy - 1) * Δy, length = Ny + 2 * Hy)

    xC = range(lon₁ + (0.5 - Hx) * Δx, lon₁ + (Nx + Hx - 0.5) * Δx, length = Nx + 2 * Hx)
    yC = range(lat₁ + (0.5 - Hy) * Δy, lat₁ + (Ny + Hy - 0.5) * Δy, length = Ny + 2 * Hy)

    zF = zeros(Nz+2Hz)
    zC = zeros(Nz+2Hz)
    dzF = zeros(Nz+2Hz)
    dzC = zeros(Nz+2Hz)
    dxC = zeros(Nx+2*Hx, Ny+2*Hy)
    dyC = zeros(Nx+2*Hx, Ny+2*Hy)
    dxF = zeros(Nx+2*Hx, Ny+2*Hy)
    dyF = zeros(Nx+2*Hx, Ny+2*Hy)
    Ax = zeros(Nx+2*Hx, Ny+2*Hy, Nz+2*Hz)
    Ay = zeros(Nx+2*Hx, Ny+2*Hy, Nz+2*Hz)
    Az = zeros(Nx+2*Hx, Ny+2*Hy)
    Vol = zeros(Nx+2*Hx, Ny+2*Hy, Nz+2*Hz)
    hFW = ones(Nx+2*Hx, Ny+2*Hy, Nz+2*Hz)
    hFS = ones(Nx+2*Hx, Ny+2*Hy, Nz+2*Hz)
    hFC = ones(Nx+2*Hx, Ny+2*Hy, Nz+2*Hz)

    zF[1+Hz:Nz+Hz+1] = Float64.(grid_info.RF)
    zC[1+Hz:Nz+Hz] = Float64.(grid_info.RC)
    dzF[1+Hz:Nz+Hz] = Float64.(grid_info.DRF)
    dzC[1+Hz:Nz+Hz] = Float64.(grid_info.DRC); dzC[1+Hz] *= 2.0 # First laryer only has half of the grid
    dxC[1+Hx:Nx+Hx, 1+Hy:Ny+Hy] = Float64.(grid_info.DXC)
    dxF[1+Hx:Nx+Hx, 1+Hy:Ny+Hy] = Float64.(grid_info.DXG)
    dyC[1+Hx:Nx+Hx, 1+Hy:Ny+Hy] = Float64.(grid_info.DYC)
    dyF[1+Hx:Nx+Hx, 1+Hy:Ny+Hy] = Float64.(grid_info.DYG)
    Az[1+Hx:Nx+Hx, 1+Hy:Ny+Hy] = Float64.(grid_info.RAC)
    hFW[1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz] = Float64.(grid_info.hFacW)
    hFS[1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz] = Float64.(grid_info.hFacS)
    hFC[1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz] = Float64.(grid_info.hFacC)

    ##### fill halos
    @views @. dzF[1:Hz] = dzF[Hz+1]
    @views @. dzC[1:Hz] = dzC[Hz+1]
    @views @. dzF[Nz+Hz+1:Nz+2*Hz] = dzF[Nz+Hz]
    @views @. dzC[Nz+Hz+1:Nz+2*Hz] = dzC[Nz+Hz]

    @views @. zF[1:Hz] = zF[1+Hz:2*Hz] + dzF[1]*2
    @views @. zC[1:Hz] = zC[1+Hz:2*Hz] + dzC[1]*2
    @views @. zF[Nz+Hz+2:Nz+2*Hz] = zF[Nz+2:Nz+Hz] - dzF[end]*2
    @views @. zC[Nz+Hz+1:Nz+2*Hz] = zC[Nz+1:Nz+Hz] - dzC[end]*2

    for g in (dxC, dyC, dxF, dyF, Az)
        fill_halo_west!(g, Hx, Nx, TX())
        fill_halo_east!(g, Hx, Nx, TX())
        fill_halo_south!(g, Hy, Ny, TY())
        fill_halo_north!(g, Hy, Ny, TY())
    end
    for g in (hFW, hFS, hFC)
        fill_halo_west!(g, Hx, Nx, TX())
        fill_halo_east!(g, Hx, Nx, TX())
        fill_halo_south!(g, Hy, Ny, TY())
        fill_halo_north!(g, Hy, Ny, TY())
        fill_halo_top!(g, Hz, Nz, TZ())
        fill_halo_bottom!(g, Hz, Nz, TZ())
    end

    ##### calculate areas and volumes
    for i in 1:Nx+2*Hx
        for j in 1:Ny+2*Hy
            for k in 1:Nz+2*Hz
                Ax[i,j,k] = dzF[k] * dyF[i,j] * hFW[i,j,k]
                Ay[i,j,k] = dzF[k] * dxF[i,j] * hFS[i,j,k]
                Vol[i,j,k] = dzF[k] * Az[i,j] * hFC[i,j,k]
            end
        end
    end

    return VerticallyStretchedLatLonGrid{TX, TY, TZ, typeof(xF), typeof(zF), typeof(dxC), typeof(Vol)}(
        xC, yC, zC, xF, yF, zF, Δx, Δy, dxC, dyC, dzC, dxF, dyF, dzF, Ax, Ay, Az, Vol, Nx, Ny, Nz, Hx, Hy, Hz)
end

function show(io::IO, g::VerticallyStretchedLatLonGrid{TX, TY, TZ}) where {TX, TY, TZ}
    xL, xR = g.xF[g.Hx+1], g.xF[g.Hx+1+g.Nx]
    yL, yR = g.yF[g.Hy+1], g.yF[g.Hy+1+g.Ny]
    zL, zR = g.zF[g.Hz+1], g.zF[g.Hz+1+g.Nz]
    dzF_min = minimum(g.dzF)
    dzF_max = maximum(g.dzF)
    print(io, "VerticallyStretchedLatLonGrid{$TX, $TY, $TZ}\n",
              "domain: x ∈ [$xL, $xR], y ∈ [$yL, $yR], z ∈ [$zL, $zR]\n",
              "topology (Tx, Ty, Tz):     ", (TX, TY, TZ), '\n',
              "resolution (Nx, Ny, Nz):   ", (g.Nx, g.Ny, g.Nz), '\n',
              "halo size (Hx, Hy, Hz):    ", (g.Hx, g.Hy, g.Hz), '\n',
              "grid spacing (Δx, Δy, Δz): ", g.Δx, ", ", g.Δy, ", [min=", dzF_min, ", max=", dzF_max,"])",)
end

function short_show(grid::VerticallyStretchedLatLonGrid{TX, TY, TZ}) where {TX, TY, TZ}
    return "VerticallyStretchedLatLonGrid{$TX, $TY, $TZ}(Nx=$(grid.Nx), Ny=$(grid.Ny), Nz=$(grid.Nz))"
end


text:
@testset "Mismatches" begin
    function test_mismatches(a, b)
        count = 0
        for (x, y) in zip(a, b)
            count += x != y
        end
        @test mismatches(a, b) === mismatches(b, a) === count
    end

    for len in 1:64, _ in 1:10
        a = random_dna_kmer(len)
        b = random_dna_kmer(len)
        if len <= 32
            test_mismatches(DNAMer(a), DNAMer(b))
        end
        test_mismatches(BigDNAMer(a), BigDNAMer(b))

        a = random_rna_kmer(len)
        b = random_rna_kmer(len)
        if len <= 32
            test_mismatches(RNAMer(a), RNAMer(b))
        end
        test_mismatches(BigRNAMer(a), BigRNAMer(b))
    end
end