text: //===----------------------------------------------------------------------===//
  //
  // 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
  // UNSUPPORTED: libcpp-no-concepts
  // UNSUPPORTED: libcpp-has-no-incomplete-ranges
  
  // <memory>
  //
  // namespace ranges {
  //   template<nothrow-input-iterator InputIterator>
  //     requires destructible<iter_value_t<InputIterator>>
  //     constexpr InputIterator destroy_n(InputIterator first, iter_difference_t<InputIterator> n) noexcept; // since C++20
  // }
  
  #include <cassert>
  #include <memory>
  #include <ranges>
  #include <type_traits>
  
  #include "test_iterators.h"
  #include "test_macros.h"
  
  // TODO(varconst): consolidate the ADL checks into a single file.
  // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
  // implementations are allowed to use a different mechanism to achieve this effect, so this check is
  // libc++-specific.
  LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy_n)>);
  
  struct NotNothrowDtrable {
    ~NotNothrowDtrable() noexcept(false) {}
  };
  static_assert(!std::is_invocable_v<decltype(std::ranges::destroy_n), NotNothrowDtrable*, int>);
  
  struct Counted {
    int& count;
  
    constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
    constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
    constexpr ~Counted() { --count; }
  
    friend void operator&(Counted) = delete;
  };
  
  template <class Iterator>
  constexpr void test() {
    {
      constexpr int N = 5;
      std::allocator<Counted> alloc;
      using Traits = std::allocator_traits<decltype(alloc)>;
      int counter = 0;
  
      Counted* out = Traits::allocate(alloc, N);
      for (int i = 0; i != N; ++i) {
        Traits::construct(alloc, out + i, counter);
      }
      assert(counter == N);
  
      std::ranges::destroy_n(Iterator(out), N);
      assert(counter == 0);
  
      Traits::deallocate(alloc, out, N);
    }
  }
  
  constexpr bool tests() {
    test<Counted*>();
    test<forward_iterator<Counted*>>();
  
    return true;
  }
  
  constexpr bool test_arrays() {
    // One-dimensional array.
    {
      constexpr int N = 5;
      constexpr int M = 3;
  
      using Array = Counted[M];
      std::allocator<Array> alloc;
      using Traits = std::allocator_traits<decltype(alloc)>;
      int counter = 0;
  
      Array* buffer = Traits::allocate(alloc, N);
      for (int i = 0; i != N; ++i) {
        Array& array_ref = *(buffer + i);
        for (int j = 0; j != M; ++j) {
          Traits::construct(alloc, std::addressof(array_ref[j]), counter);
        }
      }
      assert(counter == N * M);
  
      std::ranges::destroy_n(buffer, N);
      assert(counter == 0);
  
      Traits::deallocate(alloc, buffer, N);
    }
  
    // Multidimensional array.
    {
      constexpr int N = 5;
      constexpr int A = 3;
      constexpr int B = 3;
  
      using Array = Counted[A][B];
      std::allocator<Array> alloc;
      using Traits = std::allocator_traits<decltype(alloc)>;
      int counter = 0;
  
      Array* buffer = Traits::allocate(alloc, N);
      for (int i = 0; i != N; ++i) {
        Array& array_ref = *(buffer + i);
        for (int j = 0; j != A; ++j) {
          for (int k = 0; k != B; ++k) {
            Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
          }
        }
      }
      assert(counter == N * A * B);
  
      std::ranges::destroy_n(buffer, N);
      assert(counter == 0);
  
      Traits::deallocate(alloc, buffer, N);
    }
  
    return true;
  }
  
  int main(int, char**) {
    tests();
    test_arrays();
  
    static_assert(tests());
    // TODO: Until std::construct_at has support for arrays, it's impossible to test this
    //       in a constexpr context (see https://reviews.llvm.org/D114903).
    // static_assert(test_arrays());
  
    return 0;
  }
  