// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
// C++20 20.11.3.7 shared_ptr Creation [util.smartptr.shared.create]
#include <memory>
#ifndef __cpp_lib_shared_ptr_arrays
# error "Feature-test macro for make_shared arrays missing in <memory>"
#elif __cpp_lib_shared_ptr_arrays < 201707L
# error "Feature-test macro for make_shared arrays has wrong value in <memory>"
#endif
#include <testsuite_hooks.h>
int counter = 0;
template<typename T>
struct Alloc : std::allocator<T>
{
  Alloc() = default;
  template<typename U>
    Alloc(const Alloc<U>&) { }
  T* allocate(std::size_t n)
  {
    ++counter;
    return std::allocator<T>::allocate(n);
  }
};
void
test01()
{
  Alloc<int> a;
  std::shared_ptr<int[]> p1 = std::allocate_shared<int[]>(a, 24);
  VERIFY( counter == 1 );
  VERIFY( p1[23] == 0 );
  std::shared_ptr<int[48]> p2 = std::allocate_shared<int[48]>(a);
  VERIFY( counter == 2 );
  VERIFY( p2[47] == 0 );
  std::shared_ptr<int[][12]> p3 = std::allocate_shared<int[][12]>(a, 3);
  VERIFY( counter == 3 );
  VERIFY( p3[2][11] == 0 );
  std::shared_ptr<int[4][5]> p4 = std::allocate_shared<int[4][5]>(a);
  VERIFY( counter == 4 );
  VERIFY( p4[3][4] == 0 );
}
void
test02()
{
  std::shared_ptr<int[]> p1 = std::make_shared<int[]>(24);
  VERIFY( p1[23] == 0 );
  std::shared_ptr<int[48]> p2 = std::make_shared<int[48]>();
  VERIFY( p2[47] == 0 );
  std::shared_ptr<int[][12]> p3 = std::make_shared<int[][12]>(3);
  VERIFY( p3[2][11] == 0 );
  std::shared_ptr<int[4][5]> p4 = std::make_shared<int[4][5]>();
  VERIFY( p4[3][4] == 0 );
}
#include <vector>
#include <cstdint>
std::vector<std::uintptr_t> addresses;
void
test03()
{
  // Verify construction and destruction order
  struct Addressed
  {
    Addressed() { addresses.push_back(me()); }
    ~Addressed() { VERIFY( addresses.back() == me() ); addresses.pop_back(); }
    std::uintptr_t me() const { return reinterpret_cast<std::uintptr_t>(this); }
  };
  auto check = [](auto shptr) {
    std::uintptr_t last = 0;
    for (auto a : addresses)
    {
      VERIFY( a > last );
      last = a;
    }
    shptr.reset();
    return addresses.empty();
  };
  VERIFY( check(std::make_shared<Addressed[][2]>(3)) );
  VERIFY( check(std::make_shared<Addressed[4][2]>()) );
}
void
test04()
{
  // Verify initial value
  auto p1 = std::make_shared<int[]>(3, 9);
  VERIFY( p1[0] == 9 && p1[1] == 9 && p1[2] == 9 );
  auto p2 = std::make_shared<int[2]>(4);
  VERIFY( p2[0] == 4 && p2[1] == 4 );
  auto p3 = std::make_shared<int[][3]>(10, {1,2,3});
  const auto& p3_0 = p3[0];
  VERIFY( p3_0[0] == 1 && p3_0[1] == 2 && p3_0[2] == 3 );
  for (int i = 1; i < 10; ++i)
    for (int j = 0; j < 3; ++j)
      VERIFY( p3[i][j] == p3_0[j] );
  auto p4 = std::make_shared<int[10][3]>({4,5,6});
  const auto& p4_0 = p4[0];
  VERIFY( p4_0[0] == 4 && p4_0[1] == 5 && p4_0[2] == 6 );
  for (int i = 1; i < 10; ++i)
    for (int j = 0; j < 3; ++j)
      VERIFY( p4[i][j] == p4_0[j] );
  auto p5 = std::make_shared<int[][3][2]>(10, {{1,2},{3,4},{5,6}});
  const auto& p5_0 = p5[0];
  VERIFY( p5_0[0][0] == 1 && p5_0[0][1] == 2 );
  VERIFY( p5_0[1][0] == 3 && p5_0[1][1] == 4 );
  VERIFY( p5_0[2][0] == 5 && p5_0[2][1] == 6 );
  for (int i = 1; i < 10; ++i)
    for (int j = 0; j < 3; ++j)
      for (int k = 0; k < 2; ++k)
	VERIFY( p5[i][j][k] == p5_0[j][k] );
  auto p6 = std::make_shared<int[4][3][2]>({{7,8},{9,10},{11,12}});
  const auto& p6_0 = p6[0];
  VERIFY( p6_0[0][0] ==  7 && p6_0[0][1] ==  8 );
  VERIFY( p6_0[1][0] ==  9 && p6_0[1][1] == 10 );
  VERIFY( p6_0[2][0] == 11 && p6_0[2][1] == 12 );
  for (int i = 1; i < 4; ++i)
    for (int j = 0; j < 3; ++j)
      for (int k = 0; k < 2; ++k)
	VERIFY( p6[i][j][k] == p6_0[j][k] );
}
void
test05()
{
  // Examples from the standard
  using namespace std;
  // Example 2
  {
    shared_ptr<double[]> p = make_shared<double[]>(1024);
    // shared_ptr to a value-initialized double[1024]
    for (int i = 0; i < 1024; ++i)
      VERIFY( p[i] == 0.0 );
    shared_ptr<double[][2][2]> q = make_shared<double[][2][2]>(6);
    // shared_ptr to a value-initialized double[6][2][2]
    for (int i = 0; i < 6; ++i)
      for (auto& j : q[i])
	for (auto& k : j)
	  VERIFY( k == 0.0 );
  }
  // Example 3
  {
    shared_ptr<double[1024]> p = make_shared<double[1024]>();
    // shared_ptr to a value-initialized double[1024]
    for (int i = 0; i < 1024; ++i)
      VERIFY( p[i] == 0.0 );
    shared_ptr<double[6][2][2]> q = make_shared<double[6][2][2]>();
    // shared_ptr to a value-initialized double[6][2][2]
    for (int i = 0; i < 6; ++i)
      for (auto& j : q[i])
	for (auto& k : j)
	  VERIFY( k == 0.0 );
  }
  // Example 4
  {
    shared_ptr<double[]> p = make_shared<double[]>(1024, 1.0);
    // shared_ptr to a double[1024], where each element is 1.0
    for (int i = 0; i < 1024; ++i)
      VERIFY( p[i] == 1.0 );
    shared_ptr<double[][2]> q = make_shared<double[][2]>(6, {1.0, 0.0});
    // shared_ptr to a double[6][2], where each double[2] element is {1.0, 0.0}
    for (int i = 0; i < 6; ++i)
      VERIFY( q[i][0] == 1.0 && q[i][1] == 0.0 );
    shared_ptr<vector<int>[]> r = make_shared<vector<int>[]>(4, {1, 2});
    // shared_ptr to a vector<int>[4], where each vector has contents {1, 2}
    for (int i = 0; i < 4; ++i)
      VERIFY( r[i] == vector<int>({1, 2}) );
  }
  // Example 5
  {
    shared_ptr<double[1024]> p = make_shared<double[1024]>(1.0);
    // shared_ptr to a double[1024], where each element is 1.0
    for (int i = 0; i < 1024; ++i)
      VERIFY( p[i] == 1.0 );
    shared_ptr<double[6][2]> q = make_shared<double[6][2]>({1.0, 0.0});
    // shared_ptr to a double[6][2], where each double[2] element is {1.0, 0.0}
    for (int i = 0; i < 6; ++i)
      VERIFY( q[i][0] == 1.0 && q[i][1] == 0.0 );
    shared_ptr<vector<int>[4]> r = make_shared<vector<int>[4]>({1, 2});
    // shared_ptr to a vector<int>[4], where each vector has contents {1, 2}
    for (int i = 0; i < 4; ++i)
      VERIFY( r[i] == vector<int>({1, 2}) );
  }
}
int
main()
{
  test01();
  test02();
  test03();
  test04();
  test05();
}