// { dg-do run { target c++11 } }

// Copyright (C) 2012-2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

#include <memory>
#include <scoped_allocator>
#include <vector>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>

// 20.12.4 Scoped allocator adaptor members [allocator.adaptor.members]
//
// Test piecewise construction of std::pair by scoped_allocator_adaptor

using __gnu_test::uneq_allocator;
using std::scoped_allocator_adaptor;

// a DefaultConstructible and CopyConstructible type
struct def
{
  def() : id(999) { }

  int id;
};

// a CopyConstructible and non-DefaultConstructible type
struct copyable
{
  copyable(int id) : id(id) { }

  // not constructed with an allocator so nothing to test
  bool verify() const { return true; }

  int id;
};

// a MoveConstructible and non-DefaultConstructible type
struct move_only
{
  move_only(int id) : id(id) { }
  move_only(move_only&&) = default;

  // not constructed with an allocator so nothing to test
  bool verify() const { return true; }

  int id;
};

// a type for which std::uses_allocator is true
struct uses_alloc_post
{
  typedef uneq_allocator<uses_alloc_post> allocator_type;

  uses_alloc_post(const allocator_type& alloc)
  : allocator_personality(alloc.get_personality()), id(999)
  { }

  uses_alloc_post(copyable arg, const allocator_type& alloc)
  : allocator_personality(alloc.get_personality()), id(arg.id)
  { }

  uses_alloc_post(move_only arg, const allocator_type& alloc)
  : allocator_personality(alloc.get_personality()), id(arg.id)
  { }

  // allocator-extended copy ctor
  uses_alloc_post(const uses_alloc_post& other, const allocator_type& alloc)
  : allocator_personality(alloc.get_personality()), id(other.id)
  { }

  // verify we were constructed with right allocator
  bool verify() const { return allocator_personality == id; }

  int allocator_personality;
  int id;
};

// a type for which std::uses_allocator is true
struct uses_alloc_pre : uses_alloc_post
{
  typedef uneq_allocator<uses_alloc_pre> allocator_type;

  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc)
  : uses_alloc_post(alloc)
  { }

  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc,
                 copyable arg)
  : uses_alloc_post(arg, alloc)
  { }

  // allocator-extended copy ctor
  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc,
                 const uses_alloc_pre& other)
  : uses_alloc_post(other, alloc)
  { }

  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc,
                 move_only arg)
  : uses_alloc_post(std::move(arg), alloc)
  { }
};

template<typename A, typename B>
  void
  test_def()
  {
    typedef std::pair<A, B> test_type;
    typedef uneq_allocator<test_type> alloc_type;
    typedef scoped_allocator_adaptor<alloc_type, alloc_type> alloc_adaptor;

    int inner_id = 2;
    alloc_adaptor a(-1, alloc_type(inner_id)); // outer=-1, inner=2

    // all pair members that can be constructed with an allocator
    // should be constructed with the inner allocator, with personality==2

    auto p = a.allocate(1);

    // construct(pair<T1, T2>* p, piecewise_construct_t, tuple<...>, tuple<...>)
    std::tuple<> t;
    a.construct(p, std::piecewise_construct, t, t);
    VERIFY( p->first.id == 999 );
    VERIFY( p->second.id == 999 );
    a.destroy(p);

    // construct(pair<T1, T2>* __p)
    a.construct(p);
    VERIFY( p->first.id == 999 );
    VERIFY( p->second.id == 999 );
    auto pp = *p;
    a.destroy(p);

    // construct(pair<T1, T2>* p, const pair<U, V>& x)
    a.construct(p, pp);
    VERIFY( p->first.id == 999 );
    VERIFY( p->second.id == 999 );
    a.destroy(p);

    // construct(pair<T1, T2>* p, pair<U, V>&& x)
    a.construct(p, std::move(pp));
    VERIFY( p->first.id == 999 );
    VERIFY( p->second.id == 999 );
    a.destroy(p);

    a.deallocate(p, 1);
  }

template<typename A, typename B>
  void
  test_copying()
  {
    typedef std::pair<A, B> test_type;
    typedef uneq_allocator<test_type> alloc_type;
    typedef scoped_allocator_adaptor<alloc_type, alloc_type> alloc_adaptor;

    int inner_id = 2;
    alloc_adaptor a(-1, alloc_type(inner_id)); // outer=-1, inner=2

    // all pair members that can be constructed with an allocator
    // should be constructed with the inner allocator, with personality==2

    auto p = a.allocate(1);

    // construct(pair<T1, T2>* p, piecewise_construct_t, tuple<...>, tuple<...>)
    auto t = std::make_tuple(copyable(inner_id));
    a.construct(p, std::piecewise_construct, t, t);
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    a.destroy(p);

    // construct(pair<T1, T2>* __p)
    // cannot test this overload using non-DefaultConstructible types

    // construct(pair<T1, T2>* p, U&& x, V&& y)
    copyable c(inner_id);
    a.construct(p, c, c);
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    auto pp = *p;
    a.destroy(p);

    // construct(pair<T1, T2>* p, const pair<U, V>& x)
    a.construct(p, pp);
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    a.destroy(p);

    // construct(pair<T1, T2>* p, pair<U, V>&& x)
    a.construct(p, std::move(pp));
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    a.destroy(p);

    a.deallocate(p, 1);
  }

template<typename A, typename B>
  void
  test_moving()
  {
    typedef std::pair<A, B> test_type;
    typedef uneq_allocator<test_type> alloc_type;
    typedef scoped_allocator_adaptor<alloc_type, alloc_type> alloc_adaptor;

    int inner_id = 2;
    alloc_adaptor a(-1, alloc_type(inner_id)); // outer=-1, inner=2

    // all pair members that can be constructed with an allocator
    // should be constructed with the inner allocator, with personality==2

    auto p = a.allocate(1);

    // construct(pair<T1, T2>* p, piecewise_construct_t, tuple<...>, tuple<...>)
    a.construct(p, std::piecewise_construct,
                std::make_tuple(move_only(inner_id)),
                std::make_tuple(move_only(inner_id)));
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    a.destroy(p);

    // construct(pair<T1, T2>* __p)
    // cannot test this overload using non-DefaultConstructible types

    // construct(pair<T1, T2>* p, U&& x, V&& y)
    a.construct(p, move_only(inner_id), move_only(inner_id));
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    a.destroy(p);

    // construct(pair<T1, T2>* p, const pair<U, V>& x)
    // cannot test this overload using move-only types

    // construct(pair<T1, T2>* p, pair<U, V>&& x)
    a.construct(p, std::make_pair(move_only(inner_id), move_only(inner_id)));
    VERIFY( p->first.verify() );
    VERIFY( p->second.verify() );
    a.destroy(p);

    a.deallocate(p, 1);
  }

void test01()
{
  test_def<def, def>();
  test_def<def, uses_alloc_pre>();
  test_def<def, uses_alloc_post>();
  test_def<uses_alloc_pre, def>();
  test_def<uses_alloc_pre, uses_alloc_pre>();
  test_def<uses_alloc_pre, uses_alloc_post>();
  test_def<uses_alloc_post, def>();
  test_def<uses_alloc_post, uses_alloc_pre>();
  test_def<uses_alloc_post, uses_alloc_post>();
}

void test02()
{
  test_copying<copyable, copyable>();
  test_copying<copyable, uses_alloc_pre>();
  test_copying<copyable, uses_alloc_post>();
  test_copying<uses_alloc_pre, copyable>();
  test_copying<uses_alloc_pre, uses_alloc_pre>();
  test_copying<uses_alloc_pre, uses_alloc_post>();
  test_copying<uses_alloc_post, copyable>();
  test_copying<uses_alloc_post, uses_alloc_pre>();
  test_copying<uses_alloc_post, uses_alloc_post>();
}

void test03()
{
  test_moving<move_only, move_only>();
  test_moving<move_only, uses_alloc_pre>();
  test_moving<move_only, uses_alloc_post>();
  test_moving<uses_alloc_pre, move_only>();
  test_moving<uses_alloc_pre, uses_alloc_pre>();
  test_moving<uses_alloc_pre, uses_alloc_post>();
  test_moving<uses_alloc_post, move_only>();
  test_moving<uses_alloc_post, uses_alloc_pre>();
  test_moving<uses_alloc_post, uses_alloc_post>();
}

int main()
{
  test01();
  test02();
  test03();
}
