//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// template<class T, class U>
// concept swappable_with = // see below

#include <concepts>

#include <array>
#include <cassert>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "type_classification/moveconstructible.h"
#include "type_classification/swappable.h"

template <class T, class U>
constexpr bool check_swappable_with_impl() {
  static_assert(std::swappable_with<T, U> == std::swappable_with<U, T>);
  return std::swappable_with<T, U>;
}

template <class T, class U>
constexpr bool check_swappable_with() {
  static_assert(!check_swappable_with_impl<T, U>());
  static_assert(!check_swappable_with_impl<T, U const>());
  static_assert(!check_swappable_with_impl<T const, U>());
  static_assert(!check_swappable_with_impl<T const, U const>());

  static_assert(!check_swappable_with_impl<T, U&>());
  static_assert(!check_swappable_with_impl<T, U const&>());
  static_assert(!check_swappable_with_impl<T, U volatile&>());
  static_assert(!check_swappable_with_impl<T, U const volatile&>());
  static_assert(!check_swappable_with_impl<T const, U&>());
  static_assert(!check_swappable_with_impl<T const, U const&>());
  static_assert(!check_swappable_with_impl<T const, U volatile&>());
  static_assert(!check_swappable_with_impl<T const, U const volatile&>());

  static_assert(!check_swappable_with_impl<T&, U>());
  static_assert(!check_swappable_with_impl<T&, U const>());
  static_assert(!check_swappable_with_impl<T const&, U>());
  static_assert(!check_swappable_with_impl<T const&, U const>());
  static_assert(!check_swappable_with_impl<T volatile&, U>());
  static_assert(!check_swappable_with_impl<T volatile&, U const>());
  static_assert(!check_swappable_with_impl<T const volatile&, U>());
  static_assert(!check_swappable_with_impl<T const volatile&, U const>());

  static_assert(!check_swappable_with_impl<T&, U const&>());
  static_assert(!check_swappable_with_impl<T&, U volatile&>());
  static_assert(!check_swappable_with_impl<T&, U const volatile&>());
  static_assert(!check_swappable_with_impl<T const&, U&>());
  static_assert(!check_swappable_with_impl<T const&, U const&>());
  static_assert(!check_swappable_with_impl<T const&, U volatile&>());
  static_assert(!check_swappable_with_impl<T const&, U const volatile&>());
  static_assert(!check_swappable_with_impl<T volatile&, U&>());
  static_assert(!check_swappable_with_impl<T volatile&, U const&>());
  static_assert(!check_swappable_with_impl<T volatile&, U const volatile&>());
  static_assert(!check_swappable_with_impl<T const volatile&, U&>());
  static_assert(!check_swappable_with_impl<T const volatile&, U const&>());
  static_assert(!check_swappable_with_impl<T const volatile&, U volatile&>());
  static_assert(
      !check_swappable_with_impl<T const volatile&, U const volatile&>());

  static_assert(!check_swappable_with_impl<T, U&&>());
  static_assert(!check_swappable_with_impl<T, U const&&>());
  static_assert(!check_swappable_with_impl<T, U volatile&&>());
  static_assert(!check_swappable_with_impl<T, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T const, U&&>());
  static_assert(!check_swappable_with_impl<T const, U const&&>());
  static_assert(!check_swappable_with_impl<T const, U volatile&&>());
  static_assert(!check_swappable_with_impl<T const, U const volatile&&>());

  static_assert(!check_swappable_with_impl<T&&, U>());
  static_assert(!check_swappable_with_impl<T&&, U const>());
  static_assert(!check_swappable_with_impl<T const&&, U>());
  static_assert(!check_swappable_with_impl<T const&&, U const>());
  static_assert(!check_swappable_with_impl<T volatile&&, U>());
  static_assert(!check_swappable_with_impl<T volatile&&, U const>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U const>());

  static_assert(!check_swappable_with_impl<T&, U&&>());
  static_assert(!check_swappable_with_impl<T&, U const&&>());
  static_assert(!check_swappable_with_impl<T&, U volatile&&>());
  static_assert(!check_swappable_with_impl<T&, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T const&, U&&>());
  static_assert(!check_swappable_with_impl<T const&, U const&&>());
  static_assert(!check_swappable_with_impl<T const&, U volatile&&>());
  static_assert(!check_swappable_with_impl<T const&, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T volatile&, U&&>());
  static_assert(!check_swappable_with_impl<T volatile&, U const&&>());
  static_assert(!check_swappable_with_impl<T volatile&, U volatile&&>());
  static_assert(!check_swappable_with_impl<T volatile&, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T const volatile&, U&&>());
  static_assert(!check_swappable_with_impl<T const volatile&, U const&&>());
  static_assert(!check_swappable_with_impl<T const volatile&, U volatile&&>());
  static_assert(
      !check_swappable_with_impl<T const volatile&, U const volatile&&>());

  static_assert(!check_swappable_with_impl<T&&, U&>());
  static_assert(!check_swappable_with_impl<T&&, U const&>());
  static_assert(!check_swappable_with_impl<T&&, U volatile&>());
  static_assert(!check_swappable_with_impl<T&&, U const volatile&>());
  static_assert(!check_swappable_with_impl<T const&&, U&>());
  static_assert(!check_swappable_with_impl<T const&&, U const&>());
  static_assert(!check_swappable_with_impl<T const&&, U volatile&>());
  static_assert(!check_swappable_with_impl<T const&&, U const volatile&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U const&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U volatile&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U const volatile&>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U&>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U const&>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U volatile&>());
  static_assert(
      !check_swappable_with_impl<T const volatile&&, U const volatile&>());

  static_assert(!check_swappable_with_impl<T&&, U&&>());
  static_assert(!check_swappable_with_impl<T&&, U const&&>());
  static_assert(!check_swappable_with_impl<T&&, U volatile&&>());
  static_assert(!check_swappable_with_impl<T&&, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T const&&, U&&>());
  static_assert(!check_swappable_with_impl<T const&&, U const&&>());
  static_assert(!check_swappable_with_impl<T const&&, U volatile&&>());
  static_assert(!check_swappable_with_impl<T const&&, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U&&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U const&&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U volatile&&>());
  static_assert(!check_swappable_with_impl<T volatile&&, U const volatile&&>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U&&>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U const&&>());
  static_assert(!check_swappable_with_impl<T const volatile&&, U volatile&&>());
  static_assert(
      !check_swappable_with_impl<T const volatile&&, U const volatile&&>());
  return check_swappable_with_impl<T&, U&>();
}

template <class T, class U>
constexpr bool check_swappable_with_including_lvalue_ref_to_volatile() {
  constexpr auto result = check_swappable_with<T, U>();
  static_assert(check_swappable_with_impl<T volatile&, U volatile&>() ==
                result);
  return result;
}

namespace fundamental {
static_assert(
    check_swappable_with_including_lvalue_ref_to_volatile<int, int>());
static_assert(
    check_swappable_with_including_lvalue_ref_to_volatile<double, double>());
static_assert(
    !check_swappable_with_including_lvalue_ref_to_volatile<int, double>());

static_assert(
    check_swappable_with_including_lvalue_ref_to_volatile<int*, int*>());
static_assert(
    !check_swappable_with_including_lvalue_ref_to_volatile<int, int*>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (*)(), int (*)()>());
static_assert(
    !check_swappable_with_including_lvalue_ref_to_volatile<int, int (*)()>());

struct S {};
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<int, S>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int S::*, int S::*>());
static_assert(
    !check_swappable_with_including_lvalue_ref_to_volatile<int, int S::*>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)(), int (S::*)()>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int, int (S::*)()>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() noexcept, int (S::*)() noexcept>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() noexcept, int (S::*)()>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() const, int (S::*)() const>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() const, int (S::*)()>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() const noexcept, int (S::*)() const noexcept>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() const, int (S::*)() const noexcept>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() volatile, int (S::*)() volatile>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() volatile, int (S::*)()>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() const volatile, int (S::*)() const volatile>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int (S::*)() const volatile, int (S::*)()>());

static_assert(
    check_swappable_with_including_lvalue_ref_to_volatile<int[5], int[5]>());
static_assert(
    !check_swappable_with_including_lvalue_ref_to_volatile<int[5], int[6]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5], double[5]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5], double[6]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<int[5][6],
                                                                     int[5]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<int[5][6],
                                                                     int[6]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], double[5]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], double[6]>());
static_assert(check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], int[5][6]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], int[5][4]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], int[6][5]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], double[5][6]>());
static_assert(!check_swappable_with_including_lvalue_ref_to_volatile<
              int[5][6], double[6][5]>());

// always false
static_assert(!check_swappable_with_impl<void, void>());
static_assert(!check_swappable_with_impl<int, void>());
static_assert(!check_swappable_with_impl<int&, void>());
static_assert(!check_swappable_with_impl<void, int>());
static_assert(!check_swappable_with_impl<void, int&>());
static_assert(!check_swappable_with_impl<int, int()>());
static_assert(!check_swappable_with_impl<int, int (&)()>());
} // namespace fundamental

namespace adl {
static_assert(
    check_swappable_with<lvalue_adl_swappable, lvalue_adl_swappable>());
static_assert(check_swappable_with<lvalue_rvalue_adl_swappable,
                                   lvalue_rvalue_adl_swappable>());
static_assert(check_swappable_with<rvalue_lvalue_adl_swappable,
                                   rvalue_lvalue_adl_swappable>());
static_assert(
    check_swappable_with_impl<rvalue_adl_swappable, rvalue_adl_swappable>());
static_assert(!check_swappable_with_impl<lvalue_rvalue_adl_swappable&,
                                         lvalue_rvalue_adl_swappable&&>());

struct s1 {};
struct no_common_reference_with_s1 {
  friend void swap(s1&, no_common_reference_with_s1&);
  friend void swap(no_common_reference_with_s1&, s1&);
};
static_assert(!check_swappable_with<s1, no_common_reference_with_s1>());

struct one_way_swappable_with_s1 {
  friend void swap(s1&, one_way_swappable_with_s1&);
  operator s1();
};
static_assert(std::common_reference_with<one_way_swappable_with_s1, s1>);
static_assert(!check_swappable_with<one_way_swappable_with_s1, s1>());

struct one_way_swappable_with_s1_other_way {
  friend void swap(one_way_swappable_with_s1_other_way&, s1&);
  operator s1();
};
static_assert(
    std::common_reference_with<one_way_swappable_with_s1_other_way, s1>);
static_assert(!check_swappable_with<one_way_swappable_with_s1_other_way, s1>());

struct can_swap_with_s1_but_not_swappable {
  can_swap_with_s1_but_not_swappable(can_swap_with_s1_but_not_swappable&&) =
      delete;
  friend void swap(s1&, can_swap_with_s1_but_not_swappable&);
  friend void swap(can_swap_with_s1_but_not_swappable&, s1&);

  operator s1() const;
};
static_assert(
    std::common_reference_with<can_swap_with_s1_but_not_swappable, s1>);
static_assert(!std::swappable<can_swap_with_s1_but_not_swappable>);
static_assert(
    !check_swappable_with<can_swap_with_s1_but_not_swappable&, s1&>());

struct swappable_with_s1 {
  friend void swap(s1&, swappable_with_s1&);
  friend void swap(swappable_with_s1&, s1&);
  operator s1() const;
};
static_assert(check_swappable_with<swappable_with_s1, s1>());

struct swappable_with_const_s1_but_not_swappable {
  swappable_with_const_s1_but_not_swappable(
      swappable_with_const_s1_but_not_swappable const&);
  swappable_with_const_s1_but_not_swappable(
      swappable_with_const_s1_but_not_swappable const&&);
  swappable_with_const_s1_but_not_swappable&
  operator=(swappable_with_const_s1_but_not_swappable const&);
  swappable_with_const_s1_but_not_swappable&
  operator=(swappable_with_const_s1_but_not_swappable const&&);

  friend void swap(s1 const&, swappable_with_const_s1_but_not_swappable const&);
  friend void swap(swappable_with_const_s1_but_not_swappable const&, s1 const&);

  operator s1 const &() const;
};
static_assert(
    !std::swappable<swappable_with_const_s1_but_not_swappable const&>);
static_assert(!std::swappable_with<
              swappable_with_const_s1_but_not_swappable const&, s1 const&>);

struct swappable_with_volatile_s1_but_not_swappable {
  swappable_with_volatile_s1_but_not_swappable(
      swappable_with_volatile_s1_but_not_swappable volatile&);
  swappable_with_volatile_s1_but_not_swappable(
      swappable_with_volatile_s1_but_not_swappable volatile&&);
  swappable_with_volatile_s1_but_not_swappable&
  operator=(swappable_with_volatile_s1_but_not_swappable volatile&);
  swappable_with_volatile_s1_but_not_swappable&
  operator=(swappable_with_volatile_s1_but_not_swappable volatile&&);

  friend void swap(s1 volatile&,
                   swappable_with_volatile_s1_but_not_swappable volatile&);
  friend void swap(swappable_with_volatile_s1_but_not_swappable volatile&,
                   s1 volatile&);

  operator s1 volatile &() volatile;
};
static_assert(
    !std::swappable<swappable_with_volatile_s1_but_not_swappable volatile&>);
static_assert(
    !std::swappable_with<swappable_with_volatile_s1_but_not_swappable volatile&,
                         s1 volatile&>);

struct swappable_with_cv_s1_but_not_swappable {
  swappable_with_cv_s1_but_not_swappable(
      swappable_with_cv_s1_but_not_swappable const volatile&);
  swappable_with_cv_s1_but_not_swappable(
      swappable_with_cv_s1_but_not_swappable const volatile&&);
  swappable_with_cv_s1_but_not_swappable&
  operator=(swappable_with_cv_s1_but_not_swappable const volatile&);
  swappable_with_cv_s1_but_not_swappable&
  operator=(swappable_with_cv_s1_but_not_swappable const volatile&&);

  friend void swap(s1 const volatile&,
                   swappable_with_cv_s1_but_not_swappable const volatile&);
  friend void swap(swappable_with_cv_s1_but_not_swappable const volatile&,
                   s1 const volatile&);

  operator s1 const volatile &() const volatile;
};
static_assert(
    !std::swappable<swappable_with_cv_s1_but_not_swappable const volatile&>);
static_assert(
    !std::swappable_with<swappable_with_cv_s1_but_not_swappable const volatile&,
                         s1 const volatile&>);

struct s2 {
  friend void swap(s2 const&, s2 const&);
  friend void swap(s2 volatile&, s2 volatile&);
  friend void swap(s2 const volatile&, s2 const volatile&);
};

struct swappable_with_const_s2 {
  swappable_with_const_s2(swappable_with_const_s2 const&);
  swappable_with_const_s2(swappable_with_const_s2 const&&);
  swappable_with_const_s2& operator=(swappable_with_const_s2 const&);
  swappable_with_const_s2& operator=(swappable_with_const_s2 const&&);

  friend void swap(swappable_with_const_s2 const&,
                   swappable_with_const_s2 const&);
  friend void swap(s2 const&, swappable_with_const_s2 const&);
  friend void swap(swappable_with_const_s2 const&, s2 const&);

  operator s2 const &() const;
};
static_assert(std::swappable_with<swappable_with_const_s2 const&, s2 const&>);

struct swappable_with_volatile_s2 {
  swappable_with_volatile_s2(swappable_with_volatile_s2 volatile&);
  swappable_with_volatile_s2(swappable_with_volatile_s2 volatile&&);
  swappable_with_volatile_s2& operator=(swappable_with_volatile_s2 volatile&);
  swappable_with_volatile_s2& operator=(swappable_with_volatile_s2 volatile&&);

  friend void swap(swappable_with_volatile_s2 volatile&,
                   swappable_with_volatile_s2 volatile&);
  friend void swap(s2 volatile&, swappable_with_volatile_s2 volatile&);
  friend void swap(swappable_with_volatile_s2 volatile&, s2 volatile&);

  operator s2 volatile &() volatile;
};
static_assert(
    std::swappable_with<swappable_with_volatile_s2 volatile&, s2 volatile&>);

struct swappable_with_cv_s2 {
  swappable_with_cv_s2(swappable_with_cv_s2 const volatile&);
  swappable_with_cv_s2(swappable_with_cv_s2 const volatile&&);
  swappable_with_cv_s2& operator=(swappable_with_cv_s2 const volatile&);
  swappable_with_cv_s2& operator=(swappable_with_cv_s2 const volatile&&);

  friend void swap(swappable_with_cv_s2 const volatile&,
                   swappable_with_cv_s2 const volatile&);
  friend void swap(s2 const volatile&, swappable_with_cv_s2 const volatile&);
  friend void swap(swappable_with_cv_s2 const volatile&, s2 const volatile&);

  operator s2 const volatile &() const volatile;
};
static_assert(std::swappable_with<swappable_with_cv_s2 const volatile&,
                                  s2 const volatile&>);

struct swappable_with_rvalue_ref_to_s1_but_not_swappable {
  friend void swap(swappable_with_rvalue_ref_to_s1_but_not_swappable&&,
                   swappable_with_rvalue_ref_to_s1_but_not_swappable&&);
  friend void swap(s1&&, swappable_with_rvalue_ref_to_s1_but_not_swappable&&);
  friend void swap(swappable_with_rvalue_ref_to_s1_but_not_swappable&&, s1&&);

  operator s1() const;
};
static_assert(
    !std::swappable<swappable_with_rvalue_ref_to_s1_but_not_swappable const&&>);
static_assert(
    !std::swappable_with<
        swappable_with_rvalue_ref_to_s1_but_not_swappable const&&, s1 const&&>);

struct swappable_with_rvalue_ref_to_const_s1_but_not_swappable {
  friend void
  swap(s1 const&&,
       swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&);
  friend void
  swap(swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&,
       s1 const&&);

  operator s1 const() const;
};
static_assert(!std::swappable<
              swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&>);
static_assert(!std::swappable_with<
              swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&,
              s1 const&&>);

struct swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable {
  swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable(
      swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&);
  swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable(
      swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&);
  swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable& operator=(
      swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&);
  swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable& operator=(
      swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&);

  friend void
  swap(s1 volatile&&,
       swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&);
  friend void
  swap(swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&,
       s1 volatile&&);

  operator s1 volatile &&() volatile&&;
};
static_assert(
    !std::swappable<
        swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&>);
static_assert(
    !std::swappable_with<
        swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&,
        s1 volatile&&>);

struct swappable_with_rvalue_ref_to_cv_s1_but_not_swappable {
  swappable_with_rvalue_ref_to_cv_s1_but_not_swappable(
      swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&);
  swappable_with_rvalue_ref_to_cv_s1_but_not_swappable(
      swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&);
  swappable_with_rvalue_ref_to_cv_s1_but_not_swappable& operator=(
      swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&);
  swappable_with_rvalue_ref_to_cv_s1_but_not_swappable& operator=(
      swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&);

  friend void
  swap(s1 const volatile&&,
       swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&);
  friend void
  swap(swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&,
       s1 const volatile&&);

  operator s1 const volatile &&() const volatile&&;
};
static_assert(
    !std::swappable<
        swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&>);
static_assert(
    !std::swappable_with<
        swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&,
        s1 const volatile&&>);

struct s3 {
  friend void swap(s3&&, s3&&);
  friend void swap(s3 const&&, s3 const&&);
  friend void swap(s3 volatile&&, s3 volatile&&);
  friend void swap(s3 const volatile&&, s3 const volatile&&);
};

struct swappable_with_rvalue_ref_to_s3 {
  friend void swap(swappable_with_rvalue_ref_to_s3&&,
                   swappable_with_rvalue_ref_to_s3&&);
  friend void swap(s3&&, swappable_with_rvalue_ref_to_s3&&);
  friend void swap(swappable_with_rvalue_ref_to_s3&&, s3&&);

  operator s3() const;
};
static_assert(std::swappable_with<swappable_with_rvalue_ref_to_s3&&, s3&&>);

struct swappable_with_rvalue_ref_to_const_s3 {
  swappable_with_rvalue_ref_to_const_s3(
      swappable_with_rvalue_ref_to_const_s3 const&);
  swappable_with_rvalue_ref_to_const_s3(
      swappable_with_rvalue_ref_to_const_s3 const&&);
  swappable_with_rvalue_ref_to_const_s3&
  operator=(swappable_with_rvalue_ref_to_const_s3 const&);
  swappable_with_rvalue_ref_to_const_s3&
  operator=(swappable_with_rvalue_ref_to_const_s3 const&&);

  friend void swap(swappable_with_rvalue_ref_to_const_s3 const&&,
                   swappable_with_rvalue_ref_to_const_s3 const&&);
  friend void swap(s3 const&&, swappable_with_rvalue_ref_to_const_s3 const&&);
  friend void swap(swappable_with_rvalue_ref_to_const_s3 const&&, s3 const&&);

  operator s3() const;
};
static_assert(std::swappable_with<swappable_with_rvalue_ref_to_const_s3 const&&,
                                  s3 const&&>);

struct swappable_with_rvalue_ref_to_volatile_s3 {
  swappable_with_rvalue_ref_to_volatile_s3(
      swappable_with_rvalue_ref_to_volatile_s3 volatile&);
  swappable_with_rvalue_ref_to_volatile_s3(
      swappable_with_rvalue_ref_to_volatile_s3 volatile&&);
  swappable_with_rvalue_ref_to_volatile_s3&
  operator=(swappable_with_rvalue_ref_to_volatile_s3 volatile&);
  swappable_with_rvalue_ref_to_volatile_s3&
  operator=(swappable_with_rvalue_ref_to_volatile_s3 volatile&&);

  friend void swap(swappable_with_rvalue_ref_to_volatile_s3 volatile&&,
                   swappable_with_rvalue_ref_to_volatile_s3 volatile&&);
  friend void swap(s3 volatile&&,
                   swappable_with_rvalue_ref_to_volatile_s3 volatile&&);
  friend void swap(swappable_with_rvalue_ref_to_volatile_s3 volatile&&,
                   s3 volatile&&);

  operator s3 volatile &&() volatile;
};
static_assert(
    std::swappable_with<swappable_with_rvalue_ref_to_volatile_s3 volatile&&,
                        s3 volatile&&>);

struct swappable_with_rvalue_ref_to_cv_s3 {
  swappable_with_rvalue_ref_to_cv_s3(
      swappable_with_rvalue_ref_to_cv_s3 const volatile&);
  swappable_with_rvalue_ref_to_cv_s3(
      swappable_with_rvalue_ref_to_cv_s3 const volatile&&);
  swappable_with_rvalue_ref_to_cv_s3&
  operator=(swappable_with_rvalue_ref_to_cv_s3 const volatile&);
  swappable_with_rvalue_ref_to_cv_s3&
  operator=(swappable_with_rvalue_ref_to_cv_s3 const volatile&&);

  friend void swap(swappable_with_rvalue_ref_to_cv_s3 const volatile&&,
                   swappable_with_rvalue_ref_to_cv_s3 const volatile&&);
  friend void swap(s3 const volatile&&,
                   swappable_with_rvalue_ref_to_cv_s3 const volatile&&);
  friend void swap(swappable_with_rvalue_ref_to_cv_s3 const volatile&&,
                   s3 const volatile&&);

  operator s3 const volatile &&() const volatile;
};
static_assert(
    std::swappable_with<swappable_with_rvalue_ref_to_cv_s3 const volatile&&,
                        s3 const volatile&&>);

namespace union_swap {
union adl_swappable {
  int x;
  double y;

  operator int() const;
};

void swap(adl_swappable&, adl_swappable&) noexcept;
void swap(adl_swappable&&, adl_swappable&&) noexcept;
void swap(adl_swappable&, int&) noexcept;
void swap(int&, adl_swappable&) noexcept;
} // namespace union_swap
static_assert(
    std::swappable_with<union_swap::adl_swappable, union_swap::adl_swappable>);
static_assert(std::swappable_with<union_swap::adl_swappable&,
                                  union_swap::adl_swappable&>);
static_assert(std::swappable_with<union_swap::adl_swappable&&,
                                  union_swap::adl_swappable&&>);
static_assert(std::swappable_with<union_swap::adl_swappable&, int&>);
} // namespace adl

namespace standard_types {
static_assert(
    check_swappable_with<std::array<int, 10>, std::array<int, 10> >());
static_assert(
    !check_swappable_with<std::array<int, 10>, std::array<double, 10> >());
static_assert(check_swappable_with<std::deque<int>, std::deque<int> >());
static_assert(!check_swappable_with<std::deque<int>, std::vector<int> >());
static_assert(
    check_swappable_with<std::forward_list<int>, std::forward_list<int> >());
static_assert(
    !check_swappable_with<std::forward_list<int>, std::vector<int> >());
static_assert(check_swappable_with<std::list<int>, std::list<int> >());
static_assert(!check_swappable_with<std::list<int>, std::vector<int> >());

static_assert(
    check_swappable_with<std::map<int, void*>, std::map<int, void*> >());
static_assert(!check_swappable_with<std::map<int, void*>, std::vector<int> >());
static_assert(check_swappable_with<std::optional<std::vector<int> >,
                                   std::optional<std::vector<int> > >());
static_assert(!check_swappable_with<std::optional<std::vector<int> >,
                                    std::vector<int> >());
static_assert(check_swappable_with<std::vector<int>, std::vector<int> >());
static_assert(!check_swappable_with<std::vector<int>, int>());
} // namespace standard_types

namespace types_with_purpose {
static_assert(!check_swappable_with<DeletedMoveCtor, DeletedMoveCtor>());
static_assert(!check_swappable_with<ImplicitlyDeletedMoveCtor,
                                    ImplicitlyDeletedMoveCtor>());
static_assert(!check_swappable_with<DeletedMoveAssign, DeletedMoveAssign>());
static_assert(!check_swappable_with<ImplicitlyDeletedMoveAssign,
                                    ImplicitlyDeletedMoveAssign>());
static_assert(!check_swappable_with<NonMovable, NonMovable>());
static_assert(
    !check_swappable_with<DerivedFromNonMovable, DerivedFromNonMovable>());
static_assert(!check_swappable_with<HasANonMovable, HasANonMovable>());
} // namespace types_with_purpose

namespace LWG3175 {
// Example taken directly from [concept.swappable]
template <class T, std::swappable_with<T> U>
constexpr void value_swap(T&& t, U&& u) {
  std::ranges::swap(std::forward<T>(t), std::forward<U>(u));
}

template <std::swappable T>
constexpr void lv_swap(T& t1, T& t2) {
  std::ranges::swap(t1, t2);
}

namespace N {
struct A {
  int m;
};
struct Proxy {
  A* a;
  constexpr Proxy(A& a_) : a{&a_} {}
  friend constexpr void swap(Proxy x, Proxy y) {
    std::ranges::swap(*x.a, *y.a);
  }
};
constexpr Proxy proxy(A& a) { return Proxy{a}; }
} // namespace N

constexpr bool CheckRegression() {
  int i = 1, j = 2;
  lv_swap(i, j);
  assert(i == 2 && j == 1);

  N::A a1 = {5}, a2 = {-5};
  value_swap(a1, proxy(a2));
  assert(a1.m == -5 && a2.m == 5);
  return true;
}

static_assert(CheckRegression());
} // namespace LWG3175
