// -*- C++ -*-
//===-------------------------- concepts ----------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_CONCEPTS
#define _LIBCPP_CONCEPTS

/*
    concepts synopsis
namespace std {
  // [concepts.lang], language-related concepts
  // [concept.same], concept same_as
  template<class T, class U>
    concept same_as = see below;

  // [concept.derived], concept derived_from
  template<class Derived, class Base>
    concept derived_from = see below;

  // [concept.convertible], concept convertible_to
  template<class From, class To>
    concept convertible_to = see below;

  // [concept.commonref], concept common_reference_with
  template<class T, class U>
    concept common_reference_with = see below;

  // [concept.common], concept common_with
  template<class T, class U>
    concept common_with = see below;

  // [concepts.arithmetic], arithmetic concepts
  template<class T>
    concept integral = see below;
  template<class T>
    concept signed_integral = see below;
  template<class T>
    concept unsigned_integral = see below;
  template<class T>
    concept floating_point = see below;

  // [concept.assignable], concept assignable_from
  template<class LHS, class RHS>
    concept assignable_from = see below;

  // [concept.swappable], concept swappable
  namespace ranges {
    inline namespace unspecified {
      inline constexpr unspecified swap = unspecified;
    }
  }
  template<class T>
    concept swappable = see below;
  template<class T, class U>
    concept swappable_with = see below;

  // [concept.destructible], concept destructible
  template<class T>
    concept destructible = see below;

  // [concept.constructible], concept constructible_from
  template<class T, class... Args>
    concept constructible_from = see below;

  // [concept.defaultconstructible], concept default_constructible
  template<class T>
    concept default_constructible = see below;

  // [concept.moveconstructible], concept move_constructible
  template<class T>
    concept move_constructible = see below;

  // [concept.copyconstructible], concept copy_constructible
  template<class T>
    concept copy_constructible = see below;

  // [concepts.compare], comparison concepts
  // [concept.boolean], concept boolean
  template<class B>
    concept boolean = see below;

  // [concept.equalitycomparable], concept equality_comparable
  template<class T>
    concept equality_comparable = see below;
  template<class T, class U>
    concept equality_comparable_with = see below;

  // [concept.totallyordered], concept totally_ordered
  template<class T>
    concept totally_ordered = see below;
  template<class T, class U>
    concept totally_ordered_with = see below;

  // [concepts.object], object concepts
  template<class T>
    concept movable = see below;
  template<class T>
    concept copyable = see below;
  template<class T>
    concept semiregular = see below;
  template<class T>
    concept regular = see below;

  // [concepts.callable], callable concepts
  // [concept.invocable], concept invocable
  template<class F, class... Args>
    concept invocable = see below;

  // [concept.regularinvocable], concept regular_invocable
  template<class F, class... Args>
    concept regular_invocable = see below;

  // [concept.predicate], concept predicate
  template<class F, class... Args>
    concept predicate = see below;

  // [concept.relation], concept relation
  template<class R, class T, class U>
    concept relation = see below;

  // [concept.equiv], concept equivalence_relation
  template<class R, class T, class U>
    concept equivalence_relation = see below;

  // [concept.strictweakorder], concept strict_weak_order
  template<class R, class T, class U>
    concept strict_weak_order = see below;
}

*/

#include <__config>
#include <functional>
#include <type_traits>
#include <utility>
#include <version>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)

// [concept.same]

template<class _Tp, class _Up>
concept __same_as_impl = _VSTD::_IsSame<_Tp, _Up>::value;

template<class _Tp, class _Up>
concept same_as = __same_as_impl<_Tp, _Up> && __same_as_impl<_Up, _Tp>;

// [concept.derived]
template<class _Dp, class _Bp>
concept derived_from =
  is_base_of_v<_Bp, _Dp> &&
  is_convertible_v<const volatile _Dp*, const volatile _Bp*>;

// [concept.convertible]
template<class _From, class _To>
concept convertible_to =
  is_convertible_v<_From, _To> &&
  requires(add_rvalue_reference_t<_From> (&__f)()) {
    static_cast<_To>(__f());
  };

// [concept.commonref]
template<class _Tp, class _Up>
concept common_reference_with =
  same_as<common_reference_t<_Tp, _Up>, common_reference_t<_Up, _Tp>> &&
  convertible_to<_Tp, common_reference_t<_Tp, _Up>> &&
  convertible_to<_Up, common_reference_t<_Tp, _Up>>;

// [concept.common]
template<class _Tp, class _Up>
concept common_with =
  same_as<common_type_t<_Tp, _Up>, common_type_t<_Up, _Tp>> &&
  requires {
    static_cast<common_type_t<_Tp, _Up>>(_VSTD::declval<_Tp>());
    static_cast<common_type_t<_Tp, _Up>>(_VSTD::declval<_Up>());
  } &&
  common_reference_with<
    add_lvalue_reference_t<const _Tp>,
    add_lvalue_reference_t<const _Up>> &&
  common_reference_with<
    add_lvalue_reference_t<common_type_t<_Tp, _Up>>,
    common_reference_t<
      add_lvalue_reference_t<const _Tp>,
      add_lvalue_reference_t<const _Up>>>;

// [concepts.arithmetic], arithmetic concepts
template<class _Tp>
concept integral = is_integral_v<_Tp>;

template<class _Tp>
concept signed_integral = integral<_Tp> && is_signed_v<_Tp>;

template<class _Tp>
concept unsigned_integral = integral<_Tp> && !signed_integral<_Tp>;

template<class _Tp>
concept floating_point = is_floating_point_v<_Tp>;

// [concept.assignable]
template<class _Lhs, class _Rhs>
concept assignable_from =
  is_lvalue_reference_v<_Lhs> &&
  common_reference_with<const remove_reference_t<_Lhs>&, const remove_reference_t<_Rhs>&> &&
  requires (_Lhs __lhs, _Rhs&& __rhs) {
    { __lhs = _VSTD::forward<_Rhs>(__rhs) } -> same_as<_Lhs>;
  };

// [concept.destructible]

template<class _Tp>
concept destructible = _VSTD::is_nothrow_destructible_v<_Tp>;

// [concept.constructible]
template<class _Tp, class... _Args>
concept constructible_from =
    destructible<_Tp> && _VSTD::is_constructible_v<_Tp, _Args...>;

// [concept.default.init]

template<class _Tp>
concept __default_initializable = requires { ::new _Tp; };

template<class _Tp>
concept default_initializable = constructible_from<_Tp> &&
    requires { _Tp{}; } && __default_initializable<_Tp>;

// [concept.moveconstructible]
template<class _Tp>
concept move_constructible =
  constructible_from<_Tp, _Tp> && convertible_to<_Tp, _Tp>;

// [concept.copyconstructible]
template<class _Tp>
concept copy_constructible =
  move_constructible<_Tp> &&
  constructible_from<_Tp, _Tp&> && convertible_to<_Tp&, _Tp> &&
  constructible_from<_Tp, const _Tp&> && convertible_to<const _Tp&, _Tp> &&
  constructible_from<_Tp, const _Tp> && convertible_to<const _Tp, _Tp>;

// [concept.booleantestable]
template<class _Tp>
concept __boolean_testable_impl = convertible_to<_Tp, bool>;

template<class _Tp>
concept __boolean_testable = __boolean_testable_impl<_Tp> && requires(_Tp&& __t) {
  { !std::forward<_Tp>(__t) } -> __boolean_testable_impl;
};

// [concept.equalitycomparable]
template<class _Tp, class _Up>
concept __weakly_equality_comparable_with =
  requires(const remove_reference_t<_Tp>& __t, const remove_reference_t<_Up>& __u) {
    { __t == __u } -> __boolean_testable;
    { __t != __u } -> __boolean_testable;
    { __u == __t } -> __boolean_testable;
    { __u != __t } -> __boolean_testable;
  };

template<class _Tp>
concept equality_comparable = __weakly_equality_comparable_with<_Tp, _Tp>;

template<class _Tp, class _Up>
concept equality_comparable_with =
  equality_comparable<_Tp> && equality_comparable<_Up> &&
  common_reference_with<const remove_reference_t<_Tp>&, const remove_reference_t<_Up>&> &&
  equality_comparable<
    common_reference_t<
      const remove_reference_t<_Tp>&,
      const remove_reference_t<_Up>&>> &&
  __weakly_equality_comparable_with<_Tp, _Up>;

// [concept.invocable]
template<class _Fn, class... _Args>
concept invocable = requires(_Fn&& __fn, _Args&&... __args) {
  _VSTD::invoke(_VSTD::forward<_Fn>(__fn), _VSTD::forward<_Args>(__args)...); // not required to be equality preserving
};

// [concept.regular.invocable]
template<class _Fn, class... _Args>
concept regular_invocable = invocable<_Fn, _Args...>;

#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP_CONCEPTS
