#include <algorithm>   // iter_swap
#include <type_traits> // integral_constant / false_type / true_type
#include <iostream>
#include <array>
#include <chrono>

namespace Arcoth
{
    template <typename I>
	void compare_swap( I& a, I& b )
	{
		if (a > b)
			std::swap(a, b);
	}

	template <std::size_t lo, std::size_t hi, std::size_t r>
	struct oddeven_merge
	{
		template <typename Iter>
		void operator()(Iter first)
		{
			call_(first, std::integral_constant<bool, step < hi - lo>{});
		}

	private:
		static auto const step = 2*r;

		template <typename Iter>
		static void call_(Iter first, std::true_type)
		{
			oddeven_merge<lo    , hi, step>{}(first);
			oddeven_merge<lo + r, hi, step>{}(first);

			for (std::size_t i = lo + r; i <= hi - r; i += step)
				compare_swap(first[i], first[i+r]);
		}

		template <typename Iter>
		static void call_(Iter first, std::false_type)
		{
			compare_swap(first[lo], first[lo+r]);
		}
	};

	template <std::size_t lo, std::size_t hi>
	struct oddeven_merge_sort_range
	{
		static auto const mid = lo + (hi-lo)/2;

		template <typename Iter>
		void operator()( Iter first )
		{
			oddeven_merge_sort_range<lo   , mid>{}(first);
			oddeven_merge_sort_range<mid+1, hi >{}(first);
			oddeven_merge<lo, hi, 1>{}(first);
		}
	};

	template <std::size_t lo>
	struct oddeven_merge_sort_range<lo, lo>
	{
		template <typename I> void operator()(I) {}
	};

	template <std::size_t N, typename Iter>
	void oddeven_merge_sort( Iter i )
	{
		static_assert( (N & (N-1)) == 0 && N != 0, "N must be a power of 2" );
		oddeven_merge_sort_range<0, N-1>{}(i);
	}
}

namespace camper
{
	template <typename T>
	using eval = typename T::type;

	template <typename T> struct identity
	{
	   using type = T;
	};

	template <int... i> struct index_list : identity<index_list<i...>> {};

	template <int N, int... i> struct make_index_list : make_index_list<N-1, 0, 1+i...> {};

	template <int... i> struct make_index_list<0, i...> : index_list<i...> {};

	template <typename... T> struct type_list : identity<type_list<T...>> {};

	template <typename... T> struct merge_type_lists : merge_type_lists<eval<T>...> {};
	template <typename... T, typename... U, typename... V>
	struct merge_type_lists<type_list<T...>, type_list<U...>, V...>
	    : merge_type_lists<type_list<T..., U...>, V...>
	{};
	template <typename... T>
	struct merge_type_lists<type_list<T...>>
	    : type_list<T...>
	{};

	/////////////////////////////////////
	template <int a, int b>
	struct index_pair
	{
	    static constexpr auto first = a;
	    static constexpr auto second = b;
	};

	template <int lo, int hi, int r, typename = eval<make_index_list<(hi-lo)/(2*r)>>>
	struct oddeven_merge_simple_range;
	template <int lo, int hi, int r, int... I>
	struct oddeven_merge_simple_range<lo, hi, r, index_list<I...>>
	    : type_list<index_pair<lo+r+I*2*r,lo+2*r+I*2*r>...>
	{};

	template <int lo, int hi, int r>
	struct oddeven_merge
	    : std::conditional<(r*2<hi-lo),
				     merge_type_lists<oddeven_merge<lo, hi, 2*r>, oddeven_merge<lo+r, hi, 2*r>, oddeven_merge_simple_range<lo, hi, r>>,
				     type_list<index_pair<lo, lo+r>>>::type
	{};

	template <int lo, int hi>
	struct oddeven_merge_sort_range
	    : std::conditional<(lo<hi),
				     merge_type_lists<oddeven_merge_sort_range<lo, lo+(hi-lo)/2>, oddeven_merge_sort_range<lo+(hi-lo)/2+1,hi>, oddeven_merge<lo,hi,1>>,
				     type_list<>>::type
	{};

	struct evaluator
	{
		template <typename... Args>
		evaluator(Args&&...) {}
	};
	void evaluate(evaluator) {}

	template <typename RandomIter, typename... Pairs>
	void oddeven_merge_sort_loop(RandomIter begin, type_list<Pairs...>)
	{
		static constexpr unsigned char pairs[][2] = { { Pairs::first, Pairs::second }... };
		for ( int i = 0; i != sizeof...(Pairs); ++i )
			Arcoth::compare_swap( begin[pairs[i][0]], begin[pairs[i][1]] );
	}

	template <std::size_t N, typename RandomIter>
	void oddeven_merge_sort_loop(RandomIter begin)
	{
	    static_assert( N>0 && (N&(N-1))==0, "N must be power of 2" );
	    oddeven_merge_sort_loop(begin, eval<oddeven_merge_sort_range<0, N-1>>{});
	}
	template <typename RandomIter, typename... Pairs>
	void oddeven_merge_sort_unrolled(RandomIter begin, type_list<Pairs...>)
	{
bool x[]={ ((begin[Pairs::second]<begin[Pairs::first]?std::swap(begin[Pairs::second],begin[Pairs::first]):void()),false)... };(void)x;
	}

	template <std::size_t N, typename RandomIter>
	void oddeven_merge_sort_unrolled(RandomIter begin)
	{
	    static_assert( N>0 && (N&(N-1))==0, "N must be power of 2" );
	    oddeven_merge_sort_unrolled(begin, eval<oddeven_merge_sort_range<0, N-1>>{});
	}


}

namespace SeppJ
{
	template <std::size_t N, std::size_t step_size, std::size_t step = 1> struct odd_even_compare
	{
	  template<typename Iterator> void operator() (Iterator it)
	  {
	    if (it[step * step_size + step_size] < it[step * step_size])
		std::swap(it[step * step_size + step_size], it[step * step_size]);
	    odd_even_compare<N, step_size, step + 1>()(it);
	  }
	};

	template <std::size_t N, std::size_t step_size> struct odd_even_compare<N, step_size, N>
	{
	  template<typename Iterator> void operator() (Iterator it)
	  {
	    if (it[N * step_size + step_size] < it[N * step_size])
		std::swap(it[N * step_size + step_size], it[N * step_size]);
	  }
	};


	template <std::size_t N, std::size_t step_size = 1> struct odd_even_merge
	{
	  template<typename Iterator> void operator()(Iterator it)
	  {
	    odd_even_merge<N/2, 2 * step_size>()(it);
	    odd_even_merge<N/2, 2 * step_size>()(it + step_size);
	    odd_even_compare<N - 3, step_size>()(it);
	  }
	};

	template<std::size_t step_size> struct odd_even_merge<2, step_size>
	{
	  template<typename Iterator> void operator()(Iterator it)
	  {
	    if (it[step_size] < it[0])
		std::swap(it[0], it[step_size]);
	  }
	};

	template <std::size_t N> struct odd_even_mergesort
	{
	  template<typename Iterator> void operator()(Iterator it)
	  {
	    odd_even_mergesort<N/2>()(it);
	    odd_even_mergesort<N/2>()(it + N/2);
	    odd_even_merge<N>()(it);
	  }
	};

	template<> struct odd_even_mergesort<1>
	{template<typename Iterator> void operator() (Iterator) {}};

	template<> struct odd_even_mergesort<0>
	{template<typename Iterator> void operator() (Iterator) {}};
}

#include <array>
#include <random>
#include <memory>

template <std::size_t N,
          std::size_t outer_loopings,
          std::size_t inner_loopings,
          typename T>
void test(T obj, int64_t const* values)
{
	using namespace std::chrono;
	auto best = high_resolution_clock::duration::max();

	uint64_t check;

	uint64_t array[N*inner_loopings];

	for (unsigned j = 0; j != outer_loopings; ++j )
	{
		check = 0;

		std::copy_n( values, N*inner_loopings, array );

		auto start = high_resolution_clock::now();

			for (unsigned i = 0; i != inner_loopings; ++i)
			{
				obj( array + N*i );

				for (std::size_t c = 0; c != N; c += 2)
					(check += array[c]) *= array[c+1];
			}

		auto end = high_resolution_clock::now();

		if (end - start < best)
		{
			best = end - start;
			j = 0;
		}
	}

	std::cout << best.count() << ' ' << std::hex << check << std::dec << '\n';
}

auto constexpr size = 64, inner = 2048, outer = 5000;

int main()
{
	std::uniform_int_distribution<int64_t> dis(std::numeric_limits<int64_t>::min());
	std::mt19937_64 engine( std::random_device{}() );

	int64_t values[size*inner];

	std::generate( std::begin(values), std::end(values), [&] {return dis(engine);} );

	std::cout << "SeppJ:            "; test<size, outer, inner>( SeppJ::odd_even_mergesort<size>{}                       , values );
	std::cout << "camper(Loop):     "; test<size, outer, inner>( [] (auto iter) {camper::oddeven_merge_sort_loop<size>(iter);}, values );
	std::cout << "camper(unrolled): "; test<size, outer, inner>( [] (auto iter) {camper::oddeven_merge_sort_unrolled<size>(iter);}, values );
	std::cout << "Arcoth:           "; test<size, outer, inner>( [] (auto iter) {Arcoth::oddeven_merge_sort<size>(iter);}, values );
}
