#pragma GCC system_header

#ifndef _LIBCXX_MEMORY
#define _LIBCXX_MEMORY

#include <__config>
#include <cstddef>
#include <new>
#include <type_traits>
#include <utility>

_LIBCXX_BEGIN_NAMESPACE_STD

template <typename T>
class unique_ptr {
public:
    unique_ptr()
        : m_data(nullptr)
    {
    }

    unique_ptr(T* data)
        : m_data(data)
    {
    }

    unique_ptr(std::nullptr_t)
        : m_data(nullptr)
    {
    }

    unique_ptr& operator=(std::nullptr_t)
    {
        reset();
        return *this;
    }

    unique_ptr(unique_ptr&& moving) noexcept
        : m_data(nullptr)
    {
        moving.swap(*this);
    }

    unique_ptr& operator=(unique_ptr&& moving) noexcept
    {
        moving.swap(*this);
        return *this;
    }

    template <typename U>
    unique_ptr(unique_ptr<U>&& moving)
    {
        unique_ptr<T> tmp((T*)moving.release());
        tmp.swap(*this);
    }

    template <typename U>
    unique_ptr& operator=(unique_ptr<U>&& moving)
    {
        unique_ptr<T> tmp((T*)moving.release());
        tmp.swap(*this);
        return *this;
    }

    T* release() noexcept
    {
        T* result = m_data;
        m_data = nullptr;
        return result;
    }

    void swap(unique_ptr& src) noexcept
    {
        T* tmp_data = m_data;
        m_data = src.m_data;
        src.m_data = tmp_data;
    }

    void reset()
    {
        T* tmp = release();
        delete tmp;
    }

    void reset(T* new_ptr)
    {
        T* tmp = release();
        delete tmp;
        m_data = new_ptr;
    }

    ~unique_ptr()
    {
        delete m_data;
    }

    unique_ptr(unique_ptr const&) = delete;
    unique_ptr& operator=(unique_ptr const&) = delete;

    T* operator->() const { return m_data; }
    T& operator*() const { return *m_data; }

    T* get() const { return m_data; }
    explicit operator bool() const { return m_data; }

private:
    T* m_data { nullptr };
};

template <class Alloc>
struct allocator_traits {
    using allocator_type = Alloc;
    using value_type = typename Alloc::value_type;
    using pointer = typename Alloc::pointer;
    using const_pointer = typename Alloc::const_pointer;
    using void_pointer = typename Alloc::void_pointer;
    using const_void_pointer = typename Alloc::const_void_pointer;
    using difference_type = typename Alloc::difference_type;
    using size_type = typename Alloc::size_type;
};

template <class _Alloc, class S>
struct __rebind_alloc_helper;

template <template <class, class...> class _Alloc, class New, class Old, class... OtherArgs>
struct __rebind_alloc_helper<_Alloc<Old, OtherArgs...>, New> {
    typedef typename std::allocator_traits<_Alloc<New, OtherArgs...>>::allocator_type type;
};

template <class T>
struct allocator {
    using value_type = T;
    using pointer = T*;
    using const_pointer = const T*;
    using void_pointer = void*;
    using const_void_pointer = const void*;
    using difference_type = ptrdiff_t;
    using size_type = size_t;

    allocator() = default;
    ~allocator() = default;

    pointer allocate(size_t n)
    {
        return reinterpret_cast<pointer>(operator new(n * sizeof(T)));
    }

    void deallocate(pointer p, size_t n)
    {
        return operator delete(p, n);
    }
};

template <class T, class... Args>
static constexpr unique_ptr<T> make_unique(Args&&... args)
{
    return new T(forward<Args>(args)...);
}

template <class T, class... Args>
static constexpr T* construct_at(T* p, Args&&... args)
{
    return reinterpret_cast<T*>(new (static_cast<void*>(p)) T(forward<Args>(args)...));
}

template <class T>
static constexpr void destroy_at(T* p)
{
    if constexpr (is_array_v<T>) {
        static_assert(true, "Not implemented destroy_at for arrays");
    }
    return p->~T();
}

#if defined(_LIBCXX_BUILD_OPUNTIAOS_EXTENSIONS)
_LIBCXX_BEGIN_OPUNTIAOS_EXTENSION

template <class T, class... Args>
static constexpr T& construct(Args&&... args)
{
    T* newobject = new T(forward<Args>(args)...);
    return *newobject;
}

_LIBCXX_END_OPUNTIAOS_EXTENSION
#endif

template <class T>
T* to_address(T* p)
{
    static_assert(!is_function_v<T>);
    return p;
}

template <class T>
T* to_address(const T& p)
{
    return to_address(p.operator->());
}

_LIBCXX_END_NAMESPACE_STD

#endif // _LIBCXX_MEMORY