/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
#include <vector>
#include <algorithm>
#include <functional>

#if defined (STLPORT) && defined (_STLP_DEBUG) && defined (_STLP_DEBUG_MODE_THROWS)
#  define _STLP_DO_CHECK_BAD_PREDICATE
#  include <stdexcept>
#endif

#include "cppunit/cppunit_proxy.h"

#if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
using namespace std;
#endif

//
// TestCase class
//
class SortTest : public CPPUNIT_NS::TestCase
{
  CPPUNIT_TEST_SUITE(SortTest);
  CPPUNIT_TEST(sort1);
  CPPUNIT_TEST(sort2);
  CPPUNIT_TEST(sort3);
  CPPUNIT_TEST(sort4);
  CPPUNIT_TEST(stblsrt1);
  CPPUNIT_TEST(stblsrt2);
#if defined (_STLP_DO_CHECK_BAD_PREDICATE)
  CPPUNIT_TEST(bad_predicate_detected);
#endif
  CPPUNIT_TEST_SUITE_END();

protected:
  void sort1();
  void sort2();
  void sort3();
  void sort4();
  void stblsrt1();
  void stblsrt2();
  void bad_predicate_detected();

  static bool string_less(const char* a_, const char* b_)
  {
    return strcmp(a_, b_) < 0 ? 1 : 0;
  }
};

CPPUNIT_TEST_SUITE_REGISTRATION(SortTest);

//
// tests implementation
//
void SortTest::stblsrt1()
{
  //Check that stable_sort do sort
  int numbers[6] = { 1, 50, -10, 11, 42, 19 };
  stable_sort(numbers, numbers + 6);
  //-10 1 11 19 42 50
  CPPUNIT_ASSERT(numbers[0]==-10);
  CPPUNIT_ASSERT(numbers[1]==1);
  CPPUNIT_ASSERT(numbers[2]==11);
  CPPUNIT_ASSERT(numbers[3]==19);
  CPPUNIT_ASSERT(numbers[4]==42);
  CPPUNIT_ASSERT(numbers[5]==50);

  char const* letters[6] = {"bb", "aa", "ll", "dd", "qq", "cc" };
  stable_sort(letters, letters + 6, string_less);
  // aa bb cc dd ll qq
  CPPUNIT_ASSERT( strcmp(letters[0], "aa") == 0 );
  CPPUNIT_ASSERT( strcmp(letters[1], "bb") == 0 );
  CPPUNIT_ASSERT( strcmp(letters[2], "cc") == 0 );
  CPPUNIT_ASSERT( strcmp(letters[3], "dd") == 0 );
  CPPUNIT_ASSERT( strcmp(letters[4], "ll") == 0 );
  CPPUNIT_ASSERT( strcmp(letters[5], "qq") == 0 );
}

struct Data {
  Data(int index, int value)
    : m_index(index), m_value(value) {}

  bool operator == (const Data& other) const
  { return m_index == other.m_index && m_value == other.m_value; }
  bool operator < (const Data& other) const
  { return m_value < other.m_value; }

private:
  int m_index, m_value;
};

void SortTest::stblsrt2()
{
  //Check that stable_sort is stable:
  Data datas[] = {
    Data(0, 10),
    Data(1, 8),
    Data(2, 6),
    Data(3, 6),
    Data(4, 6),
    Data(5, 4),
    Data(6, 9)
  };
  stable_sort(datas, datas + 7);

  CPPUNIT_ASSERT( datas[0] == Data(5, 4) );
  CPPUNIT_ASSERT( datas[1] == Data(2, 6) );
  CPPUNIT_ASSERT( datas[2] == Data(3, 6) );
  CPPUNIT_ASSERT( datas[3] == Data(4, 6) );
  CPPUNIT_ASSERT( datas[4] == Data(1, 8) );
  CPPUNIT_ASSERT( datas[5] == Data(6, 9) );
  CPPUNIT_ASSERT( datas[6] == Data(0, 10) );
}

void SortTest::sort1()
{
  int numbers[6] = { 1, 50, -10, 11, 42, 19 };

  sort(numbers, numbers + 6);
  // -10 1 11 19 42 50
  CPPUNIT_ASSERT(numbers[0]==-10);
  CPPUNIT_ASSERT(numbers[1]==1);
  CPPUNIT_ASSERT(numbers[2]==11);
  CPPUNIT_ASSERT(numbers[3]==19);
  CPPUNIT_ASSERT(numbers[4]==42);
  CPPUNIT_ASSERT(numbers[5]==50);
}

void SortTest::sort2()
{
  int numbers[] = { 1, 50, -10, 11, 42, 19 };

  int count = sizeof(numbers) / sizeof(numbers[0]);
  sort(numbers, numbers + count, greater<int>());

  //  50 42 19 11 1 -10
  CPPUNIT_ASSERT(numbers[5]==-10);
  CPPUNIT_ASSERT(numbers[4]==1);
  CPPUNIT_ASSERT(numbers[3]==11);
  CPPUNIT_ASSERT(numbers[2]==19);
  CPPUNIT_ASSERT(numbers[1]==42);
  CPPUNIT_ASSERT(numbers[0]==50);
}

void SortTest::sort3()
{
  vector<bool> boolVector;

  boolVector.push_back( true );
  boolVector.push_back( false );

  sort( boolVector.begin(), boolVector.end() );

  CPPUNIT_ASSERT(boolVector[0]==false);
  CPPUNIT_ASSERT(boolVector[1]==true);
}

/*
 * A small utility class to check a potential compiler bug
 * that can result in a bad sort algorithm behavior. The type
 * _Tp of the SortTestFunc has to be SortTestAux without any
 * reference qualifier.
 */
struct SortTestAux {
  SortTestAux (bool &b) : _b(b)
  {}

  SortTestAux (SortTestAux const&other) : _b(other._b) {
    _b = true;
  }

  bool &_b;

private:
  //explicitely defined as private to avoid warnings:
  SortTestAux& operator = (SortTestAux const&);
};

template <class _Tp>
void SortTestFunc (_Tp) {
}

void SortTest::sort4()
{
  bool copy_constructor_called = false;
  SortTestAux instance(copy_constructor_called);
  SortTestAux &r_instance = instance;
  SortTestAux const& rc_instance = instance;

  SortTestFunc(r_instance);
  CPPUNIT_ASSERT(copy_constructor_called);
  copy_constructor_called = false;
  SortTestFunc(rc_instance);
  CPPUNIT_ASSERT(copy_constructor_called);
}

#if defined (_STLP_DO_CHECK_BAD_PREDICATE)
void SortTest::bad_predicate_detected()
{
  int numbers[] = { 0, 0, 1, 0, 0, 1, 0, 0 };
  try {
    sort(numbers, numbers + sizeof(numbers) / sizeof(numbers[0]), less_equal<int>());

    //Here is means that no exception has been raised
    CPPUNIT_ASSERT( false );
  }
  catch (runtime_error const&)
  { /*OK bad predicate has been detected.*/ }

  try {
    stable_sort(numbers, numbers + sizeof(numbers) / sizeof(numbers[0]), less_equal<int>());

    //Here is means that no exception has been raised
    CPPUNIT_ASSERT( false );
  }
  catch (runtime_error const&)
  { /*OK bad predicate has been detected.*/ }
}
#endif
