/*
								+-----------------------------------+
								|                                   |
								|***Compile-time sorting networks***|
								|                                   |
								|   Copyright  © -tHE SWINe- 2014   |
								|                                   |
								|           StaticSort.h            |
								|                                   |
								+-----------------------------------+
*/

#pragma once
#ifndef __STATIC_BITONIC_SORT_INCLUDED
#define __STATIC_BITONIC_SORT_INCLUDED

/**
 *	@file StaticSort.h
 *	@date 2014
 *	@author -tHE SWINe-
 *	@brief compiler-generated bitonic sorting networs
 *
 *	@todo rest this in msvc 60
 */

#include "Typelist.h"
#include "Integer.h"
#include <utility>

/**
 *	@brief internal implementation of the static sort
 */
namespace static_sort {

/**
 *	@brief sorter assertion container template
 *	@tparam b_value is asserted value (must be a compile-time constant)
 */
template <const bool b_value>
struct CSorterAssert {};

/**
 *	@brief sorter assertion container template (specialization for assertion passed)
 */
template <>
struct CSorterAssert<true> {
	typedef void POWER_OF_TWO_IS_NOT_LOWER; /**< @brief assertion tag for power of two calculation */
	typedef void NOT_POWER_OF_TWO; /**< @brief assertion tag for power of two calculation */
	typedef void NOT_THE_FIRST_SMALLER_POWER_OF_TWO; /**< @brief assertion tag for power of two calculation */
	typedef void NONPOSITIVE_NUMBER_OF_CSWAPS; /**< @brief assertion tag for cswap list generation */
};

/**
 *	@brief compare-swap index pair
 *
 *	@tparam _n_index_a is index of the lower element
 *	@tparam _n_index_b is index of the higher element
 */
template <size_t _n_index_a, size_t _n_index_b>
struct TCSwapPair {
	enum {
		n_index_a = _n_index_a, /**< @brief index of the lower element */
		n_index_b = _n_index_b /**< @brief index of the higher element */
	};
};

template <const size_t _n_first, const int _n_length, const bool _b_swap_direction>
class CBitonicMerge_Generator {
protected:
	enum {
		n_first = _n_first,
		n_length = _n_length,
		b_swap_direction = _b_swap_direction,

		n_half = n_Make_Lower_POT_Static(n_length - 1), // strictly lower power of two (not equal)
		n_last = n_first + n_length - n_half,
		n_middle = n_first + n_half,
		n_rest = n_length - n_half,
		n_local_cswap_num = n_last - n_first
	};

public:
	template <size_t n_last_minus_index MSVC_OMMIT_ARG(class GppDummy)>
	class CMakeCSwapList {
	public:
		typedef TCSwapPair<n_last - n_last_minus_index + ((b_swap_direction)? n_half : 0),
			n_last - n_last_minus_index + ((b_swap_direction)? 0 : n_half)> CCSwap;
		typedef typename CMakeCSwapList<n_last_minus_index - 1 MSVC_OMMIT_ARG(GppDummy)>::CResult CTail;
		typedef CTypelist<CCSwap, CTail> CResult;
	};

	template <MSVC_OMMIT(class GppDummy)> // hack - avoid full specialization in non-namespace scope
	class CMakeCSwapList<0 MSVC_OMMIT_ARG(GppDummy)> {
	public:
		typedef CTypelistEnd CResult;
	};

protected:
	enum {
		b_POT_less_than_length = n_half < n_length,
		b_POT_is_POT = b_Is_POT_Static(n_half),
		b_POT_is_the_first_smaller_POT = size_t(n_half) * 2 >= n_length,
	};

	typedef typename CSorterAssert<b_POT_less_than_length>::POWER_OF_TWO_IS_NOT_LOWER CAssert0; // make sure that the power of two is always less than the length
	typedef typename CSorterAssert<b_POT_is_POT>::NOT_POWER_OF_TWO CAssert1; // make sure the power of two is in fact a power of two
	typedef typename CSorterAssert<b_POT_is_the_first_smaller_POT>::NOT_THE_FIRST_SMALLER_POWER_OF_TWO CAssert2; // make sure it is the first smaller power of two

	template <const size_t n_recurse_len MSVC_OMMIT_ARG(class GppDummy)>
	struct CRecurseLeft {
		typedef typename CBitonicMerge_Generator<n_first,
			n_recurse_len, b_swap_direction>::CResult CResult;
	};

	template <MSVC_OMMIT(class GppDummy)> // hack - avoid full specialization in non-namespace scope
	struct CRecurseLeft<0 MSVC_OMMIT_ARG(GppDummy)> {
		typedef CTypelistEnd CResult;
	};

	template <const size_t n_recurse_len MSVC_OMMIT_ARG(class GppDummy)>
	struct CRecurseRight {
		typedef typename CBitonicMerge_Generator<n_middle,
			n_recurse_len, b_swap_direction>::CResult CResult;
	};

	template <MSVC_OMMIT(class GppDummy)> // hack - avoid full specialization in non-namespace scope
	struct CRecurseRight<0 MSVC_OMMIT_ARG(GppDummy)> {
		typedef CTypelistEnd CResult;
	};

	typedef typename CRecurseLeft<(n_half - (n_half == 1)) MSVC_OMMIT_ARG(void)>::CResult CLeftRecurse;
	typedef typename CRecurseRight<(n_rest - (n_rest == 1)) MSVC_OMMIT_ARG(void)>::CResult CRightRecurse;
	// avoid sorting 1-element sequences (shorter than making another specialization for 1 element)

	typedef typename CMakeCSwapList<n_local_cswap_num MSVC_OMMIT_ARG(void)>::CResult CLocalCompareList;
	typedef typename CConcatTypelist<CLeftRecurse, CRightRecurse>::CResult CRecurse;

public:
	typedef typename CConcatTypelist<CLocalCompareList, CRecurse>::CResult CResult;
};

template <const size_t _n_first, const size_t _n_length, const bool _b_swap_direction>
class CBitonicSort_Generator {
protected:
	enum {
		n_first = _n_first,
		b_swap_direction = _b_swap_direction,
		n_length = _n_length,
		n_half = n_length / 2,
		n_middle = n_first + n_half,
		n_rest = n_length - n_half
	};

	template <const size_t n_recurse_len MSVC_OMMIT_ARG(class GppDummy)>
	struct CRecurseLeft {
		typedef typename CBitonicSort_Generator<n_first,
			n_recurse_len, !b_swap_direction>::CResult CResult;
	};

	template <MSVC_OMMIT(class GppDummy)>
	struct CRecurseLeft<0 MSVC_OMMIT_ARG(GppDummy)> {
		typedef CTypelistEnd CResult;
	};

	template <const size_t n_recurse_len MSVC_OMMIT_ARG(class GppDummy)>
	struct CRecurseRight {
		typedef typename CBitonicSort_Generator<n_middle,
			n_recurse_len, b_swap_direction>::CResult CResult;
	};

	template <MSVC_OMMIT(class GppDummy)> // hack - avoid full specialization in non-namespace scope
	struct CRecurseRight<0 MSVC_OMMIT_ARG(GppDummy)> {
		typedef CTypelistEnd CResult;
	};

	template <const size_t n_recurse_len MSVC_OMMIT_ARG(class GppDummy)>
	struct CCallMerge {
		typedef typename CBitonicMerge_Generator<n_first,
			n_recurse_len, b_swap_direction>::CResult CResult;
	};

	template <MSVC_OMMIT(class GppDummy)> // hack - avoid full specialization in non-namespace scope
	struct CCallMerge<0 MSVC_OMMIT_ARG(GppDummy)> {
		typedef CTypelistEnd CResult;
	};

	typedef typename CRecurseLeft<(n_half - (n_half == 1)) MSVC_OMMIT_ARG(void)>::CResult CLeftRecurse;
	typedef typename CRecurseRight<(n_rest - (n_rest == 1)) MSVC_OMMIT_ARG(void)>::CResult CRightRecurse;
	typedef typename CCallMerge<(n_length - (n_length == 1)) MSVC_OMMIT_ARG(void)>::CResult CMergePass;
	// avoid sorting 1-element sequences (shorter than making another specialization for 1 element)

	typedef typename CConcatTypelist<CLeftRecurse, CRightRecurse>::CResult CRecurse;

public:
	typedef typename CConcatTypelist<CRecurse, CMergePass>::CResult CResult;
};

/**
 *	@brief sorting network generator
 *	@tparam n_seq_length is length of the sorted sequence
 */
template <const size_t n_seq_length>
class CSortingNetwork_Generator {
public:
	typedef typename CBitonicSort_Generator<0, n_seq_length, false>::CResult CSortingSequence;
};

template <class CRanIt>
struct CCSwap_Wrapper {
	template <class CCSwapInfo>
	struct CLoop {
		enum {
			a = CCSwapInfo::n_index_a,
			b = CCSwapInfo::n_index_b
		};

		static inline void Run(CRanIt p_data)
		{
			if(p_data[a] > p_data[b])
				std::swap(p_data[a], p_data[b]);
		}
	};
};

template <class CRanIt, class CPred>
struct CCSwap_Wrapper_Pred {
	template <class CCSwapInfo>
	struct CLoop {
		enum {
			a = CCSwapInfo::n_index_a,
			b = CCSwapInfo::n_index_b
		};

		static inline void Run(std::pair<CRanIt, CPred> p_data_pred)
		{
			CRanIt &p_data = p_data_pred.first;
			if(p_data_pred.second(p_data[b], p_data[a])) // if ordering is *violated*
				std::swap(p_data[a], p_data[b]);
		}
	};
};

} // ~static_sort

/**
 *	@brief compile-time fixed-size bitonic sort
 *	@tparam _n_length is length of the sorted sequence
 */
template <size_t _n_length>
class CBitonicSort_Static {
public:
	typedef typename static_sort::CSortingNetwork_Generator<_n_length>::CSortingSequence
		CSortingSequence; /**< @brief generated sorting sequence */

	/**
	 *	@brief parameters, stored as enum
	 */
	enum {
		n_length = _n_length, /**< @brief length of the sorted sequence */
		n_cswap_num = CTypelistLength<CSortingSequence>::n_result /**< @brief number of compare-swap operations of this sort */
	};

public:
	/**
	 *	@brief constructor; sorts the data
	 *
	 *	@tparam CRanIt is random-access iterator type
	 *
	 *	@param[in] p_begin_it is iterator, pointing to the first element of the sorted sequence
	 *	@param[in] p_end_it is iterator, pointing one past the last element of the sorted sequence
	 */
	template <class CRanIt>
	inline CBitonicSort_Static(CRanIt p_begin_it, CRanIt UNUSED(p_end_it))
	{
		_ASSERTE(p_end_it >= p_begin_it);
		_ASSERTE(p_end_it - p_begin_it == n_length);
		CTypelistForEach<CSortingSequence, static_sort::CCSwap_Wrapper<CRanIt> >::Run(p_begin_it);
	}

	/**
	 *	@brief constructor; sorts the data using a predicate
	 *
	 *	@tparam CRanIt is random-access iterator type
	 *	@tparam CPred is less-than predicate
	 *
	 *	@param[in] p_begin_it is iterator, pointing to the first element of the sorted sequence
	 *	@param[in] p_end_it is iterator, pointing one past the last element of the sorted sequence
	 *	@param[in] pred is instance of a less-than predicate
	 */
	template <class CRanIt, class CPred>
	inline CBitonicSort_Static(CRanIt p_begin_it, CRanIt UNUSED(p_end_it), CPred pred)
	{
		_ASSERTE(p_end_it >= p_begin_it);
		_ASSERTE(p_end_it - p_begin_it == n_length);
		CTypelistForEach<CSortingSequence, static_sort::CCSwap_Wrapper_Pred<CRanIt,
			CPred> >::Run(std::pair<CRanIt, CPred>(p_begin_it, pred));
	}
};

/**
 *	@brief compile-time fixed-size bitonic sort
 *	@tparam _n_length is length of the sorted sequence
 */
template <size_t _n_element_index, size_t _n_length>
class CNthElement_Static {
public:
	typedef typename static_sort::CSortingNetwork_Generator<_n_length>::CSortingSequence
		CFullSortingSequence; /**< @brief generated sorting sequence */

protected:
	/**
	 *	@brief intermediates, stored as enum
	 */
	enum {
		b_valid_index = _n_element_index < _n_length /**< @brief index check */
	};

	typedef typename CTypelistAssert<b_valid_index>::INDEX_OUT_OF_BOUNDS CAssert0; /**< @brief make sure that n_element_index is inside the array bounds */

	/**
	 *	@brief compares indices, stored in a specialization of static_sort::TCSwapPair to a reference
	 *
	 *	@tparam n_element_of_interest is the first reference index
	 *	@tparam n_element_of_interest1 is the second reference index
	 */
	template <size_t n_element_of_interest, size_t n_element_of_interest1>
	struct CResultCSwapPred_Wrapper {
		template <class CCSwapInfo>
		struct CPredicate {
			/**
			 *	@brief results, stored as enum
			 */
			enum {
				n_index_a = CCSwapInfo::n_index_a,
				n_index_b = CCSwapInfo::n_index_b,
				b_result = n_index_a == n_element_of_interest || n_index_b == n_element_of_interest ||
					n_index_a == n_element_of_interest1 || n_index_b == n_element_of_interest1 /**< @brief result; set if eigher of the indices matches either of the arguments */
			};
		};
	};

	template <class CList MSVC_OMMIT_ARG(class GppDummy)>
	class CDiscardUnusedWrites {
	protected:
		typedef typename CList::CHead CHead;
		typedef typename CList::CTail CTail;

		typedef typename CDiscardUnusedWrites<CTail
			MSVC_OMMIT_ARG(GppDummy)>::CResult CProcessedTail; // process the tail first

		typedef CResultCSwapPred_Wrapper<CHead::n_index_a, CHead::n_index_b> CPredicate;
		//typedef typename CFilterTypelist<CProcessedTail,
		//	CPredicate>::CResult CDependentCSwaps; // find cswaps that read n_index_a or n_index_b in the future
		// todo - write a CTypelistFindIf to filter only the first match and then finish

		enum {
			b_has_dependencies = CHead::n_index_a == _n_element_index ||
				CHead::n_index_b == _n_element_index || !CIsSameType<typename
				CFilterTypelist<CProcessedTail, CPredicate>::CResult, CTypelistEnd>::b_result
			// look for future dependences, preserve the last cswap
			// note that calculating CDependentCSwaps inside may actually
			// be omitted by the operator ||, leading to faster compile times
		};

	public:
		typedef typename CChooseType<CTypelist<CHead, CProcessedTail>, CProcessedTail,
			b_has_dependencies>::CResult CResult; /**< @brief filtered list */
		// keep the head only if there are references
	};

	template <MSVC_OMMIT(class GppDummy)>
	class CDiscardUnusedWrites<CTypelistEnd MSVC_OMMIT_ARG(GppDummy)> {
	public:
		typedef CTypelistEnd CResult; /**< @brief filtered list */
	};

public:
	typedef typename CDiscardUnusedWrites<CFullSortingSequence
		MSVC_OMMIT_ARG(void)>::CResult CPartialSortingSequence; /**< @brief partial sorting sequence */

	/**
	 *	@brief parameters, stored as enum
	 */
	enum {
		n_length = _n_length, /**< @brief length of the sorted sequence */
		n_element_index = _n_element_index, /**< @brief index of the sorted element */
		n_cswap_num = CTypelistLength<CPartialSortingSequence>::n_result /**< @brief number of compare-swap operations of this sort */
	};

public:
	/**
	 *	@brief constructor; partially sorts the data
	 *
	 *	After the function returns, the element at position n_element_index
	 *	has the value of the same element in a completely sorted sequence.
	 *	The rest of the sequence is rearranged, but not (fully) sorted.
	 *
	 *	@tparam CRanIt is random-access iterator type
	 *
	 *	@param[in] p_begin_it is iterator, pointing to the first element of the sorted sequence
	 *	@param[in] p_end_it is iterator, pointing one past the last element of the sorted sequence
	 */
	template <class CRanIt>
	inline CNthElement_Static(CRanIt p_begin_it, CRanIt p_end_it)
	{
		_ASSERTE(p_end_it >= p_begin_it);
		_ASSERTE(p_end_it - p_begin_it == n_length);
		CTypelistForEach<CPartialSortingSequence, static_sort::CCSwap_Wrapper<CRanIt> >::Run(p_begin_it);
	}

	/**
	 *	@brief constructor; partially sorts the data using a predicate
	 *
	 *	After the function returns, the element at position n_element_index
	 *	has the value of the same element in a completely sorted sequence.
	 *	The rest of the sequence is rearranged, but not (fully) sorted.
	 *
	 *	@tparam CRanIt is random-access iterator type
	 *	@tparam CPred is less-than predicate
	 *
	 *	@param[in] p_begin_it is iterator, pointing to the first element of the sorted sequence
	 *	@param[in] p_end_it is iterator, pointing one past the last element of the sorted sequence
	 *	@param[in] pred is instance of a less-than predicate
	 */
	template <class CRanIt, class CPred>
	inline CNthElement_Static(CRanIt p_begin_it, CRanIt p_end_it, CPred pred)
	{
		_ASSERTE(p_end_it >= p_begin_it);
		_ASSERTE(p_end_it - p_begin_it == n_length);
		CTypelistForEach<CPartialSortingSequence, static_sort::CCSwap_Wrapper_Pred<CRanIt,
			CPred> >::Run(std::pair<CRanIt, CPred>(p_begin_it, pred));
	}
};

#ifdef __STATIC_BITONIC_SORT_COMPILE_UNIT_TESTS

/**
 *	@brief a simple test of correctness, as well as performance comparison with
 *		std::sort, std::stable_sort and std::nth_element
 */
class CStaticSort_UnitTest {
public:
	struct TProfiledInt {
		static size_t n_swap_num;
		static size_t n_move_num;

		int i;

		TProfiledInt(int _i = int())
			:i(_i)
		{
			++ n_move_num;
		}

		TProfiledInt(const TProfiledInt &_i)
			:i(_i.i)
		{
			++ n_move_num;
		}

		operator int() const
		{
			return i;
		}

		TProfiledInt &operator =(int _i)
		{
			++ n_move_num;
			i = _i;
			return *this;
		}

		TProfiledInt &operator =(const TProfiledInt &_i)
		{
			++ n_move_num;
			i = _i,i;
			return *this;
		}

		void Swap(TProfiledInt &_i)
		{
			++ n_swap_num;
			std::swap(i, _i.i);
		}
	};

	class CLessThanWithCounting {
	protected:
		size_t &m_r_n_compare_num;

	public:
		CLessThanWithCounting(size_t &r_n_compare_num)
			:m_r_n_compare_num(r_n_compare_num)
		{}

		bool operator ()(const TProfiledInt &l, const TProfiledInt &r) const
		{
			++ m_r_n_compare_num;
			return int(l) < int(r);
		}
	};

public:
	/**
	 *	@brief main test function
	 *
	 *	@param[in] length is instance of CCTSize for length (unused)
	 *	@param[in] element_of_interest is instance of CCTSize for element
	 *		of interesr (unused)
	 *
	 *	@note This function throws std::runtime_error (if the test failed).
	 *	@note The reason for the argument is that msvc¨60 has a problem with
	 *		explicit template member functions.
	 */
	template <class CLen, class CElem>
	static void Run(CLen UNUSED(length),
		CElem UNUSED(element_of_interest), bool b_verbose = true) // throw(std::runtime_error)
	{
		enum {
			n_seq_length = CLen::n_size,
			n_elem_of_interest = CElem::n_size
		};

		srand(int(time(0)));

		int seq[n_seq_length];
		for(int i = 0; i < n_seq_length; ++ i)
			seq[i] = i;
		for(int i = 0; i < n_seq_length; ++ i)
			std::swap(seq[i], seq[i + size_t(rand() / float(RAND_MAX) * (n_seq_length - i))]);
		if(b_verbose) {
			printf("the original sequence:\n");
			for(int i = 0; i < n_seq_length; ++ i)
				printf((i == n_elem_of_interest)? ">%d< " : "%d ", seq[i]);
			printf("\n\n");
		}
		{
			TProfiledInt p_data[n_seq_length];
			std::copy(seq, seq + n_seq_length, p_data);
			typedef CBitonicSort_Static<n_seq_length> CMySorter;
			size_t n_compare_num = 0;
			TProfiledInt::n_swap_num = 0;
			TProfiledInt::n_move_num = 0;
			CMySorter(p_data, p_data + CMySorter::n_length, CLessThanWithCounting(n_compare_num));

			if(b_verbose) {
				printf("CBitonicSort_Static: number of compares: %d (%d performed), swaps: %d, moves: %d\n",
					CMySorter::n_cswap_num, n_compare_num, TProfiledInt::n_swap_num, TProfiledInt::n_move_num);
				for(int i = 0; i < CMySorter::n_length; ++ i)
					printf((i == n_elem_of_interest)? ">%d< " : "%d ", int(p_data[i]));
				printf("\n\n");
			}
			for(int i = 0; i < n_seq_length; ++ i)
				Assert(p_data[i] == i); // make sure it is sorted
		}
		if(b_verbose) {
			TProfiledInt p_data[n_seq_length];
			std::copy(seq, seq + n_seq_length, p_data);
			size_t n_compare_num = 0;
			TProfiledInt::n_swap_num = 0;
			TProfiledInt::n_move_num = 0;
			std::sort(p_data, p_data + n_seq_length, CLessThanWithCounting(n_compare_num));
			printf("std::sort: number of compares: %d, swaps: %d, moves: %d\n",
				n_compare_num, TProfiledInt::n_swap_num, TProfiledInt::n_move_num);
			for(int i = 0; i < n_seq_length; ++ i)
				printf((i == n_elem_of_interest)? ">%d< " : "%d ", int(p_data[i]));
			printf("\n\n");
		}
		if(b_verbose) {
			TProfiledInt p_data[n_seq_length];
			std::copy(seq, seq + n_seq_length, p_data);
			size_t n_compare_num = 0;
			TProfiledInt::n_swap_num = 0;
			TProfiledInt::n_move_num = 0;
			std::stable_sort(p_data, p_data + n_seq_length, CLessThanWithCounting(n_compare_num));
			printf("std::stable_sort: number of compares: %d, swaps: %d, moves: %d\n",
				n_compare_num, TProfiledInt::n_swap_num, TProfiledInt::n_move_num);
			for(int i = 0; i < n_seq_length; ++ i)
				printf((i == n_elem_of_interest)? ">%d< " : "%d ", int(p_data[i]));
			printf("\n\n");
		}
		{
			TProfiledInt p_data[n_seq_length];
			std::copy(seq, seq + n_seq_length, p_data);
			typedef CNthElement_Static<n_elem_of_interest, n_seq_length> CMySorter;
			size_t n_compare_num = 0;
			TProfiledInt::n_swap_num = 0;
			TProfiledInt::n_move_num = 0;
			CMySorter(p_data, p_data + CMySorter::n_length, CLessThanWithCounting(n_compare_num));

			if(b_verbose) {
				printf("CNthElement_Static: number of compares: %d (%d performed), swaps: %d, moves: %d\n",
					CMySorter::n_cswap_num, n_compare_num, TProfiledInt::n_swap_num, TProfiledInt::n_move_num);
				for(int i = 0; i < CMySorter::n_length; ++ i)
					printf((i == CMySorter::n_element_index)? ">%d< " : "%d ", int(p_data[i]));
				printf("\n\n");
			}
			Assert(p_data[n_elem_of_interest] == n_elem_of_interest); // make sure that element is sorted
		}
		if(b_verbose) {
			TProfiledInt p_data[n_seq_length];
			std::copy(seq, seq + n_seq_length, p_data);
			size_t n_compare_num = 0;
			TProfiledInt::n_swap_num = 0;
			TProfiledInt::n_move_num = 0;
			std::nth_element(p_data, p_data + n_elem_of_interest,
				p_data + n_seq_length, CLessThanWithCounting(n_compare_num));
			printf("std::nth_element_ number of compares: %d, swaps: %d, moves: %d\n",
				n_compare_num, TProfiledInt::n_swap_num, TProfiledInt::n_move_num);
			for(int i = 0; i < n_seq_length; ++ i)
				printf((i == n_elem_of_interest)? ">%d< " : "%d ", int(p_data[i]));
			printf("\n\n");
		}
	}

protected:
	static void Assert(bool b_expression) // throw(std::runtime_error)
	{
		if(!b_expression)
			throw std::runtime_error("error: unit tests failed");
	}
};

size_t CStaticSort_UnitTest::TProfiledInt::n_swap_num = 0;
size_t CStaticSort_UnitTest::TProfiledInt::n_move_num = 0;

namespace std {

void swap(CStaticSort_UnitTest::TProfiledInt &a, CStaticSort_UnitTest::TProfiledInt &b)
{
	a.Swap(b);
}

};

#endif // __STATIC_BITONIC_SORT_COMPILE_UNIT_TESTS

#endif // !__STATIC_BITONIC_SORT_INCLUDED
