#ifndef __GBLIBCPP_MEMORY__
#define __GBLIBCPP_MEMORY__

#include <bits/compressed_pair>
#include <cstddef>
#include <new>
#include <type_traits>
#include <utility>

namespace std {

template <typename T>
constexpr T* addressof(T& arg) noexcept {
    return __builtin_addressof(arg);
}

// template <typename T>
// constexpr enable_if_t<is_function_v<remove_reference_t<T>>, T*>
// addressof(T& arg) noexcept
// {
//     return &arg;
// }

// template <typename T>
// constexpr enable_if_t<!is_function_v<remove_reference_t<T>>, T*>
// addressof(T& arg) noexcept
// {
//     return reinterpret_cast<T*>(
//         &const_cast<char&>(
//             reinterpret_cast<const volatile char&>(arg)
//         )
//     );
// }

template <typename T>
const T* addressof(const T&&) = delete;

namespace __helpers {

    template <typename Ptr, typename = void>
    struct pointer_difference_type {
        using type = std::ptrdiff_t;
    };

    template <typename Ptr>
    struct pointer_difference_type<Ptr,
                                   std::void_t<typename Ptr::difference_type>> {
        using type = typename Ptr::difference_type;
    };

    template <typename Ptr>
    using pointer_difference_type_t =
        typename pointer_difference_type<Ptr>::type;

    template <typename Base, typename T>
    struct rebind;

    template <template <typename, typename...> typename Template,
              typename NewType, typename OldType, typename... Args>
    struct rebind<Template<OldType, Args...>, NewType> {
        using type = Template<NewType, Args...>;
    };

    template <typename Ptr, typename T, typename = void>
    struct try_rebind {
        using type = typename rebind<Ptr, T>::type;
    };

    template <typename Ptr, typename T>
    struct try_rebind<Ptr, T, std::void_t<typename Ptr::template rebind<T>>> {
        using type = typename Ptr::template rebind<T>;
    };

    template <typename Ptr, typename = void>
    struct pointer_element {};

    template <typename Ptr>
    struct pointer_element<
        Ptr, std::enable_if_t<std::is_same_v<
                 void, std::void_t<typename Ptr::element_type>>>> {
        using type = typename Ptr::element_type;
    };

    template <template <typename, typename...> typename Template, typename T,
              typename... Args>
    struct pointer_element<Template<T, Args...>, void> {
        using type = T;
    };

    template <typename Ptr, typename = void>
    struct pointer_traits_impl {};

    template <typename Ptr>
    struct pointer_traits_impl<
        Ptr, std::void_t<typename pointer_element<Ptr>::type>> {
        using pointer = Ptr;
        using element_type = typename pointer_element<Ptr>::type;
        using difference_type = pointer_difference_type_t<Ptr>;

        template <typename U>
        using rebind = typename try_rebind<Ptr, U>::type;

        static pointer pointer_to(element_type& ref) {
            return Ptr::pointer_to(ref);
        }
    };

    template <typename T>
    struct pointer_traits_impl<T*, void> {
        using pointer = T*;
        using element_type = T;
        using difference_type = std::ptrdiff_t;

        template <typename U>
        using rebind = U*;

        static pointer pointer_to(element_type& ref) {
            return std::addressof(ref);
        }
    };

} // namespace __helpers

template <typename Ptr>
struct pointer_traits : public __helpers::pointer_traits_impl<Ptr> {};

namespace __helpers {

    template <typename Alloc, typename = void>
    struct allocator_pointer {
        using type = typename Alloc::value_type*;
    };

    template <typename Alloc>
    struct allocator_pointer<Alloc, std::void_t<typename Alloc::pointer>> {
        using type = typename Alloc::pointer;
    };

    template <typename Alloc>
    using allocator_pointer_t = typename allocator_pointer<Alloc>::type;

    template <typename Alloc, typename Pointer, typename = void>
    struct allocator_const_pointer {
        using type = typename std::pointer_traits<Pointer>::template rebind<
            const typename Alloc::value_type>;
    };

    template <typename Alloc, typename Pointer>
    struct allocator_const_pointer<Alloc, Pointer,
                                   std::void_t<typename Alloc::const_pointer>> {
        using type = typename Alloc::const_pointer;
    };

    template <typename Alloc, typename Pointer>
    using allocator_const_pointer_t =
        typename allocator_const_pointer<Alloc, Pointer>::type;

    template <typename Alloc, typename Pointer, typename = void>
    struct allocator_void_pointer {
        using type =
            typename std::pointer_traits<Pointer>::template rebind<void>;
    };

    template <typename Alloc, typename Pointer>
    struct allocator_void_pointer<Alloc, Pointer,
                                  std::void_t<typename Alloc::void_pointer>> {
        using type = typename Alloc::void_pointer;
    };

    template <typename Alloc, typename Pointer>
    using allocator_void_pointer_t =
        typename allocator_void_pointer<Alloc, Pointer>::type;

    template <typename Alloc, typename Pointer, typename = void>
    struct allocator_const_void_pointer {
        using type =
            typename std::pointer_traits<Pointer>::template rebind<const void>;
    };

    template <typename Alloc, typename Pointer>
    struct allocator_const_void_pointer<
        Alloc, Pointer, std::void_t<typename Alloc::const_void_pointer>> {
        using type = typename Alloc::const_void_pointer;
    };

    template <typename Alloc, typename Pointer>
    using allocator_const_void_pointer_t =
        typename allocator_const_void_pointer<Alloc, Pointer>::type;

    template <typename Alloc, typename = void>
    struct allocator_difference_type {
        using type = std::ptrdiff_t;
    };
    template <typename Alloc>
    struct allocator_difference_type<
        Alloc, std::void_t<typename Alloc::difference_type>> {
        using type = typename Alloc::difference_type;
    };
    template <typename Alloc>
    using allocator_difference_type_t =
        typename allocator_difference_type<Alloc>::type;

    template <typename Alloc, typename = void>
    struct allocator_size_type {
        using type = std::size_t;
    };
    template <typename Alloc>
    struct allocator_size_type<Alloc, std::void_t<typename Alloc::size_type>> {
        using type = typename Alloc::size_type;
    };
    template <typename Alloc>
    using allocator_size_type_t = typename allocator_size_type<Alloc>::type;

    template <typename Alloc, typename = void>
    struct allocator_prop_copy {
        using type = std::false_type;
    };
    template <typename Alloc>
    struct allocator_prop_copy<
        Alloc,
        std::void_t<typename Alloc::propagate_on_container_copy_assignment>> {
        using type = typename Alloc::propagate_on_container_copy_assignment;
    };
    template <typename Alloc>
    using allocator_prop_copy_t = typename allocator_prop_copy<Alloc>::type;

    template <typename Alloc, typename = void>
    struct allocator_prop_move {
        using type = std::false_type;
    };
    template <typename Alloc>
    struct allocator_prop_move<
        Alloc,
        std::void_t<typename Alloc::propagate_on_container_move_assignment>> {
        using type = typename Alloc::propagate_on_container_move_assignment;
    };
    template <typename Alloc>
    using allocator_prop_move_t = typename allocator_prop_move<Alloc>::type;

    template <typename Alloc, typename = void>
    struct allocator_prop_swap {
        using type = std::false_type;
    };
    template <typename Alloc>
    struct allocator_prop_swap<
        Alloc, std::void_t<typename Alloc::propagate_on_container_swap>> {
        using type = typename Alloc::propagate_on_container_swap;
    };
    template <typename Alloc>
    using allocator_prop_swap_t = typename allocator_prop_swap<Alloc>::type;

    template <typename Alloc, typename = void>
    struct is_always_equal {
        using type = std::false_type;
    };
    template <typename Alloc>
    struct is_always_equal<Alloc,
                           std::void_t<typename Alloc::is_always_equal>> {
        using type = typename Alloc::is_always_equal;
    };
    template <typename Alloc>
    using is_always_equal_t = typename is_always_equal<Alloc>::type;

    template <typename Alloc, typename = void>
    struct allocator_select_on_copy {
        static constexpr Alloc get(const Alloc& alloc) { return alloc; }
    };

    template <typename Alloc>
    struct allocator_select_on_copy<
        Alloc,
        std::enable_if_t<std::is_same_v<
            void,
            std::void_t<
                decltype(std::declval<Alloc>()
                             .select_on_container_copy_construction())>>>> {
        static constexpr Alloc get(const Alloc& alloc) {
            return alloc.select_on_container_copy_construction();
        }
    };

    template <typename Allocator, typename T, typename = void>
    struct allocator_rebind_type {
        using type = typename rebind<Allocator, T>::type;
    };

    template <typename Allocator, typename T>
    struct allocator_rebind_type<
        Allocator, T,
        std::void_t<typename Allocator::template rebind<T>::other>> {
        using type = typename Allocator::template rebind<T>::other;
    };

} // namespace __helpers

template <typename T>
struct allocator {
    using value_type = T;
    using propagate_on_container_move_assignment = std::true_type;

    constexpr allocator() noexcept = default;
    constexpr allocator(const allocator& other) noexcept = default;

    template <typename U>
    constexpr allocator(const allocator<U>&) noexcept {}

    constexpr ~allocator() = default;

    // throws std::bad_alloc
    [[nodiscard]] constexpr T* allocate(std::size_t n) {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    // TODO: check allocated size
    constexpr void deallocate(T* ptr, std::size_t) { ::operator delete(ptr); }
};

template <typename T1, typename T2>
constexpr bool operator==(const allocator<T1>&, const allocator<T2>&) noexcept {
    return true;
}

template <typename T, typename... Args>
constexpr std::enable_if_t<
    std::is_same_v<T*, decltype(::new(std::declval<void*>())
                                    T(std::declval<Args>()...))>,
    T*>
construct_at(T* p, Args&&... args) {
    return ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}

template <typename T>
constexpr void destroy_at(T* p) {
    // TODO: destroy array
    p->~T();
}

template <typename Allocator>
struct allocator_traits {
    using allocator_type = Allocator;
    using value_type = typename Allocator::value_type;
    using pointer = __helpers::allocator_pointer_t<Allocator>;
    using const_pointer =
        __helpers::allocator_const_pointer_t<Allocator, pointer>;
    using void_pointer =
        __helpers::allocator_void_pointer_t<Allocator, pointer>;
    using const_void_pointer =
        __helpers::allocator_const_void_pointer_t<Allocator, pointer>;
    using difference_type = __helpers::allocator_difference_type_t<Allocator>;
    using size_type = __helpers::allocator_size_type_t<Allocator>;
    using propagate_on_container_copy_assignment =
        __helpers::allocator_prop_copy_t<Allocator>;
    using propagate_on_container_move_assignment =
        __helpers::allocator_prop_move_t<Allocator>;
    using propagate_on_container_swap =
        __helpers::allocator_prop_swap_t<Allocator>;
    using is_always_equal = __helpers::is_always_equal_t<Allocator>;

    template <typename T>
    using rebind_alloc =
        typename __helpers::allocator_rebind_type<Allocator, T>::type;

    [[nodiscard]] static constexpr pointer allocate(Allocator& alloc,
                                                    size_type n) {
        return alloc.allocate(n);
    }
    static constexpr void deallocate(Allocator& alloc, pointer p, size_type n) {
        return alloc.deallocate(p, n);
    }
    template <typename T, typename... Args>
    static constexpr void construct(Allocator&, T* p, Args&&... args) {
        std::construct_at(p, std::forward<Args>(args)...);
    }
    template <typename T>
    static constexpr void destroy(Allocator&, T* p) {
        std::destroy_at(p);
    }

    static constexpr Allocator select_on_container_copy_construction(
        const Allocator& alloc) {
        return __helpers::allocator_select_on_copy<Allocator>::get(alloc);
    }
};

template <typename T>
struct default_delete {
    __GBLIBCPP_CONSTEXPR default_delete() noexcept = default;

    template <typename U,
              std::enable_if_t<std::is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR default_delete(const default_delete<U>&) noexcept {}

    __GBLIBCPP_CONSTEXPR void operator()(T* p) const { delete p; }
};

// TODO: weak_ptr
template <typename T>
class shared_ptr {
   public:
    using element_type = std::remove_extent_t<T>;
    using pointer = element_type*; // TODO: pointer_traits
    using const_pointer = const element_type*;
    using reference = element_type&;
    using const_reference = const element_type&;

   private:
    struct control_block_base {
        std::size_t ref_count;
        std::size_t weak_count;
        pointer ptr;

        constexpr control_block_base(std::size_t ref_count,
                                     std::size_t weak_count, pointer ptr)
            : ref_count(ref_count), weak_count(weak_count), ptr(ptr) {}

        virtual constexpr ~control_block_base() = default;
        virtual constexpr void do_delete() = 0;

        // template <typename U, std::enable_if_t<std::is_convertible_v<T*, U*>,
        // bool> = true> constexpr operator typename
        // shared_ptr<U>::control_block_base*() const
        // {
        //     return this;
        // }
    };

    template <typename Deleter>
    struct control_block : public virtual control_block_base, private Deleter {
        using Base = control_block_base;

        virtual constexpr ~control_block() = default;

        constexpr control_block(std::size_t ref_count, std::size_t weak_count,
                                pointer ptr)
            : control_block_base{ref_count, weak_count, ptr}, Deleter{} {}

        template <typename UDeleter>
        constexpr control_block(std::size_t ref_count, std::size_t weak_count,
                                pointer ptr, UDeleter&& deleter)
            : control_block_base{ref_count, weak_count, ptr}
            , Deleter(std::forward<UDeleter>(deleter)) {}

        virtual constexpr void do_delete() override {
            if (this->Base::ptr)
                this->Deleter::operator()(this->Base::ptr);
            this->Base::ptr = nullptr;
        }
    };

    control_block_base* cb{};
    pointer ptr{};

    void inc_ref() {
        if (cb)
            ++cb->ref_count; // TODO: lock and atomic
    }

    void dec_ref() {
        if (cb && --cb->ref_count == 0) {
            cb->do_delete();
            if (cb->weak_count == 0)
                delete cb;
        }
    }

   private:
    template <typename Allocator, typename Deleter>
    using rebound_allocator = typename std::allocator_traits<
        Allocator>::template rebind_alloc<control_block<Deleter>>;

    template <typename Allocator, typename Deleter>
    using rebound_traits =
        typename std::allocator_traits<rebound_allocator<Allocator, Deleter>>;

    template <typename U>
    friend class shared_ptr;

   public:
    __GBLIBCPP_CONSTEXPR shared_ptr() noexcept = default;
    __GBLIBCPP_CONSTEXPR shared_ptr(std::nullptr_t) noexcept : shared_ptr{} {}

    template <typename U, enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR explicit shared_ptr(U* p) // TODO: array type
        : cb(new control_block<default_delete<T>>{1, 0, p}), ptr(p) {}

    template <typename U, typename Deleter,
              enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR explicit shared_ptr(U* p, Deleter d)
        : cb(new control_block<Deleter>{1, 0, p, d}), ptr(p) {}

    template <typename Deleter>
    __GBLIBCPP_CONSTEXPR explicit shared_ptr(std::nullptr_t, Deleter d)
        : cb(new control_block<Deleter>{1, 0, nullptr, d}) {}

    template <typename U, typename Deleter, typename Alloc,
              enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR explicit shared_ptr(U* p, Deleter d, Alloc alloc) {
        cb = rebound_traits<Alloc, Deleter>::allocate(alloc, 1);
        rebound_traits<Alloc, Deleter>::construct(alloc, cb, 1, 0, p, d);

        ptr = p;
    }

    template <typename Deleter, typename Alloc>
    __GBLIBCPP_CONSTEXPR explicit shared_ptr(std::nullptr_t, Deleter d,
                                             Alloc alloc) {
        cb = rebound_traits<Alloc, Deleter>::allocate(alloc, 1);
        rebound_traits<Alloc, Deleter>::construct(alloc, cb, 1, 0, nullptr, d);
    }

    __GBLIBCPP_CONSTEXPR
    shared_ptr(const shared_ptr& other) noexcept
        : cb(other.cb), ptr(other.ptr) {
        inc_ref();
    }

    __GBLIBCPP_CONSTEXPR
    shared_ptr(shared_ptr&& other) noexcept
        : cb(std::exchange(other.cb, nullptr))
        , ptr(std::exchange(other.ptr, nullptr)) {}

    template <typename U, enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR shared_ptr(const shared_ptr<U>& other) noexcept
        : cb((control_block_base*)other.cb), ptr((T*)other.ptr) {
        inc_ref();
    }

    template <typename U, enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR shared_ptr(shared_ptr<U>&& other) noexcept
        : cb((control_block_base*)std::exchange(other.cb, nullptr))
        , ptr((T*)std::exchange(other.ptr, nullptr)) {}

    // TODO: weak_ptr and unique_ptr

    __GBLIBCPP_CONSTEXPR
    ~shared_ptr() {
        dec_ref();
        cb = nullptr;
        ptr = nullptr;
    }

    __GBLIBCPP_CONSTEXPR
    shared_ptr& operator=(const shared_ptr& other) noexcept {
        shared_ptr{other}.swap(*this);
        return *this;
    }

    template <typename U, enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR shared_ptr& operator=(
        const shared_ptr<U>& other) noexcept {
        shared_ptr{other}.swap(*this);
        return *this;
    }

    __GBLIBCPP_CONSTEXPR
    shared_ptr& operator=(shared_ptr&& other) noexcept {
        shared_ptr{move(other)}.swap(*this);
        return *this;
    }

    template <typename U, enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR shared_ptr& operator=(shared_ptr<U>&& other) noexcept {
        shared_ptr{move(other)}.swap(*this);
        return *this;
    }

    __GBLIBCPP_CONSTEXPR
    element_type* get() const noexcept { return cb ? ptr : nullptr; }

    __GBLIBCPP_CONSTEXPR
    explicit operator bool() const noexcept { return get(); }

    __GBLIBCPP_CONSTEXPR
    T& operator*() const noexcept { return *get(); }
    __GBLIBCPP_CONSTEXPR
    T* operator->() const noexcept { return get(); }
    __GBLIBCPP_CONSTEXPR
    element_type& operator[](std::size_t i) const noexcept { return get()[i]; }

    __GBLIBCPP_CONSTEXPR
    long use_count() const noexcept { return cb ? cb->ref_count : 0; }

    __GBLIBCPP_CONSTEXPR
    bool owner_before(const shared_ptr& other) const noexcept {
        return cb < other.cb;
    }

    __GBLIBCPP_CONSTEXPR
    void swap(shared_ptr& other) noexcept {
        std::swap(cb, other.cb);
        std::swap(ptr, other.ptr);
    }

    __GBLIBCPP_CONSTEXPR
    void reset() noexcept { shared_ptr{}.swap(*this); }

    template <typename U, enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR void reset(U* p) noexcept {
        shared_ptr{p}.swap(*this);
    }

    template <typename U, typename Deleter,
              enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR void reset(U* p, Deleter d) noexcept {
        shared_ptr{p, d}.swap(*this);
    }

    template <typename U, typename Deleter, typename Allocator,
              enable_if_t<is_convertible_v<U*, T*>, bool> = true>
    __GBLIBCPP_CONSTEXPR void reset(U* p, Deleter d, Allocator alloc) {
        shared_ptr{p, d, alloc}.swap(*this);
    }
};

template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr {
   public:
    using element_type = T;
    using deleter_type = Deleter;
    using pointer = element_type*;

    template <typename U, typename UDeleter>
    friend class unique_ptr;

   private:
    impl::compressed_pair<pointer, deleter_type> data;

   public:
    __GBLIBCPP_CONSTEXPR unique_ptr() noexcept
        : data{impl::default_construct_t{}} {}
    __GBLIBCPP_CONSTEXPR unique_ptr(std::nullptr_t) noexcept : unique_ptr{} {}
    __GBLIBCPP_CONSTEXPR unique_ptr(pointer p) noexcept
        : data{p, deleter_type{}} {}
    __GBLIBCPP_CONSTEXPR unique_ptr(pointer p, const deleter_type& d) noexcept
        : data{p, d} {}
    __GBLIBCPP_CONSTEXPR unique_ptr(pointer p, deleter_type&& d) noexcept
        : data{p, std::move(d)} {}
    __GBLIBCPP_CONSTEXPR unique_ptr(const unique_ptr&) = delete;

    __GBLIBCPP_CONSTEXPR unique_ptr(unique_ptr&& other) noexcept
        : data{std::exchange(other.data.first(), nullptr),
               std::move(other.data.second())} {}

    template <
        typename U, typename E,
        std::enable_if_t<!std::is_array_v<U> && std::is_convertible_v<U*, T*> &&
                             std::is_convertible_v<E, Deleter>,
                         bool> = true>
    __GBLIBCPP_CONSTEXPR unique_ptr(unique_ptr<U, E>&& other) noexcept
        : data{std::exchange(other.data.first(), nullptr),
               std::move(other.data.second())} {}

    __GBLIBCPP_CONSTEXPR ~unique_ptr() { reset(); }

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

    __GBLIBCPP_CONSTEXPR unique_ptr& operator=(unique_ptr&& other) noexcept {
        reset(other.release());
        get_deleter() = std::move(other.get_deleter());
        return *this;
    }

    template <
        typename U, typename E,
        std::enable_if_t<!std::is_array_v<U> && std::is_convertible_v<U*, T*> &&
                             std::is_assignable_v<Deleter&, E&&>,
                         bool> = true>
    __GBLIBCPP_CONSTEXPR unique_ptr& operator=(
        unique_ptr<U, E>&& other) noexcept {
        reset(other.release());
        get_deleter() = std::move(other.get_deleter());
        return *this;
    }

    __GBLIBCPP_CONSTEXPR void swap(unique_ptr& other) noexcept {
        std::swap(data, other.data);
    }

    __GBLIBCPP_CONSTEXPR pointer get() const noexcept { return data.first(); }
    __GBLIBCPP_CONSTEXPR deleter_type& get_deleter() noexcept {
        return data.second();
    }
    __GBLIBCPP_CONSTEXPR const deleter_type& get_deleter() const noexcept {
        return data.second();
    }

    __GBLIBCPP_CONSTEXPR pointer release() noexcept {
        pointer ret = get();
        data.first() = nullptr;
        return ret;
    }

    __GBLIBCPP_CONSTEXPR void reset(pointer p = pointer{}) noexcept {
        pointer old = release();
        data.first() = p;
        if (old)
            get_deleter()(old);
    }

    __GBLIBCPP_CONSTEXPR explicit operator bool() const noexcept {
        return get();
    }
    __GBLIBCPP_CONSTEXPR pointer operator->() const noexcept { return get(); }

    __GBLIBCPP_CONSTEXPR std::add_lvalue_reference_t<T> operator*() const
        noexcept(noexcept(*std::declval<pointer>())) {
        return *get();
    }
};

// TODO: use only one allocation
template <typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args) {
    return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}

template <typename T, typename... Args>
__GBLIBCPP_CONSTEXPR std::unique_ptr<T> make_unique(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template <typename T>
__GBLIBCPP_CONSTEXPR void swap(std::shared_ptr<T>& lhs,
                               std::shared_ptr<T>& rhs) noexcept {
    lhs.swap(rhs);
}

template <typename T, typename D>
__GBLIBCPP_CONSTEXPR void swap(std::unique_ptr<T, D>& lhs,
                               std::unique_ptr<T, D>& rhs) noexcept {
    lhs.swap(rhs);
}

} // namespace std

#endif
