#include <chrono>
#include <ctime>
#include <type_traits>
#include <tuple>
#include <iostream>
#include <algorithm>
#include <functional>
#include "smoothsort.h"
#include "timsort.h"
 
// http://b...content-available-to-author-only...r.ca/2012/05/13/timing-functions-with-chrono.html
template <typename Clock, typename Func>
inline typename Clock::duration time_function(Func&& f)
{
	auto start_time = Clock::now();
	f();
	auto time_taken = Clock::now() - start_time;
	return time_taken;
}
 
enum state_t
{
    sorted, randomized, reversed, partially_sorted_0, partially_sorted_1
};
 
template <typename value_t>
static void bench(int const size, state_t const state)
{
	typedef std::chrono::high_resolution_clock clock_type;
 
	std::cerr << "size\t" << size << std::endl;
 
	std::less<value_t> lt;
	std::greater<value_t> gt;
 
	std::vector<value_t> a;
	for(int i = 0; i < size; ++i)
	{
		a.push_back((i+1) * 10);
	}
 
	switch(state)
	{
	case randomized:
		std::random_shuffle(a.begin(), a.end());
		break;
	case reversed:
		std::reverse(a.begin(), a.end());
		break;
	case sorted:
		break;
	case partially_sorted_0:
		{
		std::random_shuffle(a.begin(), a.end());
		bool toggle = true;
		for(auto i=a.begin(); i < a.end(); i+=10)
		{
			if(toggle)
				std::stable_sort(i, i+10, lt);
			else
				std::stable_sort(i, i+10, gt);
			toggle = !toggle;
		}
		}
		break;
	case partially_sorted_1:
		{
		std::random_shuffle(a.begin(), a.end());
		bool toggle = true;
		for(auto i=a.begin(); i < a.end(); i+=1000)
		{
			if(toggle)
				std::stable_sort(i, i+1000, lt);
			else
				std::stable_sort(i, i+1000, gt);
			toggle = !toggle;
		}
		}
		break;
	default:
		assert(!"not reached");
	}
 
	{
		std::vector<value_t> b(a);
 
		auto duration = time_function<clock_type>([&]
		{
			for(int i = 0; i < 100; ++i) 
			{
				std::copy(a.begin(), a.end(), b.begin());
				std::sort(b.begin(), b.end(), lt);
      }
		});
 
		std::cerr << "std::sort " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << " ms" << std::endl;
	}
 
	{
		std::vector<value_t> b(a);
 
		auto duration = time_function<clock_type>([&]
		{
			for(int i = 0; i < 100; ++i) 
			{
				std::copy(a.begin(), a.end(), b.begin());
				std::stable_sort(b.begin(), b.end(), lt);
      }
		});
 
		std::cerr << "std::stable_sort " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << " ms" << std::endl;
	}
 
	{
		std::vector<value_t> b(a);
 
		auto duration = time_function<clock_type>([&]
		{
			for(int i = 0; i < 100; ++i) 
			{
				std::copy(a.begin(), a.end(), b.begin());
				Smoothsort(b.begin(), b.end(), lt);
      }
		});
 
		std::cerr << "smoothsort " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << " ms" << std::endl;
	}
 
	{
		std::vector<value_t> b(a);
 
		auto duration = time_function<clock_type>([&]
		{
			for(int i = 0; i < 100; ++i) 
			{
				std::copy(a.begin(), a.end(), b.begin());
				timsort(b.begin(), b.end(), lt);
      }
		});
 
		std::cerr << "timsort " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << " ms" << std::endl;
	}
}
 
static void doit(int const n, state_t const state) {
    std::cerr << "int" << std::endl;
    bench<int>(n, state);
 
    std::cerr << "double" << std::endl;
    bench<double>(n, state);
}
 
int main(int argc, char* argv[])
{
 
 
	const int N = 1000*100;
 
	std::srand(static_cast<unsigned int>(std::time(NULL)));
 
	std::cout << "RANDOMIZED SEQUENCE" << std::endl;
	doit(N, randomized);
	std::cout << std::endl;
 
	std::cout << "REVERSED SEQUENCE" << std::endl;
	doit(N, reversed);
	std::cout << std::endl;
 
	std::cout << "SORTED SEQUENCE" << std::endl;
	doit(N, sorted);
	std::cout << std::endl;
 
	std::cout << "PARTIALLY SORTED SEQUENCE 0" << std::endl;
	doit(N, partially_sorted_0);
	std::cout << std::endl;
 
	std::cout << "PARTIALLY SORTED SEQUENCE 1" << std::endl;
	doit(N, partially_sorted_1);
	std::cout << std::endl;
 
	return 0;
}