// 2001-12-27 pme
//
// Copyright (C) 2001-2024 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/>.

// 23.2.1.1 deque constructors, copy, and assignment

#include <deque>
#include <iterator>
#include <sstream>
#include <testsuite_allocator.h>
#include <testsuite_hooks.h>

using __gnu_test::copy_tracker;
using __gnu_test::tracker_allocator_counter;
using __gnu_test::tracker_allocator;
using __gnu_test::copy_constructor;
using __gnu_test::assignment_operator;
using __gnu_test::object_counter;
using __gnu_test::destructor;

typedef std::deque<object_counter>   gdeque;

// 23.2.1     required types
//
// A missing required type will cause a compile failure.
//
void
requiredTypesCheck()
{
  typedef int             T;
  typedef std::deque<T>   X;

  typedef X::reference              reference;
  typedef X::const_reference        const_reference;
  typedef X::iterator               iterator;
  typedef X::const_iterator         const_iterator;
  typedef X::size_type              size_type;
  typedef X::difference_type        difference_type;
  typedef X::value_type             value_type;
  typedef X::allocator_type         allocator_type;
  typedef X::pointer                pointer;
  typedef X::const_pointer          const_pointer;
  typedef X::reverse_iterator       reverse_iterator;
  typedef X::const_reverse_iterator const_reverse_iterator;
}


// @fn defaultConstructorCheck
// Explicitly checks the default deque constructor and destructor for both
// trivial and non-trivial types.  In addition, the size() and empty()
// member functions are explicitly checked here since it should be their
// first use. Checking those functions means checking the begin() and
// end() and their const brethren functions as well.
//
// @verbatim
// 23.2.1.1   default ctor/dtor
//  effects:
//    23.2.1.1        constructs an empty deque using the specified allocator
//  postconditions:
//    23.1 table 65   u.size() == 0
//  throws:
//  complexity:
//    23.1 table 65   constant
//
// 23.2.1.2   bool empty() const
//  semantics:
//    23.1 table 65   a.size() == 0
//    23.1 (7)        a.begin() == a.end()
//  throws:
//  complexity:
//    23.1 table 65   constant
//
// 23.2.1.2   size_type size() const
//  semantics:
//    23.1 table 65   a.end() - a.begin()
//  throws:
//  complexity:
//    23.1 table 65(A) should be constant
//
// 23.2.1     iterator begin()
//            const_iterator begin() const
//            iterator end() 
//            const_iterator end() const
//  throws:
//    23.1 (10) pt. 4 does not throw
//  complexity:
//    23.1 table 65   constant
// @endverbatim
void
defaultConstructorCheckPOD()
{
  // setup
  typedef int             T;
  typedef std::deque<T>   X;

  // run test
  X u;

  // assert postconditions
  VERIFY(u.empty());
  VERIFY(0 == u.size());
  VERIFY(u.begin() == u.end());
  VERIFY(0 == std::distance(u.begin(), u.end()));

  // teardown
}


void
defaultConstructorCheck()
{
  // setup
  typedef copy_tracker  T;
  typedef std::deque<T>     X;

  copy_tracker::reset();

  // run test
  const X u;

  // assert postconditions
  VERIFY(u.empty());
  VERIFY(0 == u.size());
  VERIFY(u.begin() == u.end());
  VERIFY(0 == std::distance(u.begin(), u.end()));

  // teardown
}


// @fn copyConstructorCheck()
// Explicitly checks the deque copy constructor.  Continues verificaton of
// ancillary member functions documented under defaultConstructorCheck().
//
// This check also tests the push_back() member function.
//
// @verbatim
// 23.2.1     copy constructor
//  effects:
//  postconditions:
//    22.1.1 table 65 a == X(a)
//                    u == a
//  throws:
//  complexity:
//    22.1.1 table 65 linear
// @endverbatim
void
copyConstructorCheck()
{
  // setup
  typedef copy_tracker  T;
  typedef std::deque<T>     X;

  const std::size_t copyBaseSize = 17;  // arbitrary

  X a;
  for (std::size_t i = 0; i < copyBaseSize; ++i)
    a.push_back(i);
  copy_tracker::reset();

  // assert preconditions
  VERIFY(!a.empty());
  VERIFY(copyBaseSize == a.size());
  VERIFY(a.begin() != a.end());
  VERIFY( copyBaseSize == static_cast<std::size_t>(std::distance(a.begin(), a.end())) );

  // run test
  X u = a;

  // assert postconditions
  VERIFY(u == a);
  VERIFY(copyBaseSize == copy_constructor::count());

  // teardown
}


// @fn fillConstructorCheck()
// This test explicitly verifies the basic fill constructor.  Like the default
// constructor, later tests depend on the fill constructor working correctly.
// That means this explicit test should precede the later tests so the error
// message given on assertion failure can be more helpful n tracking the
// problem.
// 
// 23.2.1.1   fill constructor
//  complexity:
//    23.2.1.1        linear in N
void
fillConstructorCheck()
{
  // setup
  typedef copy_tracker  T;
  typedef std::deque<T>   X;

  const X::size_type  n(23);  
  const X::value_type t(111);

  copy_tracker::reset();

  // run test
  X a(n, t);

  // assert postconditions
  VERIFY(n == a.size());
  VERIFY(n == copy_constructor::count());

  // teardown
}


// @fn fillConstructorCheck2()
// Explicit check for fill constructors masqueraded as range constructors as
// elucidated in clause 23.1.1 paragraph 9 of the standard.
//
// 23.1.1 (9) fill constructor looking like a range constructor
void
fillConstructorCheck2()
{
  typedef copy_tracker  T;
  typedef std::deque<T>   X;

  const std::size_t f = 23;  
  const std::size_t l = 111;

  copy_tracker::reset();

  X a(f, l);

  VERIFY(f == a.size());
  VERIFY(f == copy_constructor::count());
}


// @fn rangeConstructorCheckForwardIterator()
// This test copies from one deque to another to force the copy
// constructor for T to be used because the compiler will kindly
// elide copies if the default constructor can be used with
// type conversions.  Trust me.
//
// 23.2.1.1   range constructor, forward iterators
void
rangeConstructorCheckForwardIterator()
{
  // setup
  typedef copy_tracker  T;
  typedef std::deque<T>   X;

  const X::size_type  n(726); 
  const X::value_type t(307);
  X source(n, t);
  X::iterator i = source.begin();
  X::iterator j = source.end();
  X::size_type rangeSize = std::distance(i, j);

  copy_tracker::reset();

  // test
  X a(i, j);

  // assert postconditions
  VERIFY(rangeSize == a.size());
  VERIFY(copy_constructor::count() <= rangeSize);
}


// @fn rangeConstructorCheckInputIterator()
// An explicit check for range construction on an input iterator
// range, which the standard expounds upon as having a different
// complexity than forward iterators.
//
// 23.2.1.1   range constructor, input iterators
void
rangeConstructorCheckInputIterator()
{
  typedef copy_tracker  T;
  typedef std::deque<T>     X;

  std::istringstream ibuf("1234567890123456789");
  const X::size_type rangeSize = ibuf.str().size();  
  std::istream_iterator<char>  i(ibuf);
  std::istream_iterator<char>  j;

  copy_tracker::reset();

  X a(i, j);

  VERIFY(rangeSize == a.size());
  VERIFY(copy_constructor::count() <= (2 * rangeSize));
}


// 23.2.1     copy assignment
void
copyAssignmentCheck()
{
  typedef copy_tracker  T;
  typedef std::deque<T>     X;

  const X::size_type  n(18);  
  const X::value_type t(1023);
  X a(n, t);
  X r;

  copy_tracker::reset();

  r = a;

  VERIFY(r == a);
  VERIFY(n == copy_constructor::count());
}


// 23.2.1.1   fill assignment
//
// The complexity check must check dtors+copyAssign and
// copyCtor+copyAssign because that's the way the SGI implementation
// works.  Dunno if it's true standard compliant (which specifies fill
// assignment in terms of erase and insert only), but it should work
// as (most) users expect and is more efficient.
void
fillAssignmentCheck()
{
  typedef copy_tracker  T;
  typedef std::deque<T>   X;

  const X::size_type  starting_size(10);  
  const X::value_type starting_value(66);
  const X::size_type  n(23);  
  const X::value_type t(111);

  X a(starting_size, starting_value);
  copy_tracker::reset();

  // preconditions
  VERIFY(starting_size == a.size());

  // test
  a.assign(n, t);

  // postconditions
  VERIFY(n == a.size());
  VERIFY(n == (copy_constructor::count() + assignment_operator::count()));
  VERIFY(starting_size == (destructor::count() + assignment_operator::count()));
}


// @verbatim
// 23.2.1     range assignment
// 23.2.1.1   deque constructors, copy, and assignment
//  effects:
//  Constructs a deque equal to the range [first, last), using the
//  specified allocator.
//
//      template<typename InputIterator>
//        assign(InputIterator first, InputIterator last);
//      
//    is equivalent to
//
//      erase(begin(), end());
//      insert(begin(), first, last);
//
//  postconditions:
//  throws:
//  complexity:
//    forward iterators: N calls to the copy constructor, 0 reallocations
//    input iterators:   2N calls to the copy constructor, log(N) reallocations
// @endverbatim
void
rangeAssignmentCheck()
{
  typedef copy_tracker  T;
  typedef std::deque<T>   X;

  const X::size_type  source_size(726); 
  const X::value_type source_value(307);
  const X::size_type  starting_size(10);  
  const X::value_type starting_value(66);

  X source(source_size, source_value);
  X::iterator i = source.begin();
  X::iterator j = source.end();
  X::size_type rangeSize = std::distance(i, j);

  X a(starting_size, starting_value);
  VERIFY(starting_size == a.size());

  copy_tracker::reset();

  a.assign(i, j);

  VERIFY(source == a);
  VERIFY(rangeSize == (copy_constructor::count() + assignment_operator::count()));
  VERIFY(starting_size == (destructor::count() + assignment_operator::count()));
}


// 23.1 (10)  range assignment
// 23.2.1.3   with exception
void
rangeAssignmentCheckWithException()
{
  // setup
  typedef copy_tracker T;
  typedef std::deque<T>    X;

  // test
  // What does "no effects" mean?
}


// 23.1.1 (9) fill assignment looking like a range assignment
void
fillAssignmentCheck2()
{
  // setup
  typedef copy_tracker T;
  typedef std::deque<T>    X;

  // test
  // What does "no effects" mean?
}

// Verify that the default deque constructor offers the basic exception
// guarantee.
void
test_default_ctor_exception_safety()
{
  // setup
  typedef copy_tracker T;
  typedef std::deque<T, tracker_allocator<T> > X;

  T::reset();
  copy_constructor::throw_on(3);
  tracker_allocator_counter::reset();

  // test
  try
  {
    T ref;
    X a(7, ref);
    VERIFY( false );
  }
  catch (...)
  {
  }

  // assert postconditions
  VERIFY(tracker_allocator_counter::get_allocation_count() == tracker_allocator_counter::get_deallocation_count());

  // teardown
}

// Verify that the copy constructor offers the basic exception guarantee.
void
test_copy_ctor_exception_safety()
{
  // setup
  typedef copy_tracker T;
  typedef std::deque<T, tracker_allocator<T> > X;

  tracker_allocator_counter::reset();
  {
    X a(7);
    T::reset();
    copy_constructor::throw_on(3);


    // test
    try
    {
      X u(a);
      VERIFY(false);
    }
    catch (...)
    {
    }
  }

  // assert postconditions
  VERIFY(tracker_allocator_counter::get_allocation_count() == tracker_allocator_counter::get_deallocation_count());

  // teardown
}

int main()
{
  // basic functionality and standard conformance checks
  requiredTypesCheck();
  defaultConstructorCheckPOD();
  defaultConstructorCheck();
  test_default_ctor_exception_safety();
  copyConstructorCheck();
  test_copy_ctor_exception_safety();
  fillConstructorCheck();
  fillConstructorCheck2();
  rangeConstructorCheckInputIterator();
  rangeConstructorCheckForwardIterator();
  copyAssignmentCheck();
  fillAssignmentCheck();
  fillAssignmentCheck2();
  rangeAssignmentCheck();
  rangeAssignmentCheckWithException();
  return 0;
}
