// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, 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 _LIBCUDACXX_OPTIONAL
#define _LIBCUDACXX_OPTIONAL

/*
    optional synopsis

// C++1z

namespace std {
  // 23.6.3, optional for object types
  template <class T> class optional;

  // 23.6.4, no-value state indicator
  struct nullopt_t{see below };
  inline constexpr nullopt_t nullopt(unspecified );

  // 23.6.5, class bad_optional_access
  class bad_optional_access;

  // 23.6.6, relational operators
  template <class T, class U>
  constexpr bool operator==(const optional<T>&, const optional<U>&);
  template <class T, class U>
  constexpr bool operator!=(const optional<T>&, const optional<U>&);
  template <class T, class U>
  constexpr bool operator<(const optional<T>&, const optional<U>&);
  template <class T, class U>
  constexpr bool operator>(const optional<T>&, const optional<U>&);
  template <class T, class U>
  constexpr bool operator<=(const optional<T>&, const optional<U>&);
  template <class T, class U>
  constexpr bool operator>=(const optional<T>&, const optional<U>&);

  // 23.6.7 comparison with nullopt
  template <class T> constexpr bool operator==(const optional<T>&, nullopt_t) noexcept;
  template <class T> constexpr bool operator==(nullopt_t, const optional<T>&) noexcept;
  template <class T> constexpr bool operator!=(const optional<T>&, nullopt_t) noexcept;
  template <class T> constexpr bool operator!=(nullopt_t, const optional<T>&) noexcept;
  template <class T> constexpr bool operator<(const optional<T>&, nullopt_t) noexcept;
  template <class T> constexpr bool operator<(nullopt_t, const optional<T>&) noexcept;
  template <class T> constexpr bool operator<=(const optional<T>&, nullopt_t) noexcept;
  template <class T> constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept;
  template <class T> constexpr bool operator>(const optional<T>&, nullopt_t) noexcept;
  template <class T> constexpr bool operator>(nullopt_t, const optional<T>&) noexcept;
  template <class T> constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept;
  template <class T> constexpr bool operator>=(nullopt_t, const optional<T>&) noexcept;

  // 23.6.8, comparison with T
  template <class T, class U> constexpr bool operator==(const optional<T>&, const U&);
  template <class T, class U> constexpr bool operator==(const T&, const optional<U>&);
  template <class T, class U> constexpr bool operator!=(const optional<T>&, const U&);
  template <class T, class U> constexpr bool operator!=(const T&, const optional<U>&);
  template <class T, class U> constexpr bool operator<(const optional<T>&, const U&);
  template <class T, class U> constexpr bool operator<(const T&, const optional<U>&);
  template <class T, class U> constexpr bool operator<=(const optional<T>&, const U&);
  template <class T, class U> constexpr bool operator<=(const T&, const optional<U>&);
  template <class T, class U> constexpr bool operator>(const optional<T>&, const U&);
  template <class T, class U> constexpr bool operator>(const T&, const optional<U>&);
  template <class T, class U> constexpr bool operator>=(const optional<T>&, const U&);
  template <class T, class U> constexpr bool operator>=(const T&, const optional<U>&);

  // 23.6.9, specialized algorithms
  template <class T> void swap(optional<T>&, optional<T>&) noexcept(see below ); // constexpr in C++20
  template <class T> constexpr optional<see below > make_optional(T&&);
  template <class T, class... Args>
    constexpr optional<T> make_optional(Args&&... args);
  template <class T, class U, class... Args>
    constexpr optional<T> make_optional(initializer_list<U> il, Args&&... args);

  // 23.6.10, hash support
  template <class T> struct hash;
  template <class T> struct hash<optional<T>>;

  template <class T> class optional {
  public:
    using value_type = T;

    // 23.6.3.1, constructors
    constexpr optional() noexcept;
    constexpr optional(nullopt_t) noexcept;
    constexpr optional(const optional &);
    constexpr optional(optional &&) noexcept(see below);
    template <class... Args> constexpr explicit optional(in_place_t, Args &&...);
    template <class U, class... Args>
      constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
    template <class U = T>
      constexpr explicit(see-below) optional(U &&);
    template <class U>
      explicit(see-below) optional(const optional<U> &);         // constexpr in C++20
    template <class U>
      explicit(see-below) optional(optional<U> &&);              // constexpr in C++20

    // 23.6.3.2, destructor
    ~optional(); // constexpr in C++20

    // 23.6.3.3, assignment
    optional &operator=(nullopt_t) noexcept;                     // constexpr in C++20
    constexpr optional &operator=(const optional &);
    constexpr optional &operator=(optional &&) noexcept(see below);
    template <class U = T> optional &operator=(U &&);            // constexpr in C++20
    template <class U> optional &operator=(const optional<U> &); // constexpr in C++20
    template <class U> optional &operator=(optional<U> &&);      // constexpr in C++20
    template <class... Args> T& emplace(Args &&...);             // constexpr in C++20
    template <class U, class... Args>
      T& emplace(initializer_list<U>, Args &&...);               // constexpr in C++20

    // 23.6.3.4, swap
    void swap(optional &) noexcept(see below ); // constexpr in C++20

    // 23.6.3.5, observers
    constexpr T const *operator->() const;
    constexpr T *operator->();
    constexpr T const &operator*() const &;
    constexpr T &operator*() &;
    constexpr T &&operator*() &&;
    constexpr const T &&operator*() const &&;
    constexpr explicit operator bool() const noexcept;
    constexpr bool has_value() const noexcept;
    constexpr T const &value() const &;
    constexpr T &value() &;
    constexpr T &&value() &&;
    constexpr const T &&value() const &&;
    template <class U> constexpr T value_or(U &&) const &;
    template <class U> constexpr T value_or(U &&) &&;

    // [optional.monadic], monadic operations
    template<class F> constexpr auto and_then(F&& f) &;         // since C++23
    template<class F> constexpr auto and_then(F&& f) &&;        // since C++23
    template<class F> constexpr auto and_then(F&& f) const&;    // since C++23
    template<class F> constexpr auto and_then(F&& f) const&&;   // since C++23
    template<class F> constexpr auto transform(F&& f) &;        // since C++23
    template<class F> constexpr auto transform(F&& f) &&;       // since C++23
    template<class F> constexpr auto transform(F&& f) const&;   // since C++23
    template<class F> constexpr auto transform(F&& f) const&&;  // since C++23
    template<class F> constexpr optional or_else(F&& f) &&;     // since C++23
    template<class F> constexpr optional or_else(F&& f) const&; // since C++23

    // 23.6.3.6, modifiers
    void reset() noexcept; // constexpr in C++20

  private:
    T *val; // exposition only
  };

template<class T>
  optional(T) -> optional<T>;

} // namespace std

*/

#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/std/__concepts/__concept_macros.h>
#include <cuda/std/__concepts/invocable.h>
#include <cuda/std/__exception/terminate.h>
#include <cuda/std/__functional/hash.h>
#include <cuda/std/__functional/invoke.h>
#include <cuda/std/__functional/unary_function.h>
#include <cuda/std/__memory/addressof.h>
#include <cuda/std/__memory/construct_at.h>
#include <cuda/std/__new_>
#include <cuda/std/__tuple_dir/sfinae_helpers.h>
#include <cuda/std/__type_traits/conjunction.h>
#include <cuda/std/__type_traits/disjunction.h>
#include <cuda/std/__type_traits/is_copy_assignable.h>
#include <cuda/std/__type_traits/is_copy_constructible.h>
#include <cuda/std/__type_traits/is_move_assignable.h>
#include <cuda/std/__type_traits/is_move_constructible.h>
#include <cuda/std/__type_traits/is_object.h>
#include <cuda/std/__type_traits/is_trivially_copy_assignable.h>
#include <cuda/std/__type_traits/is_trivially_copy_constructible.h>
#include <cuda/std/__type_traits/is_trivially_destructible.h>
#include <cuda/std/__type_traits/is_trivially_move_assignable.h>
#include <cuda/std/__type_traits/is_trivially_move_constructible.h>
#include <cuda/std/__type_traits/negation.h>
#include <cuda/std/__type_traits/remove_cv.h>
#include <cuda/std/__utility/declval.h>
#include <cuda/std/__utility/forward.h>
#include <cuda/std/__utility/in_place.h>
#include <cuda/std/__utility/move.h>
#include <cuda/std/__utility/swap.h>
#include <cuda/std/cstdlib>
#include <cuda/std/detail/libcxx/include/__assert> // all public C++ headers provide the assertion handler
#include <cuda/std/detail/libcxx/include/__availability>
#include <cuda/std/detail/libcxx/include/__verbose_abort>
#include <cuda/std/initializer_list>

// standard-mandated includes
#include <cuda/std/version>

// [optional.syn]
#ifndef _LIBCUDACXX_HAS_NO_SPACESHIP_OPERATOR
#  include <cuda/std/compare>
#endif // !_LIBCUDACXX_HAS_NO_SPACESHIP_OPERATOR

_CCCL_PUSH_MACROS

#ifndef _LIBCUDACXX_NO_EXCEPTIONS
#  ifdef __cpp_lib_optional
#    include <optional>
#  else // ^^^ __cpp_lib_optional ^^^ / vvv !__cpp_lib_optional vvv
#    include <exception>
#  endif // !__cpp_lib_optional

_LIBCUDACXX_BEGIN_NAMESPACE_STD_NOVERSION

#  ifdef __cpp_lib_optional

using ::std::bad_optional_access;

#  else // ^^^ __cpp_lib_optional ^^^ / vvv !__cpp_lib_optional vvv
class _LIBCUDACXX_TYPE_VIS _LIBCUDACXX_AVAILABILITY_BAD_OPTIONAL_ACCESS bad_optional_access : public ::std::exception
{
public:
  const char* what() const noexcept override
  {
    return "bad access to cuda::std::optional";
  }
};
#  endif // !__cpp_lib_optional

_LIBCUDACXX_END_NAMESPACE_STD_NOVERSION

#endif // _LIBCUDACXX_NO_EXCEPTIONS

#if _CCCL_STD_VER > 2011

_LIBCUDACXX_BEGIN_NAMESPACE_STD

_CCCL_NORETURN inline _LIBCUDACXX_INLINE_VISIBILITY void __throw_bad_optional_access()
{
#  ifndef _LIBCUDACXX_NO_EXCEPTIONS
  NV_IF_ELSE_TARGET(
    NV_IS_HOST, (throw _CUDA_VSTD_NOVERSION::bad_optional_access();), (_CUDA_VSTD_NOVERSION::terminate();))
#  else
  _CUDA_VSTD_NOVERSION::terminate();
#  endif // !_LIBCUDACXX_NO_EXCEPTIONS
}

struct nullopt_t
{
  struct __secret_tag
  {
    explicit __secret_tag() = default;
  };
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
};

_LIBCUDACXX_CPO_ACCESSIBILITY nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};

struct __optional_construct_from_invoke_tag
{};

template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
struct __optional_destruct_base;

template <class _Tp>
struct __optional_destruct_base<_Tp, false>
{
  typedef _Tp value_type;
  static_assert(_CCCL_TRAIT(is_object, value_type),
                "instantiation of optional with a non-object type is undefined behavior");
  union
  {
    char __null_state_;
    __remove_cv_t<value_type> __val_;
  };
  bool __engaged_;

  _CCCL_EXEC_CHECK_DISABLE
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX20 ~__optional_destruct_base()
  {
    if (__engaged_)
    {
      __val_.~value_type();
    }
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_destruct_base() noexcept
      : __null_state_()
      , __engaged_(false)
  {}

  template <class... _Args>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
      : __val_(_CUDA_VSTD::forward<_Args>(__args)...)
      , __engaged_(true)
  {}

  template <class _Fp, class... _Args>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_destruct_base(
    __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
      : __val_(_CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Fp>(__f), _CUDA_VSTD::forward<_Args>(__args)...))
      , __engaged_(true)
  {}

  _CCCL_EXEC_CHECK_DISABLE
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX20 void reset() noexcept
  {
    if (__engaged_)
    {
      __val_.~value_type();
      __engaged_ = false;
    }
  }
};

template <class _Tp>
struct __optional_destruct_base<_Tp, true>
{
  typedef _Tp value_type;
  static_assert(_CCCL_TRAIT(is_object, value_type),
                "instantiation of optional with a non-object type is undefined behavior");
  union
  {
    char __null_state_;
    __remove_cv_t<value_type> __val_;
  };
  bool __engaged_;

  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_destruct_base() noexcept
      : __null_state_()
      , __engaged_(false)
  {}

  template <class... _Args>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
      : __val_(_CUDA_VSTD::forward<_Args>(__args)...)
      , __engaged_(true)
  {}

  template <class _Fp, class... _Args>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_destruct_base(
    __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
      : __val_(_CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Fp>(__f), _CUDA_VSTD::forward<_Args>(__args)...))
      , __engaged_(true)
  {}

  _LIBCUDACXX_INLINE_VISIBILITY constexpr void reset() noexcept
  {
    if (__engaged_)
    {
      __engaged_ = false;
    }
  }
};

template <class _Tp>
struct __optional_storage_base : __optional_destruct_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_storage_base, __optional_destruct_base, _Tp);

  using value_type = _Tp;

  _LIBCUDACXX_INLINE_VISIBILITY constexpr bool has_value() const noexcept
  {
    return this->__engaged_;
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr value_type& __get() & noexcept
  {
    return this->__val_;
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr const value_type& __get() const& noexcept
  {
    return this->__val_;
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr value_type&& __get() && noexcept
  {
    return _CUDA_VSTD::move(this->__val_);
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr const value_type&& __get() const&& noexcept
  {
    return _CUDA_VSTD::move(this->__val_);
  }

  template <class... _Args>
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX20 void __construct(_Args&&... __args)
  {
    _LIBCUDACXX_ASSERT(!has_value(), "__construct called for engaged __optional_storage");
#  if _CCCL_STD_VER > 2017
    _CUDA_VSTD::construct_at(_CUDA_VSTD::addressof(this->__val_), _CUDA_VSTD::forward<_Args>(__args)...);
#  else
    ::new ((void*) _CUDA_VSTD::addressof(this->__val_)) value_type(_CUDA_VSTD::forward<_Args>(__args)...);
#  endif
    this->__engaged_ = true;
  }

  template <class _That>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr void __construct_from(_That&& __opt)
  {
    if (__opt.has_value())
    {
      __construct(_CUDA_VSTD::forward<_That>(__opt).__get());
    }
  }

  template <class _That>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr void __assign_from(_That&& __opt)
  {
    if (this->__engaged_ == __opt.has_value())
    {
      if (this->__engaged_)
      {
        this->__val_ = _CUDA_VSTD::forward<_That>(__opt).__get();
      }
    }
    else
    {
      if (this->__engaged_)
      {
        this->reset();
      }
      else
      {
        __construct(_CUDA_VSTD::forward<_That>(__opt).__get());
      }
    }
  }
};

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __optional_can_copy_construct =
  _CCCL_TRAIT(is_trivially_copy_constructible, _Tp) ? __smf_availability::__trivial
  : _CCCL_TRAIT(is_copy_constructible, _Tp)
    ? __smf_availability::__available
    : __smf_availability::__deleted;

template <class _Tp, __smf_availability = __optional_can_copy_construct<_Tp>>
struct __optional_copy_base : __optional_storage_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_copy_base, __optional_storage_base, _Tp);
};

template <class _Tp>
struct __optional_copy_base<_Tp, __smf_availability::__available> : __optional_storage_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_copy_base, __optional_storage_base, _Tp);

  // This ctor shouldn't need to initialize the base explicitly, but g++ 9 considers it to be uninitialized
  // during constexpr evaluation if it isn't initialized explicitly. This can be replaced with the pattern
  // below, in __optional_move_base, once g++ 9 falls off our support matrix.
  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_copy_base(const __optional_copy_base& __opt)
      : __base()
  {
    this->__construct_from(__opt);
  }

  __optional_copy_base(__optional_copy_base&&)                 = default;
  __optional_copy_base& operator=(const __optional_copy_base&) = default;
  __optional_copy_base& operator=(__optional_copy_base&&)      = default;
};

template <class _Tp>
struct __optional_copy_base<_Tp, __smf_availability::__deleted> : __optional_storage_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_copy_base, __optional_storage_base, _Tp);
  __optional_copy_base(const __optional_copy_base&)            = delete;
  __optional_copy_base(__optional_copy_base&&)                 = default;
  __optional_copy_base& operator=(const __optional_copy_base&) = default;
  __optional_copy_base& operator=(__optional_copy_base&&)      = default;
};

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __optional_can_move_construct =
  _CCCL_TRAIT(is_trivially_move_constructible, _Tp) ? __smf_availability::__trivial
  : _CCCL_TRAIT(is_move_constructible, _Tp)
    ? __smf_availability::__available
    : __smf_availability::__deleted;

template <class _Tp, __smf_availability = __optional_can_move_construct<_Tp>>
struct __optional_move_base : __optional_copy_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_move_base, __optional_copy_base, _Tp);
};

template <class _Tp>
struct __optional_move_base<_Tp, __smf_availability::__available> : __optional_copy_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_move_base, __optional_copy_base, _Tp);

  __optional_move_base(const __optional_move_base&) = default;

  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_move_base(__optional_move_base&& __opt) noexcept(
    _CCCL_TRAIT(is_nothrow_move_constructible, _Tp))
  {
    this->__construct_from(_CUDA_VSTD::move(__opt));
  }

  __optional_move_base& operator=(const __optional_move_base&) = default;
  __optional_move_base& operator=(__optional_move_base&&)      = default;
};

template <class _Tp>
struct __optional_move_base<_Tp, __smf_availability::__deleted> : __optional_copy_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_move_base, __optional_copy_base, _Tp);

  __optional_move_base(const __optional_move_base&)            = default;
  __optional_move_base(__optional_move_base&&)                 = delete;
  __optional_move_base& operator=(const __optional_move_base&) = default;
  __optional_move_base& operator=(__optional_move_base&&)      = default;
};

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __optional_can_copy_assign =
  _CCCL_TRAIT(is_trivially_destructible, _Tp) && _CCCL_TRAIT(is_trivially_copy_constructible, _Tp)
      && _CCCL_TRAIT(is_trivially_copy_assignable, _Tp)
    ? __smf_availability::__trivial
  : _CCCL_TRAIT(is_destructible, _Tp) && _CCCL_TRAIT(is_copy_constructible, _Tp) && _CCCL_TRAIT(is_copy_assignable, _Tp)
    ? __smf_availability::__available
    : __smf_availability::__deleted;

template <class _Tp, __smf_availability = __optional_can_copy_assign<_Tp>>
struct __optional_copy_assign_base : __optional_move_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_copy_assign_base, __optional_move_base, _Tp);
};

template <class _Tp>
struct __optional_copy_assign_base<_Tp, __smf_availability::__available> : __optional_move_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_copy_assign_base, __optional_move_base, _Tp);

  __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
  __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;

  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_copy_assign_base&
  operator=(const __optional_copy_assign_base& __opt)
  {
    this->__assign_from(__opt);
    return *this;
  }

  __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
};

template <class _Tp>
struct __optional_copy_assign_base<_Tp, __smf_availability::__deleted> : __optional_move_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_copy_assign_base, __optional_move_base, _Tp);

  __optional_copy_assign_base(const __optional_copy_assign_base&)            = default;
  __optional_copy_assign_base(__optional_copy_assign_base&&)                 = default;
  __optional_copy_assign_base& operator=(const __optional_copy_assign_base&) = delete;
  __optional_copy_assign_base& operator=(__optional_copy_assign_base&&)      = default;
};

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __optional_can_move_assign =
  _CCCL_TRAIT(is_trivially_destructible, _Tp) && _CCCL_TRAIT(is_trivially_move_constructible, _Tp)
      && _CCCL_TRAIT(is_trivially_move_assignable, _Tp)
    ? __smf_availability::__trivial
  : _CCCL_TRAIT(is_destructible, _Tp) && _CCCL_TRAIT(is_move_constructible, _Tp) && _CCCL_TRAIT(is_move_assignable, _Tp)
    ? __smf_availability::__available
    : __smf_availability::__deleted;

template <class _Tp, __smf_availability = __optional_can_move_assign<_Tp>>
struct __optional_move_assign_base : __optional_copy_assign_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_move_assign_base, __optional_copy_assign_base, _Tp);
};

template <class _Tp>
struct __optional_move_assign_base<_Tp, __smf_availability::__available> : __optional_copy_assign_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_move_assign_base, __optional_copy_assign_base, _Tp);

  __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
  __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
  __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;

  _LIBCUDACXX_INLINE_VISIBILITY constexpr __optional_move_assign_base&
  operator=(__optional_move_assign_base&& __opt) noexcept(
    _CCCL_TRAIT(is_nothrow_move_assignable, _Tp) && _CCCL_TRAIT(is_nothrow_move_constructible, _Tp))
  {
    this->__assign_from(_CUDA_VSTD::move(__opt));
    return *this;
  }
};

template <class _Tp>
struct __optional_move_assign_base<_Tp, __smf_availability::__deleted> : __optional_copy_assign_base<_Tp>
{
  _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__optional_move_assign_base, __optional_copy_assign_base, _Tp);

  __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
  __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
  __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
  __optional_move_assign_base& operator=(__optional_move_assign_base&&)      = delete;
};

template <class _Tp>
class optional;
template <class _Tp>
struct __is_std_optional : false_type
{};
template <class _Tp>
struct __is_std_optional<optional<_Tp>> : true_type
{};

// Constraits
template <class _Tp, class _Up, class _Opt = optional<_Up>>
using __opt_check_constructible_from_opt =
  _Or<is_constructible<_Tp, _Opt&>,
      is_constructible<_Tp, _Opt const&>,
      is_constructible<_Tp, _Opt&&>,
      is_constructible<_Tp, _Opt const&&>,
      is_convertible<_Opt&, _Tp>,
      is_convertible<_Opt const&, _Tp>,
      is_convertible<_Opt&&, _Tp>,
      is_convertible<_Opt const&&, _Tp>>;

template <class _Tp, class _Up, class _Opt = optional<_Up>>
using __opt_check_assignable_from_opt =
  _Or<is_assignable<_Tp&, _Opt&>,
      is_assignable<_Tp&, _Opt const&>,
      is_assignable<_Tp&, _Opt&&>,
      is_assignable<_Tp&, _Opt const&&>>;

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_implictly_constructible =
  _CCCL_TRAIT(is_constructible, _Tp, _Up) && _CCCL_TRAIT(is_convertible, _Up, _Tp);

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_explictly_constructible =
  _CCCL_TRAIT(is_constructible, _Tp, _Up) && !_CCCL_TRAIT(is_convertible, _Up, _Tp);

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_constructible_from_U =
  !_CCCL_TRAIT(is_same, __remove_cvref_t<_Up>, in_place_t)
  && !_CCCL_TRAIT(is_same, __remove_cvref_t<_Up>, optional<_Tp>);

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_constructible_from_opt =
  !_CCCL_TRAIT(is_same, _Up, _Tp) && !__opt_check_constructible_from_opt<_Tp, _Up>::value;

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_assignable =
  _CCCL_TRAIT(is_constructible, _Tp, _Up) && _CCCL_TRAIT(is_assignable, _Tp&, _Up);

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_assignable_from_U =
  !_CCCL_TRAIT(is_same, __remove_cvref_t<_Up>, optional<_Tp>)
  && (!_CCCL_TRAIT(is_same, __remove_cvref_t<_Up>, _Tp) || !_CCCL_TRAIT(is_scalar, _Tp));

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VAR constexpr bool __opt_is_assignable_from_opt =
  !_CCCL_TRAIT(is_same, _Up, _Tp) && !__opt_check_constructible_from_opt<_Tp, _Up>::value
  && !__opt_check_assignable_from_opt<_Tp, _Up>::value;

template <class _Tp>
class optional : private __optional_move_assign_base<_Tp>
{
  using __base = __optional_move_assign_base<_Tp>;

  template <class>
  friend class optional;

public:
  using value_type = _Tp;

private:
  // Disable the reference extension using this static assert.
  static_assert(!_CCCL_TRAIT(is_same, __remove_cvref_t<value_type>, in_place_t),
                "instantiation of optional with in_place_t is ill-formed");
  static_assert(!_CCCL_TRAIT(is_same, __remove_cvref_t<value_type>, nullopt_t),
                "instantiation of optional with nullopt_t is ill-formed");
  static_assert(!_CCCL_TRAIT(is_reference, value_type),
                "instantiation of optional with a reference type is ill-formed");
  static_assert(_CCCL_TRAIT(is_destructible, value_type),
                "instantiation of optional with a non-destructible type is ill-formed");
  static_assert(!_CCCL_TRAIT(is_array, value_type), "instantiation of optional with an array type is ill-formed");

public:
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional() noexcept {}
  constexpr optional(const optional&) = default;
  constexpr optional(optional&&)      = default;
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional(nullopt_t) noexcept {}

  _LIBCUDACXX_TEMPLATE(class _In_place_t, class... _Args)
  _LIBCUDACXX_REQUIRES(_CCCL_TRAIT(is_same, _In_place_t, in_place_t)
                         _LIBCUDACXX_AND _CCCL_TRAIT(is_constructible, value_type, _Args...))
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit optional(_In_place_t, _Args&&... __args)
      : __base(in_place, _CUDA_VSTD::forward<_Args>(__args)...)
  {}

  _LIBCUDACXX_TEMPLATE(class _Up, class... _Args)
  _LIBCUDACXX_REQUIRES(_CCCL_TRAIT(is_constructible, value_type, initializer_list<_Up>&, _Args...))
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
      : __base(in_place, __il, _CUDA_VSTD::forward<_Args>(__args)...)
  {}

  _LIBCUDACXX_TEMPLATE(class _Up = value_type)
  _LIBCUDACXX_REQUIRES(
    __opt_is_constructible_from_U<_Tp, _Up> _LIBCUDACXX_AND __opt_is_implictly_constructible<_Tp, _Up>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional(_Up&& __v)
      : __base(in_place, _CUDA_VSTD::forward<_Up>(__v))
  {}

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(
    __opt_is_constructible_from_U<_Tp, _Up> _LIBCUDACXX_AND __opt_is_explictly_constructible<_Tp, _Up>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit optional(_Up&& __v)
      : __base(in_place, _CUDA_VSTD::forward<_Up>(__v))
  {}

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(
    __opt_is_constructible_from_opt<_Tp, _Up> _LIBCUDACXX_AND __opt_is_implictly_constructible<_Tp, const _Up&>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional(const optional<_Up>& __v)
  {
    this->__construct_from(__v);
  }

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(
    __opt_is_constructible_from_opt<_Tp, _Up> _LIBCUDACXX_AND __opt_is_explictly_constructible<_Tp, const _Up&>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit optional(const optional<_Up>& __v)
  {
    this->__construct_from(__v);
  }

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(
    __opt_is_constructible_from_opt<_Tp, _Up> _LIBCUDACXX_AND __opt_is_implictly_constructible<_Tp, _Up>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional(optional<_Up>&& __v)
  {
    this->__construct_from(_CUDA_VSTD::move(__v));
  }

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(
    __opt_is_constructible_from_opt<_Tp, _Up> _LIBCUDACXX_AND __opt_is_explictly_constructible<_Tp, _Up>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit optional(optional<_Up>&& __v)
  {
    this->__construct_from(_CUDA_VSTD::move(__v));
  }

private:
  template <class _Fp, class... _Args>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit optional(
    __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
      : __base(
        __optional_construct_from_invoke_tag{}, _CUDA_VSTD::forward<_Fp>(__f), _CUDA_VSTD::forward<_Args>(__args)...)
  {}

public:
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional& operator=(nullopt_t) noexcept
  {
    reset();
    return *this;
  }

  constexpr optional& operator=(const optional&) = default;
  constexpr optional& operator=(optional&&)      = default;

  _LIBCUDACXX_TEMPLATE(class _Up = value_type)
  _LIBCUDACXX_REQUIRES(__opt_is_assignable_from_U<_Tp, _Up> _LIBCUDACXX_AND __opt_is_assignable<_Tp, _Up>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional& operator=(_Up&& __v)
  {
    if (this->has_value())
    {
      this->__get() = _CUDA_VSTD::forward<_Up>(__v);
    }
    else
    {
      this->__construct(_CUDA_VSTD::forward<_Up>(__v));
    }
    return *this;
  }

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(__opt_is_assignable_from_opt<_Tp, _Up> _LIBCUDACXX_AND __opt_is_assignable<_Tp, const _Up&>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional& operator=(const optional<_Up>& __v)
  {
    this->__assign_from(__v);
    return *this;
  }

  _LIBCUDACXX_TEMPLATE(class _Up)
  _LIBCUDACXX_REQUIRES(__opt_is_assignable_from_opt<_Tp, _Up> _LIBCUDACXX_AND __opt_is_assignable<_Tp, _Up>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional& operator=(optional<_Up>&& __v)
  {
    this->__assign_from(_CUDA_VSTD::move(__v));
    return *this;
  }

  template <class... _Args, __enable_if_t<_CCCL_TRAIT(is_constructible, value_type, _Args...), int> = 0>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp& emplace(_Args&&... __args)
  {
    reset();
    this->__construct(_CUDA_VSTD::forward<_Args>(__args)...);
    return this->__get();
  }

  template <class _Up,
            class... _Args,
            __enable_if_t<_CCCL_TRAIT(is_constructible, value_type, initializer_list<_Up>&, _Args...), int> = 0>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args)
  {
    reset();
    this->__construct(__il, _CUDA_VSTD::forward<_Args>(__args)...);
    return this->__get();
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr void swap(optional& __opt) noexcept(
    _CCCL_TRAIT(is_nothrow_move_constructible, value_type) && _CCCL_TRAIT(is_nothrow_swappable, value_type))
  {
    if (this->has_value() == __opt.has_value())
    {
      using _CUDA_VSTD::swap;
      if (this->has_value())
      {
        swap(this->__get(), __opt.__get());
      }
    }
    else
    {
      if (this->has_value())
      {
        __opt.__construct(_CUDA_VSTD::move(this->__get()));
        reset();
      }
      else
      {
        this->__construct(_CUDA_VSTD::move(__opt.__get()));
        __opt.reset();
      }
    }
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr add_pointer_t<value_type const> operator->() const
  {
    _LIBCUDACXX_ASSERT(this->has_value(), "optional operator-> called on a disengaged value");
    return _CUDA_VSTD::addressof(this->__get());
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr add_pointer_t<value_type> operator->()
  {
    _LIBCUDACXX_ASSERT(this->has_value(), "optional operator-> called on a disengaged value");
    return _CUDA_VSTD::addressof(this->__get());
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr const value_type& operator*() const& noexcept
  {
    _LIBCUDACXX_ASSERT(this->has_value(), "optional operator* called on a disengaged value");
    return this->__get();
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr value_type& operator*() & noexcept
  {
    _LIBCUDACXX_ASSERT(this->has_value(), "optional operator* called on a disengaged value");
    return this->__get();
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr value_type&& operator*() && noexcept
  {
    _LIBCUDACXX_ASSERT(this->has_value(), "optional operator* called on a disengaged value");
    return _CUDA_VSTD::move(this->__get());
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr const value_type&& operator*() const&& noexcept
  {
    _LIBCUDACXX_ASSERT(this->has_value(), "optional operator* called on a disengaged value");
    return _CUDA_VSTD::move(this->__get());
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit operator bool() const noexcept
  {
    return has_value();
  }

  using __base::__get;
  using __base::has_value;

  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr value_type const&
  value() const&
  {
    if (!this->has_value())
    {
      __throw_bad_optional_access();
    }
    return this->__get();
  }

  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr value_type& value() &
  {
    if (!this->has_value())
    {
      __throw_bad_optional_access();
    }
    return this->__get();
  }

  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr value_type&& value() &&
  {
    if (!this->has_value())
    {
      __throw_bad_optional_access();
    }
    return _CUDA_VSTD::move(this->__get());
  }

  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr value_type const&&
  value() const&&
  {
    if (!this->has_value())
    {
      __throw_bad_optional_access();
    }
    return _CUDA_VSTD::move(this->__get());
  }

  template <class _Up>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr value_type value_or(_Up&& __v) const&
  {
    static_assert(_CCCL_TRAIT(is_copy_constructible, value_type),
                  "optional<T>::value_or: T must be copy constructible");
    static_assert(_CCCL_TRAIT(is_convertible, _Up, value_type), "optional<T>::value_or: U must be convertible to T");
    return this->has_value() ? this->__get() : static_cast<value_type>(_CUDA_VSTD::forward<_Up>(__v));
  }

  template <class _Up>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr value_type value_or(_Up&& __v) &&
  {
    static_assert(_CCCL_TRAIT(is_move_constructible, value_type),
                  "optional<T>::value_or: T must be move constructible");
    static_assert(_CCCL_TRAIT(is_convertible, _Up, value_type), "optional<T>::value_or: U must be convertible to T");
    return this->has_value() ? _CUDA_VSTD::move(this->__get()) : static_cast<value_type>(_CUDA_VSTD::forward<_Up>(__v));
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto and_then(_Func&& __f) &
  {
    using _Up = invoke_result_t<_Func, value_type&>;
    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                  "Result of f(value()) must be a specialization of std::optional");
    if (this->__engaged_)
    {
      return _CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Func>(__f), this->__get());
    }
    return remove_cvref_t<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  and_then(_Func&& __f) const&
  {
    using _Up = invoke_result_t<_Func, const value_type&>;
    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                  "Result of f(value()) must be a specialization of std::optional");
    if (this->__engaged_)
    {
      return _CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Func>(__f), this->__get());
    }
    return remove_cvref_t<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  and_then(_Func&& __f) &&
  {
    using _Up = invoke_result_t<_Func, value_type&&>;
    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                  "Result of f(std::move(value())) must be a specialization of std::optional");
    if (this->__engaged_)
    {
      return _CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Func>(__f), _CUDA_VSTD::move(this->__get()));
    }
    return remove_cvref_t<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  and_then(_Func&& __f) const&&
  {
    using _Up = invoke_result_t<_Func, const value_type&&>;
    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                  "Result of f(std::move(value())) must be a specialization of std::optional");
    if (this->__engaged_)
    {
      return _CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Func>(__f), _CUDA_VSTD::move(this->__get()));
    }
    return remove_cvref_t<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  transform(_Func&& __f) &
  {
    using _Up = remove_cv_t<invoke_result_t<_Func, value_type&>>;
    static_assert(!_CCCL_TRAIT(is_array, _Up), "Result of f(value()) should not be an Array");
    static_assert(!_CCCL_TRAIT(is_same, _Up, in_place_t), "Result of f(value()) should not be std::in_place_t");
    static_assert(!_CCCL_TRAIT(is_same, _Up, nullopt_t), "Result of f(value()) should not be std::nullopt_t");
    static_assert(_CCCL_TRAIT(is_object, _Up), "Result of f(value()) should be an object type");
    if (this->__engaged_)
    {
      return optional<_Up>(__optional_construct_from_invoke_tag{}, _CUDA_VSTD::forward<_Func>(__f), this->__get());
    }
    return optional<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  transform(_Func&& __f) const&
  {
    using _Up = remove_cv_t<invoke_result_t<_Func, const value_type&>>;
    static_assert(!_CCCL_TRAIT(is_array, _Up), "Result of f(value()) should not be an Array");
    static_assert(!_CCCL_TRAIT(is_same, _Up, in_place_t), "Result of f(value()) should not be std::in_place_t");
    static_assert(!_CCCL_TRAIT(is_same, _Up, nullopt_t), "Result of f(value()) should not be std::nullopt_t");
    static_assert(_CCCL_TRAIT(is_object, _Up), "Result of f(value()) should be an object type");
    if (this->__engaged_)
    {
      return optional<_Up>(__optional_construct_from_invoke_tag{}, _CUDA_VSTD::forward<_Func>(__f), this->__get());
    }
    return optional<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  transform(_Func&& __f) &&
  {
    using _Up = remove_cv_t<invoke_result_t<_Func, value_type&&>>;
    static_assert(!_CCCL_TRAIT(is_array, _Up), "Result of f(std::move(value())) should not be an Array");
    static_assert(!_CCCL_TRAIT(is_same, _Up, in_place_t),
                  "Result of f(std::move(value())) should not be std::in_place_t");
    static_assert(!_CCCL_TRAIT(is_same, _Up, nullopt_t),
                  "Result of f(std::move(value())) should not be std::nullopt_t");
    static_assert(_CCCL_TRAIT(is_object, _Up), "Result of f(std::move(value())) should be an object type");
    if (this->__engaged_)
    {
      return optional<_Up>(
        __optional_construct_from_invoke_tag{}, _CUDA_VSTD::forward<_Func>(__f), _CUDA_VSTD::move(this->__get()));
    }
    return optional<_Up>();
  }

  template <class _Func>
  _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_AVAILABILITY_THROW_BAD_OPTIONAL_ACCESS constexpr auto
  transform(_Func&& __f) const&&
  {
    using _Up = remove_cvref_t<invoke_result_t<_Func, const value_type&&>>;
    static_assert(!_CCCL_TRAIT(is_array, _Up), "Result of f(std::move(value())) should not be an Array");
    static_assert(!_CCCL_TRAIT(is_same, _Up, in_place_t),
                  "Result of f(std::move(value())) should not be std::in_place_t");
    static_assert(!_CCCL_TRAIT(is_same, _Up, nullopt_t),
                  "Result of f(std::move(value())) should not be std::nullopt_t");
    static_assert(_CCCL_TRAIT(is_object, _Up), "Result of f(std::move(value())) should be an object type");
    if (this->__engaged_)
    {
      return optional<_Up>(
        __optional_construct_from_invoke_tag{}, _CUDA_VSTD::forward<_Func>(__f), _CUDA_VSTD::move(this->__get()));
    }
    return optional<_Up>();
  }

  _LIBCUDACXX_TEMPLATE(class _Func, class _Tp2 = _Tp)
  _LIBCUDACXX_REQUIRES(invocable<_Func> _LIBCUDACXX_AND _CCCL_TRAIT(is_copy_constructible, _Tp2))
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional or_else(_Func&& __f) const&
  {
    static_assert(_CCCL_TRAIT(is_same, remove_cvref_t<invoke_result_t<_Func>>, optional),
                  "Result of f() should be the same type as this optional");
    if (this->__engaged_)
    {
      return *this;
    }
    return _CUDA_VSTD::forward<_Func>(__f)();
  }

  _LIBCUDACXX_TEMPLATE(class _Func, class _Tp2 = _Tp)
  _LIBCUDACXX_REQUIRES(invocable<_Func> _LIBCUDACXX_AND _CCCL_TRAIT(is_move_constructible, _Tp2))
  _LIBCUDACXX_INLINE_VISIBILITY constexpr optional or_else(_Func&& __f) &&
  {
    static_assert(_CCCL_TRAIT(is_same, remove_cvref_t<invoke_result_t<_Func>>, optional),
                  "Result of f() should be the same type as this optional");
    if (this->__engaged_)
    {
      return _CUDA_VSTD::move(*this);
    }
    return _CUDA_VSTD::forward<_Func>(__f)();
  }

  using __base::reset;
};

#  if _CCCL_STD_VER > 2014 && !defined(_LIBCUDACXX_HAS_NO_DEDUCTION_GUIDES)
template <class _Tp>
_CCCL_HOST_DEVICE optional(_Tp) -> optional<_Tp>;
#  endif

// Comparisons between optionals
template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() == declval<const _Up&>()), bool),
  bool>
operator==(const optional<_Tp>& __x, const optional<_Up>& __y)
{
  if (static_cast<bool>(__x) != static_cast<bool>(__y))
  {
    return false;
  }
  if (!static_cast<bool>(__x))
  {
    return true;
  }
  return *__x == *__y;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() != declval<const _Up&>()), bool),
  bool>
operator!=(const optional<_Tp>& __x, const optional<_Up>& __y)
{
  if (static_cast<bool>(__x) != static_cast<bool>(__y))
  {
    return true;
  }
  if (!static_cast<bool>(__x))
  {
    return false;
  }
  return *__x != *__y;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() < declval<const _Up&>()), bool),
  bool>
operator<(const optional<_Tp>& __x, const optional<_Up>& __y)
{
  if (!static_cast<bool>(__y))
  {
    return false;
  }
  if (!static_cast<bool>(__x))
  {
    return true;
  }
  return *__x < *__y;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() > declval<const _Up&>()), bool),
  bool>
operator>(const optional<_Tp>& __x, const optional<_Up>& __y)
{
  if (!static_cast<bool>(__x))
  {
    return false;
  }
  if (!static_cast<bool>(__y))
  {
    return true;
  }
  return *__x > *__y;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() <= declval<const _Up&>()), bool),
  bool>
operator<=(const optional<_Tp>& __x, const optional<_Up>& __y)
{
  if (!static_cast<bool>(__x))
  {
    return true;
  }
  if (!static_cast<bool>(__y))
  {
    return false;
  }
  return *__x <= *__y;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() >= declval<const _Up&>()), bool),
  bool>
operator>=(const optional<_Tp>& __x, const optional<_Up>& __y)
{
  if (!static_cast<bool>(__y))
  {
    return true;
  }
  if (!static_cast<bool>(__x))
  {
    return false;
  }
  return *__x >= *__y;
}

// Comparisons with nullopt
template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept
{
  return !static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept
{
  return !static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept
{
  return static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept
{
  return static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept
{
  return false;
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept
{
  return static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept
{
  return !static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept
{
  return true;
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept
{
  return static_cast<bool>(__x);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept
{
  return false;
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept
{
  return true;
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept
{
  return !static_cast<bool>(__x);
}

// Comparisons with T
template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() == declval<const _Up&>()), bool),
  bool>
operator==(const optional<_Tp>& __x, const _Up& __v)
{
  return static_cast<bool>(__x) ? *__x == __v : false;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() == declval<const _Up&>()), bool),
  bool>
operator==(const _Tp& __v, const optional<_Up>& __x)
{
  return static_cast<bool>(__x) ? __v == *__x : false;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() != declval<const _Up&>()), bool),
  bool>
operator!=(const optional<_Tp>& __x, const _Up& __v)
{
  return static_cast<bool>(__x) ? *__x != __v : true;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() != declval<const _Up&>()), bool),
  bool>
operator!=(const _Tp& __v, const optional<_Up>& __x)
{
  return static_cast<bool>(__x) ? __v != *__x : true;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() < declval<const _Up&>()), bool),
  bool>
operator<(const optional<_Tp>& __x, const _Up& __v)
{
  return static_cast<bool>(__x) ? *__x < __v : true;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() < declval<const _Up&>()), bool),
  bool>
operator<(const _Tp& __v, const optional<_Up>& __x)
{
  return static_cast<bool>(__x) ? __v < *__x : false;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() <= declval<const _Up&>()), bool),
  bool>
operator<=(const optional<_Tp>& __x, const _Up& __v)
{
  return static_cast<bool>(__x) ? *__x <= __v : true;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() <= declval<const _Up&>()), bool),
  bool>
operator<=(const _Tp& __v, const optional<_Up>& __x)
{
  return static_cast<bool>(__x) ? __v <= *__x : false;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() > declval<const _Up&>()), bool),
  bool>
operator>(const optional<_Tp>& __x, const _Up& __v)
{
  return static_cast<bool>(__x) ? *__x > __v : false;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() > declval<const _Up&>()), bool),
  bool>
operator>(const _Tp& __v, const optional<_Up>& __x)
{
  return static_cast<bool>(__x) ? __v > *__x : true;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() >= declval<const _Up&>()), bool),
  bool>
operator>=(const optional<_Tp>& __x, const _Up& __v)
{
  return static_cast<bool>(__x) ? *__x >= __v : false;
}

template <class _Tp, class _Up>
_LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_convertible, decltype(declval<const _Tp&>() >= declval<const _Up&>()), bool),
  bool>
operator>=(const _Tp& __v, const optional<_Up>& __x)
{
  return static_cast<bool>(__x) ? __v >= *__x : true;
}

template <class _Tp>
inline _LIBCUDACXX_INLINE_VISIBILITY constexpr __enable_if_t<
  _CCCL_TRAIT(is_move_constructible, _Tp) && _CCCL_TRAIT(is_swappable, _Tp),
  void>
swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y)))
{
  __x.swap(__y);
}

template <class _Tp>
_LIBCUDACXX_INLINE_VISIBILITY constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v)
{
  return optional<decay_t<_Tp>>(_CUDA_VSTD::forward<_Tp>(__v));
}

template <class _Tp, class... _Args>
_LIBCUDACXX_INLINE_VISIBILITY constexpr optional<_Tp> make_optional(_Args&&... __args)
{
  return optional<_Tp>(in_place, _CUDA_VSTD::forward<_Args>(__args)...);
}

template <class _Tp, class _Up, class... _Args>
_LIBCUDACXX_INLINE_VISIBILITY constexpr optional<_Tp> make_optional(initializer_list<_Up> __il, _Args&&... __args)
{
  return optional<_Tp>(in_place, __il, _CUDA_VSTD::forward<_Args>(__args)...);
}

#  ifndef __cuda_std__
template <class _Tp>
struct _LIBCUDACXX_TEMPLATE_VIS hash<__enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>>>
{
#    if _CCCL_STD_VER <= 2017 || defined(_LIBCUDACXX_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
  _LIBCUDACXX_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
  _LIBCUDACXX_DEPRECATED_IN_CXX17 typedef size_t result_type;
#    endif

  _LIBCUDACXX_INLINE_VISIBILITY size_t operator()(const optional<_Tp>& __opt) const
  {
    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
  }
};
#  endif // __cuda_std__

_LIBCUDACXX_END_NAMESPACE_STD

#endif // _CCCL_STD_VER > 2011

_CCCL_POP_MACROS

#endif // _LIBCUDACXX_OPTIONAL
