/*
    Copyright 2007-2008 Adobe Systems Incorporated
    Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
    or a copy at http://stlab.adobe.com/licenses.html )
	

	This test file started life as 
		ISO/IEC TR 18015:2006(E) Appendix D.4


Goals:
	
	Compare the performance of function pointers, functors, inline functors,
		standard functors, and native comparison operators

	Also compare the performance of qsort(), quicksort template, and std::sort


Assumptions:
	
	1) inline functors, standard functors and inlined native
		comparisons will perform similarly
	
	2) using functors is faster than using function pointers
	
	3) inline functors are as fast or faster than out of line functors
	
	4) a template is at least as fast as a hard coded function of
		the same algorithm, sometimes faster
	
	5) std::sort is faster than qsort()
	
	6) std::sort is faster than a naive quicksort template using the same functor


Since qsort's comparison function must return int (less than 0, 0, greater than 0)
	and std::sort's must return a bool, it is not possible to test them with each
	other's comparator.

*/


/******************************************************************************/

#include <functional>
#include <algorithm>
#include <cstdlib>

#include "benchmark_results.h"
#include "benchmark_timer.h"

using namespace std;

/******************************************************************************/

template <class Iterator>
void verify_sorted(Iterator first, Iterator last) {
	Iterator prev = first;
	first++;
	while (first != last) {
		if (*first++ < *prev++) {
			printf("test %i failed\n", current_test);
			break;
		}
	}
}

/******************************************************************************/
// --------- helper functions --------------------------------------------

// qsort passes void * arguments to its comparison function,
// which must return negative, 0, or positive value
int
less_than_function1( const void * lhs, const void * rhs )
	{
	if( *(const double *) lhs < *(const double *) rhs ) return -1;
	if( *(const double *) lhs > *(const double *) rhs ) return 1;
	return 0;
	}

// std::sort, on the other hand, needs a comparator that returns true or false
bool
less_than_function2( const double lhs, const double rhs )
	{
	return( lhs < rhs? true : false );
	}

// the comparison operator in the following functor is defined out of line
struct less_than_functor
{
	bool operator()( const double& lhs, const double& rhs ) const;
};

bool
less_than_functor::operator()( const double& lhs, const double& rhs ) const
	{
	return( lhs < rhs? true : false );
	}

// the comparison operator in the following functor is defined inline
struct inline_less_than_functor
{
	inline bool operator()( const double& lhs, const double& rhs ) const
		{
		return( lhs < rhs? true : false );
		}
};

/******************************************************************************/

// hard coded comparison function
template<class Iterator>
void quicksort(Iterator begin, Iterator end)
{
	// this only works for pointers and STL iterators
	typedef typename iterator_traits<Iterator>::value_type T;
	
	if ( (end - begin) > 1 ) {

		T middleValue = *begin;
		Iterator left = begin;
		Iterator right = end;

		for(;;) {

			while ( middleValue < *(--right) );
			if ( !(left < right ) ) break;
			
			while ( *(left) < middleValue )
				++left;
			if ( !(left < right ) ) break;

			// swap
			T temp = *right;
			*right = *left;
			*left = temp;
		}
		
		quicksort( begin, right + 1 );
		quicksort( right + 1, end );
	}
}

/******************************************************************************/

// comparison function passed in as a functor
template<class Iterator, typename Comparator>
void quicksort(Iterator begin, Iterator end, Comparator compare)
{
	// this only works for pointers and STL iterators
	typedef typename iterator_traits<Iterator>::value_type T;
	
	if ( (end - begin) > 1 ) {

		T middleValue = *begin;
		Iterator left = begin;
		Iterator right = end;

		for(;;) {

			while ( compare( middleValue, *(--right) ) );
			if ( !(left < right ) ) break;
			while ( compare( *(left), middleValue ) )
				++left;
			if ( !(left < right ) ) break;

			// swap
			T temp = *right;
			*right = *left;
			*left = temp;
		}
		
		quicksort( begin, right + 1, compare );
		quicksort( right + 1, end, compare );
	}
}

/******************************************************************************/

typedef bool comparator_function( const double x, const double y );

// use a pointer to function as a template parameter
// exact function is known at compile time, and can be inlined
template<class Iterator, comparator_function compare>
void quicksort(Iterator begin, Iterator end)
{
	// this only works for pointers and STL iterators
	typedef typename iterator_traits<Iterator>::value_type T;
	
	if ( (end - begin) > 1 ) {

		T middleValue = *begin;
		Iterator left = begin;
		Iterator right = end;

		for(;;) {

			while ( compare( middleValue, *(--right) ) );
			if ( !(left < right ) ) break;
			while ( compare( *(left), middleValue ) )
				++left;
			if ( !(left < right ) ) break;

			// swap
			T temp = *right;
			*right = *left;
			*left = temp;
		}
		
		quicksort( begin, right + 1, compare );
		quicksort( right + 1, end, compare );
	}
}

/******************************************************************************/

// use a function pointer
// most compilers will not inline the function argument
void quicksort_function(double* begin, double* end, comparator_function compare)
{
	if ( (end - begin) > 1 ) {

		double middleValue = *begin;
		double* left = begin;
		double* right = end;

		for(;;) {

			while ( compare( middleValue, *(--right) ) );
			if ( !(left < right ) ) break;
			while ( compare( *(left), middleValue ) )
				++left;
			if ( !(left < right ) ) break;

			// swap
			double temp = *right;
			*right = *left;
			*left = temp;
		}
		
		quicksort( begin, right + 1, compare );
		quicksort( right + 1, end, compare );
	}
}

/******************************************************************************/

//int main(int argc, char* argv[])
int FunctionObjectsTest(int iLoops, int iSize)
{
	int i;
//	int iterations = (1 < argc) ? atoi(argv[1]) : 2000; // number of iterations
//	int tablesize = (2 < argc) ? atoi(argv[2]) : 10000; // size of array
	int iterations = iLoops; // number of iterations
	int tablesize = iSize; // size of array


	// output command for documentation
/*	for (i = 0; i < argc; ++i)
		printf("%s ", argv[i] );
	printf("\n");
*/
	
	// seed the random number generator, so we get repeatable results
	srand( tablesize + 123 );
	
	
	// initialize the table to sort
	double * master_table = new double[tablesize];
	for( int n = 0; n < tablesize; ++n )
		{
		master_table[n] = static_cast<double>( rand() );
		}
	
	double * table = new double[tablesize]; // working copy
	
	
	
	// here is where the timing starts
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		qsort( table, tablesize, sizeof(double), less_than_function1 );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "qsort array with function pointer" );
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort_function( table, table + tablesize, less_than_function2 );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort function array with function pointer" );
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort( table, table + tablesize, less_than_function2 );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort template array with function pointer" );
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort<double *, less_than_function2 >( table, table + tablesize  );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort template array with template function pointer" );

	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		sort( table, table + tablesize, less_than_function2 );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "sort array with function pointer" );

	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort( table, table + tablesize, less_than_functor() );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort template array with user-supplied functor" );
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		sort( table, table + tablesize, less_than_functor() );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "sort array with user-supplied functor");

	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort( table, table + tablesize, inline_less_than_functor() );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort template array with user-supplied inline functor" );
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		sort( table, table + tablesize, inline_less_than_functor() );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "sort array with user-supplied inline functor");
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort( table, table + tablesize, less<double>() );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort template array with standard functor" );
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		sort( table, table + tablesize, less<double>() );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "sort array with standard functor");
	
	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy(master_table, master_table+tablesize, table);
		quicksort( table, table + tablesize );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "quicksort template array with native < operator" );

	start_timer();
	for (i = 0; i < iterations; ++i)
		{
		copy( master_table, master_table+tablesize, table );
		sort( table, table + tablesize );
		verify_sorted( table, table + tablesize );
		}
	record_result( timer(), "sort array with native < operator");
	

	//summarize("Function Objects", tablesize, iterations, kDontShowGMeans, kDontShowPenalty );
	
	delete[] table;
	delete[] master_table;
	
	return 0;
}
