//===----------------------------------------------------------------------===//
//
// Part of the CUDA Toolkit, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _CUDA_MEMORY_RESOURCE
#define _CUDA_MEMORY_RESOURCE

// clang-format off
/*
    memory_resource synopsis
namespace cuda {
namespace mr {
template <class Resource>
concept resource = equality_comparable<Resource>
                && requires(Resource& res, void* ptr, size_t size, size_t alignment) {
    { res.allocate(size, alignment) } -> same_as<void*>;
    { res.deallocate(ptr, size, alignment) } -> same_as<void>;
};

template <class Resource>
concept async_resource = resource<Resource>
                      && requires(Resource& res, void* ptr, size_t size, size_t alignment, cuda_stream_ref stream) {
    { res.allocate_async(size, alignment, stream) } -> same_as<void*>;
    { res.deallocate_async(ptr, size, alignment, stream) } -> same_as<void>;
};

template <class Resource, class Property>
concept has_property = resource<Resource> && requires(const Resource& res, Property prop) {
    get_property(res, prop);
};

template <class Property>
concept property_with_value = requires {
    typename Property::value_type;
};

template <class Resource, class Property, class Return>
concept has_property_with = resource<Resource>
                         && property_with_value<Property>
                         && same_as<Return, typename Property::value_type>
                         && requires(const Resource& res, Property prop) {
    get_property(res, prop) -> Return;
};

template <class Resource, class... Properties>
concept resource_with = resource<Resource> && (has_property<Resource, Properties> && ...);

template <class Resource, class... Properties>
concept async_resource_with = async_resource<Resource> && (has_property<Resource, Properties> && ...);

template <class... Properties>
class resource_ref {
    template <resource_with<Properties...> Resource>
    resource_ref(Resource&) noexcept;

    void* allocate(size_t size, size_t alignment);
    void deallocate(void* ptr, size_t size, size_t alignment);

    template <class... OtherProperties>
        requires resource_with<resource_ref, OtherProperties...>
              && resource_with<resource_ref<OtherProperties...>, Properties...>
    friend bool operator==(const resource_ref& left, const resource_ref<OtherProperties...>& right);

    template <property_with_value Property>
        requires has_property<resource_ref, Property>
    friend typename Property::value_type get_property(const resource_ref& ref, Property) noexcept;

    template <class Property>
        requires (has_property<resource_ref, Property> && !property_with_value<Property>)
    friend void get_property(const resource_ref& ref, Property) noexcept;
};

}  // mr
}  // cuda
*/
// clang-format on

#include <cuda_runtime_api.h>
// cuda_runtime_api needs to come first

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#include <cuda/__memory_resource/cuda_managed_memory_resource.h>
#include <cuda/__memory_resource/cuda_memory_resource.h>
#include <cuda/__memory_resource/cuda_pinned_memory_resource.h>
#include <cuda/__memory_resource/get_property.h>
#include <cuda/__memory_resource/properties.h>
#include <cuda/__memory_resource/resource.h>
#include <cuda/__memory_resource/resource_ref.h>

#endif //_LIBCUDACXX_BEGIN_NAMESPACE_CUDA
