/* vim: set tabstop=4 : */
/********************************************************************
	@file set_op.hpp
	@brief set operations

	@date	2006-10-23 14:02
	@author	leipeng
	@{
*********************************************************************/
#pragma once

// #include <boost/preprocessor/iteration/local.hpp>
// #include <boost/preprocessor/enum.hpp>
// #include <boost/preprocessor/enum_params.hpp>
// #include <boost/mpl/at.hpp>
// #include <boost/mpl/map.hpp>
//

#include <assert.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <functional>
#include <stdexcept>
#include <boost/tuple/tuple.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/if.hpp>
#include <boost/multi_index/identity.hpp>
#include "stdtypes.hpp"
#include "util/compare.hpp"

namespace terark {
//@{
/**
 @brief multi set intersection -- result is copy from [_First1, _Last1)

 [dest, result) is [_First1, _Last1) AND [_First2, _Last2)

 predictions:
   for any [x1, x2) in [_First1, _Last1), _Pred(x2, x1) = false,
   that is say: x1 <= x2
   allow same key in sequence
 @note
  - result is copy from [_First1, _Last1)
  - values in sequence can be equal, when values are equal, multi values will be copied
  - _InIt1::value_type, _InIt2::value_type, _OutIt::value_type need not be same
  - _InIt1::value_type and _InIt2::value_type must comparable by _Pr
  - _InIt1::value_type must assignable to _OutIt::value_type
  - _InIt2::value_type need not assignable to _OutIt::value_type
 */
template<class _InIt1, class _InIt2, class _OutIt, class _Pr>
inline
_OutIt multiset_intersection(_InIt1 _First1, _InIt1 _Last1,
							 _InIt2 _First2, _InIt2 _Last2, _OutIt dest, _Pr _Pred)
{
	for (; _First1 != _Last1 && _First2 != _Last2; )
	{
		if (_Pred(*_First1, *_First2))
			++_First1;
		else if (_Pred(*_First2, *_First1))
			++_First2;
		else
			*dest++ = *_First1++; // do not increment _First2
	}
	return (dest);
}

template<class _InIt1, class _RandInIt2, class _OutIt, class _Pr>
inline
_OutIt multiset_1small_intersection(_InIt1 _First1, _InIt1 _Last1,
									_RandInIt2 _First2, _RandInIt2 _Last2, _OutIt dest, _Pr _Pred)
{
	for (; _First1 != _Last1 && _First2 != _Last2; )
	{
		std::pair<_RandInIt2, _RandInIt2> range = std::equal_range(_First2, _Last2, *_First1, _Pred);
		if (range.first == range.second)
			++_First1;
		else {
			// !(*range.first < *_First1) --> *_First1 <= *range.first
			while (_First1 != _Last1 && !_Pred(*range.first, *_First1))
				*dest++ = *_First1++;
		}
		_First2 = range.second;
	}
	return (dest);
}

template<class _InIt1, class _RandInIt2, class _OutIt, class _Pr>
inline
_OutIt multiset_fast_intersection(_InIt1 _First1, _InIt1 _Last1,
								  _RandInIt2 _First2, _RandInIt2 _Last2, _OutIt dest, _Pr _Pred, ptrdiff_t threshold = 32)
{
	if (std::distance(_First1, _Last1) * threshold < std::distance(_First2, _Last2))
		return multiset_1small_intersection(_First1, _Last1, _First2, _Last2, dest, _Pred);
	else
		return multiset_intersection(_First1, _Last1, _First2, _Last2, dest, _Pred);
}
//@}

//////////////////////////////////////////////////////////////////////////

//@{
/**
 @brief multi set intersection2 -- result is copy from [_First2, _Last2)

 [dest, result) is [_First1, _Last1) AND [_First2, _Last2)
 for any [x1, x2) in [_First1, _Last1), _Pred(x2, x1) = false
 allow same key in sequence

 @note
  - result is copy from [_First2, _Last2)
  - values in sequence can be equal, when values are equal, multi values will be copied
  - _InIt1::value_type, _InIt2::value_type, _OutIt::value_type need not be same
  - _InIt1::value_type and _InIt2::value_type must comparable by _Pr
  - _InIt2::value_type must assignable to _OutIt::value_type
  - _InIt1::value_type need not assignable to _OutIt::value_type
 */
template<class _InIt1, class _InIt2, class _OutIt, class _Pr>
inline
_OutIt multiset_intersection2(_InIt1 _First1, _InIt1 _Last1,
							  _InIt2 _First2, _InIt2 _Last2, _OutIt dest, _Pr _Pred)
{
	for (; _First1 != _Last1 && _First2 != _Last2; )
	{
		if (_Pred(*_First1, *_First2))
			++_First1;
		else if (_Pred(*_First2, *_First1))
			++_First2;
		else
			*dest++ = *_First2++; // do not increment _First1
	}
	return (dest);
}

template<class _InIt1, class _RandInIt2, class _OutIt, class _Pr>
inline
_OutIt multiset_1small_intersection2(_InIt1 _First1, _InIt1 _Last1,
									 _RandInIt2 _First2, _RandInIt2 _Last2, _OutIt dest, _Pr _Pred)
{
	for (; _First1 != _Last1 && _First2 != _Last2; )
	{
		std::pair<_RandInIt2, _RandInIt2> range = std::equal_range(_First2, _Last2, *_First1, _Pred);
		if (range.first != range.second)
		{
			dest = std::copy(range.first, range.second, dest);
		}
		_First2 = range.second;
		++_First1;
	}
	return (dest);
}

template<class _RandInIt1, class _RandInIt2, class _OutIt, class _Pr>
inline
_OutIt multiset_fast_intersection2(_RandInIt1 _First1, _RandInIt1 _Last1,
								   _RandInIt2 _First2, _RandInIt2 _Last2, _OutIt dest, _Pr _Pred, ptrdiff_t threshold = 32)
{
	if (std::distance(_First1, _Last1) * threshold < std::distance(_First2, _Last2))
		return multiset_1small_intersection2(_First1, _Last1, _First2, _Last2, dest, _Pred);
	else
		return multiset_intersection2(_First1, _Last1, _First2, _Last2, dest, _Pred);
}
//@}

//! 一定是调用 pred(prev, curr), 而不是 pred(curr, prev)
//!
//! 可以保证对有序序列 pred(prev, curr) 等效于 !equal_to(prev, curr)
//! 从而可以对有序序列使用 set_unique(first, last, terark::not2(less_than_comp))
//! 来删除重复元素
//!
template<class _FwdIt, class _Pr> inline
_FwdIt set_unique(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
{	// remove each matching previous
	for (_FwdIt _Firstb; (_Firstb = _First) != _Last && ++_First != _Last; )
		if (_Pred(*_Firstb, *_First))
			{	// copy down
			for (; ++_First != _Last; )
				if (!_Pred(*_Firstb, *_First))
					*++_Firstb = *_First;
			return (++_Firstb);
			}
	return (_Last);
}

template<class _FwdIt> inline
	_FwdIt set_unique(_FwdIt _First, _FwdIt _Last)
{
	return set_unique(_First, _Last,
		std::equal_to<typename std::iterator_traits<_FwdIt>::value_type>()
		);
}

template<class _Pr>
class not1_functor
{
	_Pr m_pr;

public:
	typedef bool result_type;

	explicit not1_functor(_Pr _Pred) : m_pr(_Pred) {}
	not1_functor() {}

	template<class T1>
	bool operator()(const T1& x) const
	{
		return !m_pr(x);
	}
};

template<class _Pr>
class not2_functor
{
	_Pr m_pr;

public:
	typedef bool result_type;

	explicit not2_functor(_Pr _Pred) : m_pr(_Pred) {}
	not2_functor() {}

	template<class T1, class T2>
	bool operator()(const T1& x, const T2& y) const
	{
		return !m_pr(x, y);
	}
};

template<class _Pr>
not1_functor<_Pr> not1(const _Pr& _Pred)
{
	return not1_functor<_Pr>(_Pred);
}
template<class _Pr>
not2_functor<_Pr> not2(const _Pr& _Pred)
{
	return not2_functor<_Pr>(_Pred);
}

//////////////////////////////////////////////////////////////////////////
//! find in predicted near range, if not found, find it in the whole range
//!
template<class IterT, class CompareT>
IterT find_next_larger(IterT first, IterT prediction, IterT last, CompareT comp)
{
	IterT iter = std::upper_bound(first, prediction, *first, comp);
	if (iter == prediction && !comp(*first, *prediction)) // *first == *prediction
		return std::upper_bound(prediction, last, *first, comp);
	else
		return iter;
}

//////////////////////////////////////////////////////////////////////////

//! Call Convert(*iter) to convert the iterator
template<class InputIter, class ConvertedType, class Convertor>
class convertor_iterator_adaptor :
	public std::iterator< typename std::iterator_traits<InputIter>::iterator_category
						, ConvertedType>
{
	typedef convertor_iterator_adaptor<InputIter, ConvertedType, Convertor> self_t;

	Convertor m_convert;

public:
	InputIter m_iter;

	typedef typename std::iterator_traits<InputIter>::difference_type difference_type;

	explicit convertor_iterator_adaptor(InputIter iter, const Convertor& convert = Convertor())
		: m_iter(iter), m_convert(convert) {}

	ConvertedType operator*() const { return m_convert(*m_iter); }

	self_t& operator++() { ++m_iter; return *this; }
	self_t& operator--() { --m_iter; return *this; }

	self_t operator++(int) { self_t temp(*this); ++m_iter; return temp; }
	self_t operator--(int) { self_t temp(*this); --m_iter; return temp; }

	bool operator==(const self_t& y) const { return m_iter == y.m_iter; }
	bool operator!=(const self_t& y) const { return m_iter != y.m_iter; }

	bool operator<=(const self_t& y) const { return m_iter <= y.m_iter; }
	bool operator>=(const self_t& y) const { return m_iter >= y.m_iter; }

	bool operator<(const self_t& y) const { return m_iter < y.m_iter; }
	bool operator>(const self_t& y) const { return m_iter > y.m_iter; }

	difference_type operator-(const self_t& y) const { return m_iter - y.m_iter; }

	self_t  operator+(difference_type y) const { return self_t(m_iter + y, m_convert); }

	self_t& operator+=(difference_type i) { m_iter += i; return *this; }
	self_t& operator-=(difference_type i) { m_iter -= i; return *this; }
};
template<class ConvertedType, class InputIter, class Convertor>
convertor_iterator_adaptor<InputIter, ConvertedType, Convertor>
convert_iterator(InputIter iter, const Convertor& convertor)
{
	return convertor_iterator_adaptor<InputIter, ConvertedType, Convertor>(iter, convertor);
}

//! Call extractor(iter) to convert source iter to target iter
//!
//! if *iter is something computed from iter, the adapter can avoid to calling *iter
//! @note
//!  -# Extractor is applied on iter, not '*iter'
//!  -# Extractor should be a functor
template<class InputIter, class MemberType, class Extractor>
class member_iterator_adaptor
  : public std::iterator< typename std::iterator_traits<InputIter>::iterator_category
						, MemberType>
{
	typedef member_iterator_adaptor<InputIter, MemberType, Extractor> self_t;

private:
	Extractor m_extr;

public:
	InputIter m_iter;

	typedef typename std::iterator_traits<InputIter>::difference_type difference_type;

	explicit member_iterator_adaptor(InputIter iter, const Extractor& convert = Extractor())
		: m_iter(iter), m_extr(convert) {}

	MemberType operator*() const { return m_extr(m_iter); }

	self_t& operator++() { ++m_iter; return *this; }
	self_t& operator--() { --m_iter; return *this; }

	self_t operator++(int) { self_t temp(*this); ++m_iter; return temp; }
	self_t operator--(int) { self_t temp(*this); --m_iter; return temp; }

	bool operator==(const self_t& y) const { return m_iter == y.m_iter; }
	bool operator!=(const self_t& y) const { return m_iter != y.m_iter; }

	bool operator<=(const self_t& y) const { return m_iter <= y.m_iter; }
	bool operator>=(const self_t& y) const { return m_iter >= y.m_iter; }

	bool operator<(const self_t& y) const { return m_iter < y.m_iter; }
	bool operator>(const self_t& y) const { return m_iter > y.m_iter; }

	difference_type operator-(const self_t& y) const { return m_iter - y.m_iter; }

	self_t  operator+(difference_type y) const { return self_t(m_iter + y, m_extr); }

	self_t& operator+=(difference_type i) { m_iter += i; return *this; }
	self_t& operator-=(difference_type i) { m_iter -= i; return *this; }
};
template<class MemberType, class InputIter, class Extractor>
member_iterator_adaptor<InputIter, MemberType, Extractor>
member_iterator(InputIter iter, const Extractor& convertor)
{
	return member_iterator_adaptor<InputIter, MemberType, Extractor>(iter, convertor);
}
//////////////////////////////////////////////////////////////////////////

template<class Iter>
typename std::iterator_traits<Iter>::value_type
sum(Iter first, Iter last)
{
	typename std::iterator_traits<Iter>::value_type s =
	typename std::iterator_traits<Iter>::value_type();
	for (; first != last; ++first)
		s += *first;
	return s;
}
template<class C>
typename C::value_type
sum(const C& c)
{
	return sum(c.begin(), c.end());
}
template<class Iter, class Extractor>
typename Extractor::key_type
sum(Iter first, Iter last, Extractor ex)
{
	typename Extractor::key_type s = typename Extractor::key_type();
	for (; first != last; ++first)
		s += ex(*first);
	return s;
}
template<class C, class Extractor>
typename Extractor::key_type
sum(const C& c, Extractor ex)
{
	return sum(c.begin(), c.end(), ex);
}

//////////////////////////////////////////////////////////////////////////
/**
 @brief
 - like std::remove_if, but use std::swap
 */
template<class ForwIter, class Pred>
ForwIter
remove_swap_if(ForwIter first, ForwIter last, Pred pred) {
	for (; first != last; ++first)
		if (pred(*first))
			goto DoErase;
	return last;
DoErase: {
	ForwIter dest = first;
	for (++first; first != last; ++first) {
		using std::swap;
		if (!pred(*first))
			swap(*dest, *first), ++dest;
	}
	return dest;
  }
}

namespace multi_way
{

/**
 @brief can be better inline when pass by param
 */
class FirstEqualSecond {
public:
	template<class T>
	bool operator()(const std::pair<T,T>& x)const{return x.first==x.second;}
};

class MustProvideKeyExtractor;

struct tag_cache_default {}; //!< default select
struct tag_cache_none {};
struct tag_cache_key {};
struct tag_cache_value {};

template<class Value>
class CacheValueArray {
protected: // sizeof(*this) == sizeof(void*)
	Value* m_cache;
public:
	CacheValueArray() { m_cache = NULL; }
	~CacheValueArray() {
		if (m_cache)
			delete []m_cache;
	}
	void resize_cache(size_t size) {
		if (m_cache)
			delete []m_cache;
		m_cache = new Value[size];
		if (NULL == m_cache)
			abort(); // non-exception-safe, must abort
	}
};

template<class Value>
class CacheValueArray0 {
protected: // sizeof(*this) >= 3 * sizeof(void*)
	std::vector<Value> m_cache;
public:
	void resize_cache(size_t size) { m_cache.resize(size); }
};

template<class Key, class KeyExtractor>
class KeyExtractor_get_key : protected KeyExtractor {
public:
	template<class Value>
	const Key& get_key(const Value& x) const { return (*this)(x); }
};

template<class InputIter, class KeyType, class KeyExtractor, class HowCache>
class CacheStrategy; // template proto-type definition

template<class InputIter, class KeyType, class KeyExtractor>
class CacheStrategy<InputIter, KeyType, KeyExtractor, tag_cache_none>
  : protected KeyExtractor_get_key<KeyType, KeyExtractor>
{
public:
	typedef typename std::iterator_traits<InputIter>::value_type value_type;
	typedef tag_cache_none           cache_category;
	typedef boost::tuples::null_type cache_item_type;
protected:
	//! 这个函数必须要带 cache_category 参数
	//! 对 tag_cache_none, 这个函数是在 derived class 中实现的
	//! 但是，因为 derived class 中使用了 using super::get_cache_key, 所以这里必须有 get_cache_key 成员
	//! Just an empty function
	inline void get_cache_key(size_t/*,tag_cache_none*/) const {}
	inline void load_cache_item(size_t, const InputIter&) {/*do nothing...*/}
	inline void resize_cache(size_t) {/*do nothing...*/}

// just used by LoserTree::start_yan_wu
// has no this function, LoserTree::start_yan_wu don't support tag_cache_none
// 	void set_cache_item(size_t i, const cache_item_type&) {}
};

template<class InputIter, class KeyType, class KeyExtractor>
class CacheStrategy<InputIter, KeyType, KeyExtractor, tag_cache_key>
  : protected CacheValueArray<KeyType>
  , protected KeyExtractor_get_key<KeyType, KeyExtractor>
{
public:
	typedef typename std::iterator_traits<InputIter>::value_type value_type;
	typedef tag_cache_key           cache_category;
	typedef KeyType                 cache_item_type;
protected:
	//! 这个函数必须要带 cache_category 参数
	inline const KeyType&
	get_cache_key(size_t nth,tag_cache_key)const{return this->m_cache[nth];}

	inline void
	load_cache_item(size_t i,InputIter p){this->m_cache[i]=this->get_key(*p);}

	// just used by LoserTree::start_yan_wu
	void set_cache_item(size_t i, const KeyType& k) { this->m_cache[i] = k; }
};

template<class InputIter, class KeyType, class KeyExtractor>
class CacheStrategy<InputIter, KeyType, KeyExtractor, tag_cache_value>
  : protected CacheValueArray<typename std::iterator_traits<InputIter>::value_type>
  , protected KeyExtractor_get_key<KeyType, KeyExtractor>
{
public:
	typedef typename std::iterator_traits<InputIter>::value_type value_type;
	typedef tag_cache_value           cache_category;
	typedef value_type                cache_item_type;
protected:
	//! 这个函数必须要带 cache_category 参数
	inline const KeyType&
	get_cache_key(size_t i, tag_cache_value) const {
		return this->get_key(this->m_cache[i]); }

	inline void
	load_cache_item(size_t i, InputIter iter) { this->m_cache[i] = *iter; }

	// just used by LoserTree::start_yan_wu
	void set_cache_item(size_t i, const value_type& x){this->m_cache[i] = x;}
};

template<class InputIter, class KeyType, class KeyExtractor>
class CacheStrategy<InputIter, KeyType, KeyExtractor, tag_cache_default>
	/**
	 @brief cache_item_type is KeyType or value_type

	 为了 cache 的效率，仅缓存必要的数据(CPU cache, or disk cache)
	 - input_iterator_tag 表示同一个数据只从 iterator 读一次，因此缓存整个 value
	 - forward_iterator_tag 及更高级，可以允许从 iterator 读多次，因此只缓存 key
	 */
	: public boost::mpl::if_c<
		boost::is_same<std::input_iterator_tag,
					   typename std::iterator_traits<InputIter>::iterator_category
					  >::value,
		CacheStrategy<InputIter, KeyType, KeyExtractor, tag_cache_value>,
		CacheStrategy<InputIter, KeyType, KeyExtractor, tag_cache_key>
	>::type
{};

template< class way_iter_t
		, class KeyType = typename std::iterator_traits<way_iter_t>::value_type
		, bool StableSort = false //!< same Key in various way will output by way order
		, class Compare = std::less<KeyType>
		, class KeyExtractor = typename boost::mpl::if_c<
				boost::is_same<KeyType,
							   typename std::iterator_traits<way_iter_t>::value_type
							  >::value,
				boost::multi_index::identity<KeyType>,
				MustProvideKeyExtractor
			>::type
	    , class HowCache = tag_cache_default
		>
class LoserTree : protected Compare,
	public CacheStrategy<way_iter_t, KeyType, KeyExtractor, HowCache>
{
	DECLARE_NONE_COPYABLE_CLASS(LoserTree)
	typedef CacheStrategy<way_iter_t, KeyType, KeyExtractor, HowCache> super;
public:
	typedef typename std::iterator_traits<way_iter_t>::value_type value_type;
	typedef KeyType  key_type;
	typedef KeyExtractor key_extractor;
	typedef boost::integral_constant<bool, StableSort> is_stable_sort;
	typedef typename super::cache_category  cache_category;
	typedef typename super::cache_item_type cache_item_type;
	typedef boost::mpl::true_  TriCompare1, StableSort1;
	typedef boost::mpl::false_ TriCompare0, StableSort0;

public:
	/**
	 @brief construct

	 @par 图示如下：
	 @code

	 m_ways                                             this is guard value
	  ||                                                          ||
	  ||                                                          ||
	  \/                                                          \/
	 -------> 0 way_iter_t [min_value.........................max_value]
	   /      1 way_iter_t [min_value.........................max_value] <--- 每个序列均已
	   |      2 way_iter_t [min_value.........................max_value]      按 comp 排序
	   |      3 way_iter_t [min_value.........................max_value]
	  <       4 way_iter_t [min_value.........................max_value]
	   |      5 way_iter_t [min_value.........................max_value]
	   |      7 way_iter_t [min_value.........................max_value]
	   \      8 way_iter_t [min_value.........................max_value]
	 -------> end

	 @endcode

	 @param comp    value 的比较器

	 @note 每个序列最后必须要有一个 max_value 作为序列结束标志，否则会导致未定义行为
	 */
	LoserTree(const KeyType& max_key,
			  const Compare& comp = Compare(),
			  const KeyExtractor& keyExtractor = KeyExtractor())
		: Compare(comp), m_max_key(max_key)
	{
		m_tree = NULL;
		static_cast<KeyExtractor&>(*this) = keyExtractor;
	}
	~LoserTree() {
		if (m_tree) free(m_tree);
	}

	/**
	 @brief 初始化

	- 共有 n 个内部结点，n 个外部结点
	- winner 只用于初始化时计算败者树，算完后即丢弃
	- winner/loser 的第 0 个单元都不是内部结点，不属于树中的一员
	- winner 的第 0 个单元未用
	- m_tree 的第 0 个单元用于保存最终的赢者, 其它单元保存败者

	- 该初始化需要的 n-1 次比较，总的时间复杂度是 O(n)

	- 严蔚敏&吴伟民 的 LoserTree 初始化复杂度是 O(n*log(n))，并且还需要一个 min_key,
	  但是他们的初始化不需要额外的 winner 数组

    - 并且，这个实现比 严蔚敏&吴伟民 的 LoserTree 初始化更强壮
	 */
	void start() {
		size_t len = m_ways.size();
		if (terark_unlikely(len <= 0))
			throw std::invalid_argument("LoserTree: way sequence must not be empty");

		{
			size_t* t = (size_t*)realloc(m_tree, sizeof(size_t) * len);
			if (NULL == t) throw std::bad_alloc();
			m_tree = t;
		}
		this->resize_cache(len);
		size_t i;
		for (i = 0; i != len; ++i) {
			// load first value from every sequence
			this->load_cache_item(i, m_ways[i]);
		}
		if (terark_unlikely(1 == len)) {
			m_head = 0;
			return;
		}

		size_t minInnerToEx = len / 2;
		std::vector<size_t> winner(len);

		for (i = len - 1; i > minInnerToEx; --i)
			exter_loser_winner(m_tree[i], winner[i], i, len);
		size_t left, right;
		if (len & 1) { // odd
		// left child is last inner node, right child is first external node
			left = winner[len-1];
			right = 0;
		} else {
			left = 0;
			right = 1;
		}
		get_loser_winner(m_tree[minInnerToEx], winner[minInnerToEx], left, right);

		for (i = minInnerToEx; i > 0; i /= 2)
			for (size_t j = i; j > i/2; ) {
				--j;
				inner_loser_winner(m_tree[j], winner[j], j, winner);
			}
		m_head = winner[1];
	}

	//! 严蔚敏&吴伟民 的 LoserTree 初始化
	//! 复杂度是 O(n*log(n))，并且还需要一个 min_key
	void start_yan_wu(const cache_item_type& min_item,
					  const cache_item_type& max_item)
	{
		//! this function do not support tag_cache_none
		size_t len = m_ways.size();
		this->resize_cache(len+1);
		this->set_cache_item(len, min_item);

		size_t i;
		for (i = 0; i < len; ++i) {
			m_tree[i] = len;
			// load first value from every sequence
			this->load_cache_item(i, m_ways[i]);
		}
		for (i = len; i > 0; ) ajust(--i);

		// 防止 cache 的最后一个成员上升到 top ??.....
		//
		this->set_cache_item(len, max_item);

	//	assert(!m_ways.empty());
	//	if (m_head == len)
	//		ajust(len); // 会导致在 ajust 中 m_tree[parent] 越界
	}

	const value_type& current_value() const {
		assert(!m_ways.empty());
	//	assert(!empty()); // 允许访问末尾的 guardValue, 便于简化 app
		return current_value_aux(cache_category());
	}

	/**
	 @brief return current way NO.
	 */
	size_t current_way() const {
		assert(!m_ways.empty());
	//	assert(!empty()); // allow this use
		return m_head;
	}

	size_t total_ways() const { return m_ways.size(); }
	bool is_any_way_end() const { return empty(); }

	bool empty() const {
		assert(!m_ways.empty());
		const KeyType& cur_key = get_cache_key(m_head, cache_category());
		const Compare& cmp = *this;
		return !cmp(cur_key, m_max_key); // cur_key >= max_value
	}

	void increment() {
		assert(!m_ways.empty());
		assert(!empty());
		size_t top = m_head;
		this->load_cache_item(top, ++m_ways[top]);
		ajust(top);
	}

	void ajust_for_update_top() {
		assert(!m_ways.empty());
		size_t top = m_head;
		this->load_cache_item(top, m_ways[top]);
		ajust(top);
	}

	way_iter_t& top_iter() { assert(!m_ways.empty()); return  m_ways[m_head]; }
	const size_t* get_tree() const { return m_tree; }

protected:
	void ajust(size_t s) {
		size_t parent = s + m_ways.size();
		size_t*ptree =  m_tree;
		while ((parent /= 2) > 0) {
			size_t& tparent = ptree[parent];
			if (comp_cache_item(tparent, s, cache_category(), is_stable_sort()))
				std::swap(s, tparent);
		}
		m_head = s;
	}

	void exter_loser_winner(size_t& loser, size_t& winner, size_t parent, size_t len) const {
		size_t left  = 2 * parent - len;
		size_t right = left + 1;
		get_loser_winner(loser, winner, left, right);
	}
	void inner_loser_winner(size_t& loser, size_t& winner, size_t parent,
				const std::vector<size_t>& winner_vec) const {
		size_t left  = 2 * parent;
		size_t right = 2 * parent + 1;
		left  = winner_vec[left];
		right = winner_vec[right];
		get_loser_winner(loser, winner, left, right);
	}
	void get_loser_winner(size_t& loser, size_t& winner, size_t left, size_t right) const {
		if (comp_cache_item(left, right, cache_category(), is_stable_sort())) {
			loser  = right;
			winner = left;
		} else {
			loser = left;
			winner = right;
		}
	}

	const value_type& current_value_aux(tag_cache_none) const {
		assert(m_head < m_ways.size());
		return *m_ways[m_head];
	}
	const value_type& current_value_aux(tag_cache_key) const {
		assert(m_head < m_ways.size());
		return *m_ways[m_head];
	}
	const value_type& current_value_aux(tag_cache_value) const {
		assert(m_head < m_ways.size());
		return this->m_cache[m_head];
	}

	using super::get_cache_key;
	inline const KeyType& get_cache_key(size_t nth, tag_cache_none) const {
		return this->get_key(*m_ways[nth]); }

	template<class CacheCategory>
	inline bool
	comp_cache_item(size_t x, size_t y, CacheCategory, StableSort0) const {
		const KeyType& kx = get_cache_key(x, CacheCategory());
		const KeyType& ky = get_cache_key(y, CacheCategory());
		return static_cast<const Compare&>(*this)(kx, ky);
	}
	template<class CacheCategory>
	inline bool
	comp_cache_item(size_t x, size_t y, CacheCategory, StableSort1) const {
		const KeyType& kx = get_cache_key(x, CacheCategory());
		const KeyType& ky = get_cache_key(y, CacheCategory());
		return comp_key_stable(x, y, kx, ky, HasTriCompare<Compare>());
	}
	inline bool
	comp_key_stable(size_t x, size_t y, const KeyType& kx, const KeyType& ky, TriCompare1) const {
		ptrdiff_t ret = Compare::compare(kx, ky);
		if (ret < 0) return true;
		if (ret > 0) return false;
		ret = Compare::compare(kx, m_max_key);
		assert(ret <= 0);
		if (0==ret) return false;
		else        return x < y;
	}
	inline bool
	comp_key_stable(size_t x, size_t y, const KeyType& kx, const KeyType& ky, TriCompare0) const {
		const Compare& cmp = *this;
		if ( cmp(kx, ky)) return true;
		if ( cmp(ky, kx)) return false;
		if (!cmp(kx, m_max_key)) {// kx >= max_key --> kx == max_key
			// max_key is the max, so must assert this:
			assert(!cmp(m_max_key, kx));
			return false;
		}
		else return x < y;
	}

protected:
	size_t* m_tree;
	size_t  m_head; // access m_tree[0] need an extra memory load
	KeyType m_max_key;
public:
	std::vector<way_iter_t> m_ways;
typedef LoserTree MyType;
#include "multi_way_basic.hpp"
#include "multi_way_algo_loser_tree.hpp"
};

template<class _InIt>
struct Heap_WayIterEx : public std::pair<_InIt, _InIt> {
	size_t index;
	template<class _InIt2>
	Heap_WayIterEx(const Heap_WayIterEx<_InIt2>& y)
	  : std::pair<_InIt, _InIt>(y.first, y.second), index(y.index) {}
	Heap_WayIterEx(size_t index, const _InIt& i1, const _InIt& i2)
	  : std::pair<_InIt, _InIt>(i1, i2), index(index) {}
};
template<class _InIt>
Heap_WayIterEx<_InIt>
make_way_iter_ex(size_t index, const _InIt& i1, const _InIt& i2) {
	return Heap_WayIterEx<_InIt>(index, i1, i2); }

template< class _InIt
		, class KeyType = typename std::iterator_traits<_InIt>::value_type
		, bool StableSort = false //!< same Key in various way will output by way order
		, class Compare = std::less<KeyType>
		, class KeyExtractor = typename boost::mpl::if_c<
				boost::is_same<KeyType,
							   typename std::iterator_traits<_InIt>::value_type
							  >::value,
				boost::multi_index::identity<KeyType>,
				MustProvideKeyExtractor
			>::type
		, class WayIter = typename boost::mpl::if_c<StableSort, Heap_WayIterEx<_InIt>, std::pair<_InIt, _InIt> >::type
		>
class HeapMultiWay_TrivalWayIter : private Compare {
	DECLARE_NONE_COPYABLE_CLASS(HeapMultiWay_TrivalWayIter)
public:
	typedef typename std::iterator_traits<_InIt>::value_type value_type;
	typedef KeyExtractor key_extractor;
	typedef KeyType  key_type;
	typedef Compare  compare_t;
	typedef WayIter  way_iter_t;
	typedef boost::mpl::true_  TriCompare1, StableSort1;
	typedef boost::mpl::false_ TriCompare0, StableSort0;

	class HeapCompare {
		const HeapMultiWay_TrivalWayIter& heap;
	public:
		//! 反转输入参数，用做堆操作的比较
		bool operator()(const way_iter_t& x, const way_iter_t& y) const {
			assert(x.first != x.second);
			assert(y.first != y.second);
			boost::integral_constant<bool, StableSort> is_stable_sort;
			return heap.comp_item_aux(y, x, is_stable_sort);
		}
		explicit HeapCompare(const HeapMultiWay_TrivalWayIter& heap) : heap(heap) {}
	};
	friend class HeapCompare;

public:
	HeapMultiWay_TrivalWayIter(Compare comp = Compare()) : Compare(comp) {}

	void start() {
	#ifndef NDEBUG
		for (size_t i = 0, n = m_ways.size(); i < n; ++i) {
			assert(m_ways[i].first != m_ways[i].second); }
	#endif
		m_old_size = m_ways.size();
		std::make_heap(m_ways.begin(), m_ways.end(), HeapCompare(*this));
	}

	bool empty() { return m_ways.empty(); }

	const value_type& current_value() const {
		assert(!m_ways.empty());
		return *m_ways.front().first;
	}

	const WayIter& current_input() const {
		assert(!m_ways.empty());
		return  m_ways.front();
	}

	size_t current_way() const {
		assert(!m_ways.empty());
		return  m_ways.front().index;
	}

	void increment() {
		assert(!m_ways.empty());
		++m_ways.front().first;
		ajust_for_update_top();
	}

	void ajust_for_update_top() {
		if (FirstEqualSecond()(m_ways.front())) {
			pop_heap_ignore_top(m_ways.begin(), m_ways.end(), HeapCompare(*this));
			m_ways.pop_back();
		} else
			adjust_heap_top(m_ways.begin(), m_ways.end(), HeapCompare(*this));
	}

	WayIter& top_iter() { assert(!m_ways.empty()); return m_ways.front(); }

	/*
	 @par 图示如下：
	 @code
	   [                m_ways                   ]
	 0 [first..............................second]
	 1 [first..............................second]
	 2 [first..............................second] <--- 每个序列均已按 comp 排序
	 3 [first..............................second]
	 4 [first..............................second]
	 5 [first..............................second]
	 @endcode
	 way_iter_t 是一个 pair<_InIt, _InIt>，表示一个递增序列 [first, second)
	*/
	std::vector<way_iter_t> m_ways; // public
private:
	size_t     m_old_size;

	inline bool
	comp_item_aux(const way_iter_t& x, const way_iter_t& y, StableSort0) const {
		assert(x.first != x.second);
		assert(y.first != y.second);
		return (*this)(*x.first, *y.first);
	}
	inline bool
	comp_item_aux(const way_iter_t& x, const way_iter_t& y, StableSort1) const {
		assert(x.first != x.second);
		assert(y.first != y.second);
		return comp_key_stable(x.index, y.index, *x.first, *y.first,
				typename HasTriCompare<Compare>::type());
	}
	inline bool
	comp_key_stable(size_t x, size_t y, const value_type& kx, const value_type& ky, TriCompare1) const {
		ptrdiff_t ret = Compare::compare(kx, ky);
		if (ret < 0) return true;
		if (ret > 0) return false;
		else         return x < y;
	}
	inline bool
	comp_key_stable(size_t x, size_t y, const value_type& kx, const value_type& ky, TriCompare0) const {
		const Compare& cmp = *this;
		if (cmp(kx, ky)) return true;
		if (cmp(ky, kx)) return false;
		else             return x < y;
	}
public:
	size_t total_ways() const { return m_old_size; }
	bool is_any_way_end() const { return m_ways.size() != m_old_size; }

typedef HeapMultiWay_TrivalWayIter MyType;
#include "multi_way_basic.hpp"
#include "multi_way_algo_heap.hpp"
};

template< class InputIter
		, class KeyType = typename std::iterator_traits<InputIter>::value_type
		, bool StableSort = false //!< same Key in various way will output by way order
		, class Compare = std::less<KeyType>
		, class KeyExtractor = typename boost::mpl::if_c<
				boost::is_same<KeyType,
					typename std::iterator_traits<InputIter>::value_type
							  >::value,
				boost::multi_index::identity<KeyType>,
				MustProvideKeyExtractor
			>::type
	    , class HowCache = tag_cache_default
		>
class HeapMultiWay : private Compare,
	public CacheStrategy<InputIter, KeyType, KeyExtractor, HowCache>
{
	DECLARE_NONE_COPYABLE_CLASS(HeapMultiWay)
	typedef CacheStrategy<InputIter, KeyType, KeyExtractor, HowCache> super;
public:
	typedef std::pair<InputIter, InputIter>  iipair;
	typedef typename std::iterator_traits<InputIter>::value_type value_type;
	typedef KeyType      key_type;
	typedef KeyExtractor key_extractor;
	typedef typename super::cache_category cache_category;
	typedef boost::mpl::true_  TriCompare1, StableSort1;
	typedef boost::mpl::false_ TriCompare0, StableSort0;

	class HeapCompare {
		const HeapMultiWay& heap;
	public:
		//! 反转输入参数，用做堆操作的比较
		inline bool operator()(size_t x, size_t y) const {
			assert(heap.m_ways[x].first != heap.m_ways[x].second);
			assert(heap.m_ways[y].first != heap.m_ways[y].second);
			boost::integral_constant<bool, StableSort> is_stable_sort;
			return heap.comp_cache_item(y, x, cache_category(), is_stable_sort);
		}
		explicit HeapCompare(const HeapMultiWay& heap) : heap(heap) {}
	};
	friend class HeapCompare;

public:
	/**
	 @brief construct

	 @par 图示如下：
	 @code
	 m_ways     first                              second
      ||         ||                                   ||
	  ||         ||                                   ||
	  \/         \/                                   \/
	  0 iipair [min_value..............................)
	  1 iipair [min_value..............................) <--- 每个序列均已
	  2 iipair [min_value..............................)      按 comp 排序
	  3 iipair [min_value..............................)
	  4 iipair [min_value..............................)
	  5 iipair [min_value..............................)
	  7 iipair [min_value..............................)
	  8 iipair [min_value..............................)
	 @endcode

	 @param first, last [in,out] 每个元素都是一个 iipair, 表示一个递增子序列 [first, second)
	 @param comp    value 的比较器

	 @note
	  - 一般情况下，这个类的性能不如 LoserTree, 但是，这个类也有一些优点：
	    - 对每个序列用 make_pair(first, last) 来表示，概念上更清晰
	    - 序列最后不需要一个 max_value 结束标志，这一点上胜于 LoserTree
		  - 当无法提供 max_value 时，使用这个类也许是更好的办法，比如当 int key 在整个 int 值域内都有定义时
	  - LoserTree 的每个序列只用一个 iterator 表示，序列结束用 MAX_KEY 做标志
	 */
	HeapMultiWay(const Compare& comp = Compare(),
				 const KeyExtractor& keyExtractor = KeyExtractor())
	{
		static_cast<KeyExtractor&>(*this) = keyExtractor;
	}
	void start() {
		assert(!m_ways.empty());
		m_old_size = m_ways.size();
		m_heap.reserve(m_old_size);
		this->resize_cache(m_old_size);
		for (size_t way_idx = 0; way_idx < m_old_size; ++way_idx) {
			if (m_ways[way_idx].first != m_ways[way_idx].second) {
				m_heap.push_back(way_idx);
				// read first value from every sequence
				this->load_cache_item(way_idx, m_ways[way_idx].first);
			}
		}
		std::make_heap(m_heap.begin(), m_heap.end(), HeapCompare(*this));
	}

	bool empty() const { return m_heap.empty(); }

	const value_type& current_value() const {
		assert(!m_heap.empty());
		return current_value_aux(cache_category());
	}
	size_t current_way() const { assert(!m_heap.empty()); return m_heap[0]; }

	void increment() {
		assert(!m_heap.empty());
		size_t top_way = m_heap[0];
		iipair& top = *(m_ways + top_way);
		++top.first;
		if (terark_unlikely(top.first == top.second)) {
			pop_heap_ignore_top(m_heap.begin(), m_heap.end(), HeapCompare(*this));
			m_heap.pop_back();
		} else {
			this->load_cache_item(top_way, top.first);
			adjust_heap_top(m_heap.begin(), m_heap.end(), HeapCompare(*this));
		}
	}

	void ajust_for_update_top() {
		assert(!m_heap.empty());
		size_t top_way = m_heap[0];
		iipair& top = m_ways[top_way];
	//	++top.first; // caller may inc multi for top.first
		if (terark_unlikely(top.first == top.second)) {
			pop_heap_ignore_top(m_heap.begin(), m_heap.end(), HeapCompare(*this));
			m_heap.pop_back();
		} else {
			this->load_cache_item(top_way, top.first);
			adjust_heap_top(m_heap.begin(), m_heap.end(), HeapCompare(*this));
		}
	}

	iipair& top_iter() { assert(!m_heap.empty()); return m_ways[m_heap[0]]; }

protected:
	const value_type& current_value_aux(tag_cache_none) const {
		assert(m_heap[0] < m_old_size);
		return *(*(m_ways + m_heap[0])).first;
	}
	const value_type& current_value_aux(tag_cache_key) const {
		assert(m_heap[0] < m_old_size);
		return *(*(m_ways + m_heap[0])).first;
	}
	const value_type& current_value_aux(tag_cache_value) const {
		assert(m_heap[0] < m_old_size);
		return this->m_cache[m_heap[0]];
	}

	using super::get_cache_key;
	inline KeyType get_cache_key(size_t nth, tag_cache_none) const {
		assert(nth < m_old_size);
		const iipair& p = *(m_ways + nth);
		assert(p.first != p.second);
		return this->get_key(*p.first);
	}

	template<class CacheCategory>
	inline bool
	comp_cache_item(size_t x, size_t y,	CacheCategory, StableSort0) const {
		const KeyType& kx = get_cache_key(x, CacheCategory());
		const KeyType& ky = get_cache_key(y, CacheCategory());
		return static_cast<const Compare&>(*this)(kx, ky);
	}
	template<class CacheCategory>
	inline bool
	comp_cache_item(size_t x, size_t y, CacheCategory, StableSort1) const {
		const KeyType& kx = get_cache_key(x, CacheCategory());
		const KeyType& ky = get_cache_key(y, CacheCategory());
		return comp_key_stable(x, y, kx, ky, HasTriCompare<Compare>());
	}
	inline bool
	comp_key_stable(size_t x, size_t y, const KeyType& kx, const KeyType& ky, TriCompare1) const {
		ptrdiff_t ret = Compare::compare(kx, ky);
		if (ret < 0) return true;
		if (ret > 0) return false;
		else         return x < y;
	}
	inline bool
	comp_key_stable(size_t x, size_t y, const KeyType& kx, const KeyType& ky, TriCompare0) const {
		const Compare& cmp = *this;
		if (cmp(kx, ky)) return true;
		if (cmp(ky, kx)) return false;
		else             return x < y;
	}

	bool is_any_way_end() const { return m_heap.size() != m_old_size; }
	size_t total_ways() const { return m_old_size; }

protected:
	std::vector<iipair> m_ways;
	std::vector<size_t> m_heap;
	size_t			m_old_size;

typedef HeapMultiWay MyType;
#include "multi_way_basic.hpp"
#include "multi_way_algo_heap.hpp"
};

//! TODO:

template<class InputIterator>
class InputIteratorReader {
	InputIterator m_end;
public:
	bool empty(const InputIterator& iter) const { return iter != m_end; }
};

/**
 @brief 当相同元素的数目超过 minDup 时，将元素拷贝到输出

 这是一个很有用的 multi_way_copy_if._Cond, 并且也作为一个编写 _Cond 的示例
 可以在 MultiWay_CopyAtLeastDup 中对 value 进行某方面的统计（例如记录每个 value 的重复次数）
 */
class MultiWay_CopyAtLeastDup {
	ptrdiff_t m_minDup;
public:
	MultiWay_CopyAtLeastDup(ptrdiff_t minDup) : m_minDup(minDup) {}

	template<class ValueT>
	bool operator()(ptrdiff_t equal_count,
					const ValueT& /*prev_value*/,
					const ValueT& /*curr_value*/) const
	{
		// 仅当 equal_count 第一次达到 m_minDup 时才返回 true
		// 返回 equal_count >= m_minDup 会导致多次拷贝

		return equal_count == m_minDup;
	}
};

} // namespace multi_way

template<class Container>
class any_inserter_iterator :
	public std::iterator<std::output_iterator_tag, typename Container::value_type>
{
public:
	typedef Container container_type;
	typedef typename Container::reference reference;

	explicit any_inserter_iterator(Container& _Cont) : container(&_Cont) {}

	template<class T>
	any_inserter_iterator&
	operator=(const T& x) { container->insert(x); return *this; }

	// pretend to return designated value
	any_inserter_iterator& operator*() { return *this; }

	// pretend to preincrement
	any_inserter_iterator& operator++() { return *this; }

	// pretend to postincrement
	any_inserter_iterator operator++(int) { return *this; }

protected:
	Container *container;
};

template<class Container>
any_inserter_iterator<Container>
any_inserter(Container& c) { return any_inserter_iterator<Container>(c); }

} // namespace terark


/*
All modification made by Intel Corporation: © 2016 Intel Corporation

All contributions by the University of California:
Copyright (c) 2014, 2015, The Regents of the University of California (Regents)
All rights reserved.

All other contributions:
Copyright (c) 2014, 2015, the respective contributors
All rights reserved.
For the list of contributors go to https://github.com/BVLC/caffe/blob/master/CONTRIBUTORS.md

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#if USE_MKL
#include <mkl_vml_functions.h>
#include <mkl_vsl.h>
#endif

#ifdef _OPENMP
#include <omp.h>
#endif

#include <boost/math/special_functions/next.hpp>
#include <boost/random.hpp>

#include <algorithm>
#include <limits>

#include "caffe/common.hpp"
#include "caffe/util/cpu_info.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

template<>
void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const float alpha, const float* A, const float* B, const float beta,
    float* C) {
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
}

template<>
void caffe_cpu_gemm<double>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const double alpha, const double* A, const double* B, const double beta,
    double* C) {
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_dgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
}

template <>
void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const float alpha, const float* A, const float* x,
    const float beta, float* y) {
  cblas_sgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
}

template <>
void caffe_cpu_gemv<double>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const double alpha, const double* A, const double* x,
    const double beta, double* y) {
  cblas_dgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
}

template <>
void caffe_axpy<float>(const int N, const float alpha, const float* X,
    float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }

template <>
void caffe_axpy<double>(const int N, const double alpha, const double* X,
    double* Y) { cblas_daxpy(N, alpha, X, 1, Y, 1); }

template <typename Dtype>
void caffe_set(const int N, const Dtype alpha, Dtype* Y) {
  // If we are executing parallel region already then do not start another one
  // if also number of data to be processed is smaller than arbitrary:
  // threashold 12*4 cachelines per thread then no parallelization is to be made
  #ifdef _OPENMP

  int nthr = omp_get_max_threads();
  int threshold = nthr * caffe::cpu::OpenMpManager::getProcessorSpeedMHz() / 3;
  bool run_parallel =  // Do not do parallel computation from non major threads
       caffe::cpu::OpenMpManager::isMajorThread(boost::this_thread::get_id());

  // Note: we Assume GPU's CPU path is single threaded
  if (omp_in_parallel() == 0) {
    // inactive parallel region may mean also batch 1,
    // but no new threads are to be created
    run_parallel = run_parallel && (Caffe::mode() != Caffe::GPU) &&
                   (N >= threshold);
  } else {
    // If we are running active parallel region then it is CPU
    run_parallel = run_parallel && (N >= threshold);
  }

  if (run_parallel) {
    #pragma omp parallel for
    for (int i = 0; i < N; ++i) {
      Y[i] = alpha;
    }

    return;
  }

  #endif

  if (alpha == 0) {
    memset(Y, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
  } else {
    std::fill(Y, Y + N, alpha);
  }
}

template void caffe_set<char>(const int N, const char alpha, char* Y);
template void caffe_set<int>(const int N, const int alpha, int* Y);
template void caffe_set<float>(const int N, const float alpha, float* Y);
template void caffe_set<double>(const int N, const double alpha, double* Y);
template void caffe_set<size_t>(const int N, const size_t alpha, size_t* Y);

template <>
void caffe_add_scalar(const int N, const float alpha, float* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}

template <>
void caffe_add_scalar(const int N, const double alpha, double* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}

template <typename Dtype>
void caffe_cpu_copy(const int N, const Dtype* X, Dtype* Y) {
  if (X == Y) return;

#ifdef _OPENMP
  static const int threshold = omp_get_max_threads() *
                          caffe::cpu::OpenMpManager::getProcessorSpeedMHz() / 3;
  const bool run_parallel =
    (N >= threshold) &&
    (omp_in_parallel() == 0) &&
    (Caffe::mode() != Caffe::GPU) &&
    (caffe::cpu::OpenMpManager::isMajorThread(boost::this_thread::get_id()));

  if (run_parallel) {
    const int block_mem_size = 256*1024;
    const int block_size = block_mem_size / sizeof(Dtype);
    #pragma omp parallel for
    for (int i = 0; i < N; i += block_size)
      memcpy(Y + i, X + i,
              (i + block_size > N) ? (N-i)*sizeof(Dtype): block_mem_size);

    return;
  }
#endif

  memcpy(Y, X, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
}

template void caffe_cpu_copy<int>(const int N, const int* X, int* Y);
template void caffe_cpu_copy<unsigned int>(const int N, const unsigned int* X,
    unsigned int* Y);
template void caffe_cpu_copy<float>(const int N, const float* X, float* Y);
template void caffe_cpu_copy<double>(const int N, const double* X, double* Y);

template <typename Dtype>
void caffe_copy(const int N, const Dtype* X, Dtype* Y) {
  if (X != Y) {
#ifndef CPU_ONLY
    if (
#ifdef _OPENMP
         // If there are more than one openmp thread (we are in active region)
         // then checking Caffe::mode can create additional GPU Context
        (omp_in_parallel() == 0) &&
#endif
        (Caffe::mode() == Caffe::GPU)) {
      // NOLINT_NEXT_LINE(caffe/alt_fn)
      CUDA_CHECK(cudaMemcpy(Y, X, sizeof(Dtype) * N, cudaMemcpyDefault));
    } else {
#endif
      caffe_cpu_copy<Dtype>(N, X, Y);
#ifndef CPU_ONLY
    }
#endif
  }
}

template void caffe_copy<bool>(const int N, const bool* X, bool* Y);
template void caffe_copy<int>(const int N, const int* X, int* Y);
template void caffe_copy<unsigned int>(const int N, const unsigned int* X,
    unsigned int* Y);
template void caffe_copy<float>(const int N, const float* X, float* Y);
template void caffe_copy<double>(const int N, const double* X, double* Y);
template void caffe_copy<char>(const int N, const char* X, char* Y);
template void caffe_copy<size_t>(const int N, const size_t* X, size_t* Y);

template <>
void caffe_scal<float>(const int N, const float alpha, float *X) {
  cblas_sscal(N, alpha, X, 1);
}

template <>
void caffe_scal<double>(const int N, const double alpha, double *X) {
  cblas_dscal(N, alpha, X, 1);
}

template <>
void caffe_scal<size_t>(const int N, const size_t alpha, size_t *X) {
}

template <>
void caffe_cpu_axpby<float>(const int N, const float alpha, const float* X,
                            const float beta, float* Y) {
  cblas_saxpby(N, alpha, X, 1, beta, Y, 1);
}

template <>
void caffe_cpu_axpby<double>(const int N, const double alpha, const double* X,
                             const double beta, double* Y) {
  cblas_daxpby(N, alpha, X, 1, beta, Y, 1);
}

template <>
void caffe_axpy<size_t>(const int N, const size_t alpha, const size_t* X,
    size_t* Y) { }

template <>
void caffe_add<float>(const int n, const float* a, const float* b,
    float* y) {
  vsAdd(n, a, b, y);
}

template <>
void caffe_add<double>(const int n, const double* a, const double* b,
    double* y) {
  vdAdd(n, a, b, y);
}

template <>
void caffe_sub<float>(const int n, const float* a, const float* b,
    float* y) {
  vsSub(n, a, b, y);
}

template <>
void caffe_sub<double>(const int n, const double* a, const double* b,
    double* y) {
  vdSub(n, a, b, y);
}

template <>
void caffe_mul<float>(const int n, const float* a, const float* b,
    float* y) {
  vsMul(n, a, b, y);
}

template <>
void caffe_mul<double>(const int n, const double* a, const double* b,
    double* y) {
  vdMul(n, a, b, y);
}

template <>
void caffe_div<float>(const int n, const float* a, const float* b,
    float* y) {
  vsDiv(n, a, b, y);
}

template <>
void caffe_div<double>(const int n, const double* a, const double* b,
    double* y) {
  vdDiv(n, a, b, y);
}

template <>
void caffe_powx<float>(const int n, const float* a, const float b,
    float* y) {
  vsPowx(n, a, b, y);
}

template <>
void caffe_powx<double>(const int n, const double* a, const double b,
    double* y) {
  vdPowx(n, a, b, y);
}

template <>
void caffe_sqr<float>(const int n, const float* a, float* y) {
  vsSqr(n, a, y);
}

template <>
void caffe_sqr<double>(const int n, const double* a, double* y) {
  vdSqr(n, a, y);
}

template <>
void caffe_exp<float>(const int n, const float* a, float* y) {
  vsExp(n, a, y);
}

template <>
void caffe_exp<double>(const int n, const double* a, double* y) {
  vdExp(n, a, y);
}

template <>
void caffe_log<float>(const int n, const float* a, float* y) {
  vsLn(n, a, y);
}

template <>
void caffe_log<double>(const int n, const double* a, double* y) {
  vdLn(n, a, y);
}

template <>
void caffe_abs<float>(const int n, const float* a, float* y) {
    vsAbs(n, a, y);
}

template <>
void caffe_abs<double>(const int n, const double* a, double* y) {
    vdAbs(n, a, y);
}

unsigned int caffe_rng_rand() {
#ifdef DETERMINISTIC
    return 5153;
#else
    return (*caffe_rng())();
#endif
}

template <typename Dtype>
Dtype caffe_nextafter(const Dtype b) {
  return boost::math::nextafter<Dtype>(
      b, std::numeric_limits<Dtype>::max());
}

template
float caffe_nextafter(const float b);

template
double caffe_nextafter(const double b);

template <typename Dtype>
void caffe_rng_uniform(const int n, const Dtype a, const Dtype b, Dtype* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_LE(a, b);
  boost::uniform_real<Dtype> random_distribution(a, caffe_nextafter<Dtype>(b));
  boost::variate_generator<caffe::rng_t*, boost::uniform_real<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
}

template
void caffe_rng_uniform<float>(const int n, const float a, const float b,
                              float* r);

template
void caffe_rng_uniform<double>(const int n, const double a, const double b,
                               double* r);

template <typename Dtype>
void caffe_rng_gaussian(const int n, const Dtype a,
                        const Dtype sigma, Dtype* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_GT(sigma, 0);
  boost::normal_distribution<Dtype> random_distribution(a, sigma);
  boost::variate_generator<caffe::rng_t*, boost::normal_distribution<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
}

template
void caffe_rng_gaussian<float>(const int n, const float mu,
                               const float sigma, float* r);

template
void caffe_rng_gaussian<double>(const int n, const double mu,
                                const double sigma, double* r);

#ifdef USE_MKL
static void bernoulli_generate(int n, double p, int* r) {
  int seed = 17 + caffe_rng_rand() % 4096;

#ifdef _OPENMP
  int nthr = omp_get_max_threads();
  int threshold = nthr * caffe::cpu::OpenMpManager::getProcessorSpeedMHz() / 3;
  bool run_parallel =
    (Caffe::mode() != Caffe::GPU) &&
    (omp_in_parallel() == 0) &&
    (n >= threshold);
  if (!run_parallel) nthr = 1;

# pragma omp parallel num_threads(nthr)
  {
    const int ithr = omp_get_thread_num();
    const int avg_amount = (n + nthr - 1) / nthr;
    const int my_offset = ithr * avg_amount;
    const int my_amount = std::min(my_offset + avg_amount, n) - my_offset;
#else
  {
    const int my_amount = n;
    const int my_offset = 0;
#endif

    if (my_amount > 0) {
      VSLStreamStatePtr stream;
      vslNewStream(&stream, VSL_BRNG_MCG31, seed);
      vslSkipAheadStream(stream, my_offset);
      viRngBernoulli(VSL_RNG_METHOD_BERNOULLI_ICDF, stream, my_amount,
        r + my_offset, p);
      vslDeleteStream(&stream);
    }
  }
}
#endif

template <typename Dtype>
void caffe_rng_bernoulli(const int n, const Dtype p, int* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_GE(p, 0);
  CHECK_LE(p, 1);
#ifdef USE_MKL
  bernoulli_generate(n, p, r);
#else
  boost::bernoulli_distribution<Dtype> random_distribution(p);
  boost::variate_generator<caffe::rng_t*, boost::bernoulli_distribution<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
#endif
}

template
void caffe_rng_bernoulli<double>(const int n, const double p, int* r);

template
void caffe_rng_bernoulli<float>(const int n, const float p, int* r);

template <typename Dtype>
void caffe_rng_bernoulli(const int n, const Dtype p, unsigned int* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_GE(p, 0);
  CHECK_LE(p, 1);
#ifdef USE_MKL
  bernoulli_generate(n, p, reinterpret_cast<int *>(r));
#else
  boost::bernoulli_distribution<Dtype> random_distribution(p);
  boost::variate_generator<caffe::rng_t*, boost::bernoulli_distribution<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = static_cast<unsigned int>(variate_generator());
  }
#endif
}

template
void caffe_rng_bernoulli<double>(const int n, const double p, unsigned int* r);

template
void caffe_rng_bernoulli<float>(const int n, const float p, unsigned int* r);

template <>
float caffe_cpu_strided_dot<float>(const int n, const float* x, const int incx,
    const float* y, const int incy) {
  return cblas_sdot(n, x, incx, y, incy);
}

template <>
double caffe_cpu_strided_dot<double>(const int n, const double* x,
    const int incx, const double* y, const int incy) {
  return cblas_ddot(n, x, incx, y, incy);
}

template <>
size_t caffe_cpu_strided_dot<size_t>(const int n, const size_t* x,
        const int incx, const size_t* y, const int incy) {
  return 0;
}

template <typename Dtype>
Dtype caffe_cpu_dot(const int n, const Dtype* x, const Dtype* y) {
  return caffe_cpu_strided_dot(n, x, 1, y, 1);
}

template
float caffe_cpu_dot<float>(const int n, const float* x, const float* y);

template
double caffe_cpu_dot<double>(const int n, const double* x, const double* y);

template
size_t caffe_cpu_dot<size_t>(const int n, const size_t* x, const size_t* y);

template <>
float caffe_cpu_asum<float>(const int n, const float* x) {
  return cblas_sasum(n, x, 1);
}

template <>
double caffe_cpu_asum<double>(const int n, const double* x) {
  return cblas_dasum(n, x, 1);
}

template <>
size_t caffe_cpu_asum<size_t>(const int n, const size_t* x) {
  return 0;
}

template <>
void caffe_cpu_scale<float>(const int n, const float alpha, const float *x,
                            float* y) {
  cblas_scopy(n, x, 1, y, 1);
  cblas_sscal(n, alpha, y, 1);
}

template <>
void caffe_cpu_scale<double>(const int n, const double alpha, const double *x,
                             double* y) {
  cblas_dcopy(n, x, 1, y, 1);
  cblas_dscal(n, alpha, y, 1);
}

}  // namespace caffe


// Copyright 2008 Gautam Sewani
// Copyright 2008 John Maddock
//
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_MATH_DISTRIBUTIONS_DETAIL_HG_PDF_HPP
#define BOOST_MATH_DISTRIBUTIONS_DETAIL_HG_PDF_HPP

#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/lanczos.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/special_functions/pow.hpp>
#include <boost/math/special_functions/prime.hpp>
#include <boost/math/policies/error_handling.hpp>

#ifdef BOOST_MATH_INSTRUMENT
#include <typeinfo>
#endif

namespace boost{ namespace math{ namespace detail{

template <class T, class Func>
void bubble_down_one(T* first, T* last, Func f)
{
   using std::swap;
   T* next = first;
   ++next;
   while((next != last) && (!f(*first, *next)))
   {
      swap(*first, *next);
      ++first;
      ++next;
   }
}

template <class T>
struct sort_functor
{
   sort_functor(const T* exponents) : m_exponents(exponents){}
   bool operator()(int i, int j)
   {
      return m_exponents[i] > m_exponents[j];
   }
private:
   const T* m_exponents;
};

template <class T, class Lanczos, class Policy>
T hypergeometric_pdf_lanczos_imp(T /*dummy*/, unsigned x, unsigned r, unsigned n, unsigned N, const Lanczos&, const Policy&)
{
   BOOST_MATH_STD_USING

   BOOST_MATH_INSTRUMENT_FPU
   BOOST_MATH_INSTRUMENT_VARIABLE(x);
   BOOST_MATH_INSTRUMENT_VARIABLE(r);
   BOOST_MATH_INSTRUMENT_VARIABLE(n);
   BOOST_MATH_INSTRUMENT_VARIABLE(N);
   BOOST_MATH_INSTRUMENT_VARIABLE(typeid(Lanczos).name());

   T bases[9] = {
      T(n) + static_cast<T>(Lanczos::g()) + 0.5f,
      T(r) + static_cast<T>(Lanczos::g()) + 0.5f,
      T(N - n) + static_cast<T>(Lanczos::g()) + 0.5f,
      T(N - r) + static_cast<T>(Lanczos::g()) + 0.5f,
      1 / (T(N) + static_cast<T>(Lanczos::g()) + 0.5f),
      1 / (T(x) + static_cast<T>(Lanczos::g()) + 0.5f),
      1 / (T(n - x) + static_cast<T>(Lanczos::g()) + 0.5f),
      1 / (T(r - x) + static_cast<T>(Lanczos::g()) + 0.5f),
      1 / (T(N - n - r + x) + static_cast<T>(Lanczos::g()) + 0.5f)
   };
   T exponents[9] = {
      n + T(0.5f),
      r + T(0.5f),
      N - n + T(0.5f),
      N - r + T(0.5f),
      N + T(0.5f),
      x + T(0.5f),
      n - x + T(0.5f),
      r - x + T(0.5f),
      N - n - r + x + T(0.5f)
   };
   int base_e_factors[9] = {
      -1, -1, -1, -1, 1, 1, 1, 1, 1
   };
   int sorted_indexes[9] = {
      0, 1, 2, 3, 4, 5, 6, 7, 8
   };
#ifdef BOOST_MATH_INSTRUMENT
   BOOST_MATH_INSTRUMENT_FPU
   for(unsigned i = 0; i < 9; ++i)
   {
      BOOST_MATH_INSTRUMENT_VARIABLE(i);
      BOOST_MATH_INSTRUMENT_VARIABLE(bases[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(exponents[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(base_e_factors[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(sorted_indexes[i]);
   }
#endif
   std::sort(sorted_indexes, sorted_indexes + 9, sort_functor<T>(exponents));
#ifdef BOOST_MATH_INSTRUMENT
   BOOST_MATH_INSTRUMENT_FPU
   for(unsigned i = 0; i < 9; ++i)
   {
      BOOST_MATH_INSTRUMENT_VARIABLE(i);
      BOOST_MATH_INSTRUMENT_VARIABLE(bases[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(exponents[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(base_e_factors[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(sorted_indexes[i]);
   }
#endif

   do{
      exponents[sorted_indexes[0]] -= exponents[sorted_indexes[1]];
      bases[sorted_indexes[1]] *= bases[sorted_indexes[0]];
      if((bases[sorted_indexes[1]] < tools::min_value<T>()) && (exponents[sorted_indexes[1]] != 0))
      {
         return 0;
      }
      base_e_factors[sorted_indexes[1]] += base_e_factors[sorted_indexes[0]];
      bubble_down_one(sorted_indexes, sorted_indexes + 9, sort_functor<T>(exponents));

#ifdef BOOST_MATH_INSTRUMENT
      for(unsigned i = 0; i < 9; ++i)
      {
         BOOST_MATH_INSTRUMENT_VARIABLE(i);
         BOOST_MATH_INSTRUMENT_VARIABLE(bases[i]);
         BOOST_MATH_INSTRUMENT_VARIABLE(exponents[i]);
         BOOST_MATH_INSTRUMENT_VARIABLE(base_e_factors[i]);
         BOOST_MATH_INSTRUMENT_VARIABLE(sorted_indexes[i]);
      }
#endif
   }while(exponents[sorted_indexes[1]] > 1);

   //
   // Combine equal powers:
   //
   int j = 8;
   while(exponents[sorted_indexes[j]] == 0) --j;
   while(j)
   {
      while(j && (exponents[sorted_indexes[j-1]] == exponents[sorted_indexes[j]]))
      {
         bases[sorted_indexes[j-1]] *= bases[sorted_indexes[j]];
         exponents[sorted_indexes[j]] = 0;
         base_e_factors[sorted_indexes[j-1]] += base_e_factors[sorted_indexes[j]];
         bubble_down_one(sorted_indexes + j, sorted_indexes + 9, sort_functor<T>(exponents));
         --j;
      }
      --j;

#ifdef BOOST_MATH_INSTRUMENT
      BOOST_MATH_INSTRUMENT_VARIABLE(j);
      for(unsigned i = 0; i < 9; ++i)
      {
         BOOST_MATH_INSTRUMENT_VARIABLE(i);
         BOOST_MATH_INSTRUMENT_VARIABLE(bases[i]);
         BOOST_MATH_INSTRUMENT_VARIABLE(exponents[i]);
         BOOST_MATH_INSTRUMENT_VARIABLE(base_e_factors[i]);
         BOOST_MATH_INSTRUMENT_VARIABLE(sorted_indexes[i]);
      }
#endif
   }

#ifdef BOOST_MATH_INSTRUMENT
   BOOST_MATH_INSTRUMENT_FPU
   for(unsigned i = 0; i < 9; ++i)
   {
      BOOST_MATH_INSTRUMENT_VARIABLE(i);
      BOOST_MATH_INSTRUMENT_VARIABLE(bases[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(exponents[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(base_e_factors[i]);
      BOOST_MATH_INSTRUMENT_VARIABLE(sorted_indexes[i]);
   }
#endif

   T result;
   BOOST_MATH_INSTRUMENT_VARIABLE(bases[sorted_indexes[0]] * exp(static_cast<T>(base_e_factors[sorted_indexes[0]])));
   BOOST_MATH_INSTRUMENT_VARIABLE(exponents[sorted_indexes[0]]);
   {
      BOOST_FPU_EXCEPTION_GUARD
      result = pow(bases[sorted_indexes[0]] * exp(static_cast<T>(base_e_factors[sorted_indexes[0]])), exponents[sorted_indexes[0]]);
   }
   BOOST_MATH_INSTRUMENT_VARIABLE(result);
   for(unsigned i = 1; (i < 9) && (exponents[sorted_indexes[i]] > 0); ++i)
   {
      BOOST_FPU_EXCEPTION_GUARD
      if(result < tools::min_value<T>())
         return 0; // short circuit further evaluation
      if(exponents[sorted_indexes[i]] == 1)
         result *= bases[sorted_indexes[i]] * exp(static_cast<T>(base_e_factors[sorted_indexes[i]]));
      else if(exponents[sorted_indexes[i]] == 0.5f)
         result *= sqrt(bases[sorted_indexes[i]] * exp(static_cast<T>(base_e_factors[sorted_indexes[i]])));
      else
         result *= pow(bases[sorted_indexes[i]] * exp(static_cast<T>(base_e_factors[sorted_indexes[i]])), exponents[sorted_indexes[i]]);

      BOOST_MATH_INSTRUMENT_VARIABLE(result);
   }

   result *= Lanczos::lanczos_sum_expG_scaled(static_cast<T>(n + 1))
      * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(r + 1))
      * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(N - n + 1))
      * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(N - r + 1))
      /
      ( Lanczos::lanczos_sum_expG_scaled(static_cast<T>(N + 1))
         * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(x + 1))
         * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(n - x + 1))
         * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(r - x + 1))
         * Lanczos::lanczos_sum_expG_scaled(static_cast<T>(N - n - r + x + 1)));

   BOOST_MATH_INSTRUMENT_VARIABLE(result);
   return result;
}

template <class T, class Policy>
T hypergeometric_pdf_lanczos_imp(T /*dummy*/, unsigned x, unsigned r, unsigned n, unsigned N, const boost::math::lanczos::undefined_lanczos&, const Policy& pol)
{
   BOOST_MATH_STD_USING
   return exp(
      boost::math::lgamma(T(n + 1), pol)
      + boost::math::lgamma(T(r + 1), pol)
      + boost::math::lgamma(T(N - n + 1), pol)
      + boost::math::lgamma(T(N - r + 1), pol)
      - boost::math::lgamma(T(N + 1), pol)
      - boost::math::lgamma(T(x + 1), pol)
      - boost::math::lgamma(T(n - x + 1), pol)
      - boost::math::lgamma(T(r - x + 1), pol)
      - boost::math::lgamma(T(N - n - r + x + 1), pol));
}

template <class T>
inline T integer_power(const T& x, int ex)
{
   if(ex < 0)
      return 1 / integer_power(x, -ex);
   switch(ex)
   {
   case 0:
      return 1;
   case 1:
      return x;
   case 2:
      return x * x;
   case 3:
      return x * x * x;
   case 4:
      return boost::math::pow<4>(x);
   case 5:
      return boost::math::pow<5>(x);
   case 6:
      return boost::math::pow<6>(x);
   case 7:
      return boost::math::pow<7>(x);
   case 8:
      return boost::math::pow<8>(x);
   }
   BOOST_MATH_STD_USING
#ifdef __SUNPRO_CC
   return pow(x, T(ex));
#else
   return pow(x, ex);
#endif
}
template <class T>
struct hypergeometric_pdf_prime_loop_result_entry
{
   T value;
   const hypergeometric_pdf_prime_loop_result_entry* next;
};

#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable:4510 4512 4610)
#endif

struct hypergeometric_pdf_prime_loop_data
{
   const unsigned x;
   const unsigned r;
   const unsigned n;
   const unsigned N;
   unsigned prime_index;
   unsigned current_prime;
};

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

template <class T>
T hypergeometric_pdf_prime_loop_imp(hypergeometric_pdf_prime_loop_data& data, hypergeometric_pdf_prime_loop_result_entry<T>& result)
{
   while(data.current_prime <= data.N)
   {
      unsigned base = data.current_prime;
      int prime_powers = 0;
      while(base <= data.N)
      {
         prime_powers += data.n / base;
         prime_powers += data.r / base;
         prime_powers += (data.N - data.n) / base;
         prime_powers += (data.N - data.r) / base;
         prime_powers -= data.N / base;
         prime_powers -= data.x / base;
         prime_powers -= (data.n - data.x) / base;
         prime_powers -= (data.r - data.x) / base;
         prime_powers -= (data.N - data.n - data.r + data.x) / base;
         base *= data.current_prime;
      }
      if(prime_powers)
      {
         T p = integer_power<T>(static_cast<T>(data.current_prime), prime_powers);
         if((p > 1) && (tools::max_value<T>() / p < result.value))
         {
            //
            // The next calculation would overflow, use recursion
            // to sidestep the issue:
            //
            hypergeometric_pdf_prime_loop_result_entry<T> t = { p, &result };
            data.current_prime = prime(++data.prime_index);
            return hypergeometric_pdf_prime_loop_imp<T>(data, t);
         }
         if((p < 1) && (tools::min_value<T>() / p > result.value))
         {
            //
            // The next calculation would underflow, use recursion
            // to sidestep the issue:
            //
            hypergeometric_pdf_prime_loop_result_entry<T> t = { p, &result };
            data.current_prime = prime(++data.prime_index);
            return hypergeometric_pdf_prime_loop_imp<T>(data, t);
         }
         result.value *= p;
      }
      data.current_prime = prime(++data.prime_index);
   }
   //
   // When we get to here we have run out of prime factors,
   // the overall result is the product of all the partial
   // results we have accumulated on the stack so far, these
   // are in a linked list starting with "data.head" and ending
   // with "result".
   //
   // All that remains is to multiply them together, taking
   // care not to overflow or underflow.
   //
   // Enumerate partial results >= 1 in variable i
   // and partial results < 1 in variable j:
   //
   hypergeometric_pdf_prime_loop_result_entry<T> const *i, *j;
   i = &result;
   while(i && i->value < 1)
      i = i->next;
   j = &result;
   while(j && j->value >= 1)
      j = j->next;

   T prod = 1;

   while(i || j)
   {
      while(i && ((prod <= 1) || (j == 0)))
      {
         prod *= i->value;
         i = i->next;
         while(i && i->value < 1)
            i = i->next;
      }
      while(j && ((prod >= 1) || (i == 0)))
      {
         prod *= j->value;
         j = j->next;
         while(j && j->value >= 1)
            j = j->next;
      }
   }

   return prod;
}

template <class T, class Policy>
inline T hypergeometric_pdf_prime_imp(unsigned x, unsigned r, unsigned n, unsigned N, const Policy&)
{
   hypergeometric_pdf_prime_loop_result_entry<T> result = { 1, 0 };
   hypergeometric_pdf_prime_loop_data data = { x, r, n, N, 0, prime(0) };
   return hypergeometric_pdf_prime_loop_imp<T>(data, result);
}

template <class T, class Policy>
T hypergeometric_pdf_factorial_imp(unsigned x, unsigned r, unsigned n, unsigned N, const Policy&)
{
   BOOST_MATH_STD_USING
   BOOST_ASSERT(N <= boost::math::max_factorial<T>::value);
   T result = boost::math::unchecked_factorial<T>(n);
   T num[3] = {
      boost::math::unchecked_factorial<T>(r),
      boost::math::unchecked_factorial<T>(N - n),
      boost::math::unchecked_factorial<T>(N - r)
   };
   T denom[5] = {
      boost::math::unchecked_factorial<T>(N),
      boost::math::unchecked_factorial<T>(x),
      boost::math::unchecked_factorial<T>(n - x),
      boost::math::unchecked_factorial<T>(r - x),
      boost::math::unchecked_factorial<T>(N - n - r + x)
   };
   int i = 0;
   int j = 0;
   while((i < 3) || (j < 5))
   {
      while((j < 5) && ((result >= 1) || (i >= 3)))
      {
         result /= denom[j];
         ++j;
      }
      while((i < 3) && ((result <= 1) || (j >= 5)))
      {
         result *= num[i];
         ++i;
      }
   }
   return result;
}

template <class T, class Policy>
inline typename tools::promote_args<T>::type
   hypergeometric_pdf(unsigned x, unsigned r, unsigned n, unsigned N, const Policy&)
{
   BOOST_FPU_EXCEPTION_GUARD
   typedef typename tools::promote_args<T>::type result_type;
   typedef typename policies::evaluation<result_type, Policy>::type value_type;
   typedef typename lanczos::lanczos<value_type, Policy>::type evaluation_type;
   typedef typename policies::normalise<
      Policy,
      policies::promote_float<false>,
      policies::promote_double<false>,
      policies::discrete_quantile<>,
      policies::assert_undefined<> >::type forwarding_policy;

   value_type result;
   if(N <= boost::math::max_factorial<value_type>::value)
   {
      //
      // If N is small enough then we can evaluate the PDF via the factorials
      // directly: table lookup of the factorials gives the best performance
      // of the methods available:
      //
      result = detail::hypergeometric_pdf_factorial_imp<value_type>(x, r, n, N, forwarding_policy());
   }
   else if(N <= boost::math::prime(boost::math::max_prime - 1))
   {
      //
      // If N is no larger than the largest prime number in our lookup table
      // (104729) then we can use prime factorisation to evaluate the PDF,
      // this is slow but accurate:
      //
      result = detail::hypergeometric_pdf_prime_imp<value_type>(x, r, n, N, forwarding_policy());
   }
   else
   {
      //
      // Catch all case - use the lanczos approximation - where available -
      // to evaluate the ratio of factorials.  This is reasonably fast
      // (almost as quick as using logarithmic evaluation in terms of lgamma)
      // but only a few digits better in accuracy than using lgamma:
      //
      result = detail::hypergeometric_pdf_lanczos_imp(value_type(), x, r, n, N, evaluation_type(), forwarding_policy());
   }

   if(result > 1)
   {
      result = 1;
   }
   if(result < 0)
   {
      result = 0;
   }

   return policies::checked_narrowing_cast<result_type, forwarding_policy>(result, "boost::math::hypergeometric_pdf<%1%>(%1%,%1%,%1%,%1%)");
}

}}} // namespaces

#endif


/******
6 成员函数模板
6.3 特化
有些资料上说目前的C++标准不允许在类模板之外全特化一个未被特化的类模板（指的是类模板A）的成员函数。
整体感觉：类模板中的成员函数全特化可能还不算太完善，编码时要注意测试。
在实际工作中，建议把这些特化版本写在类模板内部，然后类模板一般也要写在头文件。

7 类 / 类模板中的类模板（嵌套）

*****/

#include <iostream>
//#include <boost/type_index.hpp>

using namespace std;
//#pragma warning(disable : 4996)

namespace _nmsp1
{
	template <typename T1>
	class A
	{
	public:
		template <typename T2>
		A(T2 v1, T2 v2); //构造函数模板，引入了自己的模板参数T2，与类A的模板参数T1没有关系

	public:
		A(double v1, double v2)
		{
			cout << "A::A(double,double)执行了!" << endl;
		}
		A(T1 v1, T1 v2)
		{
			cout << "A::A(T1,T1)执行了!" << endl;
		}

	public:
		//拷贝构造函数模板
		template <typename U>
		A(const A<U>& other)
		//A(A<U>& other)
		{
			cout << "A::A(const A<U>& other)拷贝构造函数模板执行了!" << endl;
		}

		//拷贝赋值运算符模板
		template <typename U>
		A<T1>& operator=(const A<U>& other)
		//A<T1>& operator=(A<U>& other)
		{
			//.....
			cout << "operator=(const A<U>& other)拷贝赋值运算符模板执行了!" << endl;
			return *this;
		}

		////拷贝赋值运算符
		//A<T1>& operator=(const A<T1>& other)
		//{
		//	cout << "operator=(const A<T1>& other)拷贝赋值运算符执行了!" << endl;
		//	return *this;
		//}

		//---------------------------------
		/*template <typename T4>
		virtual void myvirfunc() {}*/
		virtual void myvirfunc2() {}

		//----------------------------------
		//template <typename T3>
		//void myft(T3 tmpt) //普通成员函数模板
		//{
		//	cout << tmpt << endl;
		//}
		template <typename T3, typename T4> //普通成员函数模板
		void myft(T3 tmpt,T4 tmpt2)
		{
			cout << "myft()泛化版本" << endl;
			cout << tmpt << endl;
			cout << tmpt2 << endl;
		}

		template <typename T4> //偏特化
		void myft(int tmpt, T4 tmpt2);
		/*{
			cout << "myft(int,T4)偏特化版本" << endl;
			cout << tmpt << endl;
			cout << tmpt2 << endl;
		}*/

		template <> //全特化
		void myft(int tmpt, float tmpt2);
		/*{
			cout << "myft(int,float)全特化版本" << endl;
			cout << tmpt << endl;
			cout << tmpt2 << endl;
		}*/

	public:
		template <typename U>
		class OtherC
		{
		public:
			void myfOC();
			/*{
				cout << "myfOC执行了" << endl;
			}*/
		};

		T1 m_ic;
		static constexpr int m_stcvalue = 200;
	};

	//在类外实现类模板的构造函数模板
	template <typename T1>
	template <typename T2>
	A<T1>::A(T2 v1, T2 v2)
	{
		cout << "A::A(T2,T2)执行了!" << endl;
	}

	//在类外实现类模板A的myft成员函数模板的偏特化版本
	template <typename T1>
	template <typename T4>
	void A<T1>::myft(int tmpt, T4 tmpt2)
	{
		cout << "myft(int,T4)偏特化版本" << endl;
		cout << tmpt << endl;
		cout << tmpt2 << endl;
	}

	//将myfOC实现在类外面
	template <typename T1>
	template <typename U>
	void A<T1>::OtherC<U>::myfOC()
	{
		cout << "类模板A的myfOC执行了" << endl;
	}

	//在类外实现类模板A的myft成员函数模板的全特化版本
	//template <typename T1>
	//template <> //全特化
	//void A<T1>::myft(int tmpt, float tmpt2)
	//{
	//	cout << "myft(int,float)全特化版本" << endl;
	//	cout << tmpt << endl;
	//	cout << tmpt2 << endl;
	//}

	//类模板A的全特化版本
	template <>
	class A<float>
	{
	public:
		template <typename T3, typename T4> //普通成员函数模板
		void myft(T3 tmpt, T4 tmpt2)
		{
			cout << "类A特化版本的myft()泛化版本" << endl;
			cout << tmpt << endl;
			cout << tmpt2 << endl;
		}
		//template <> //全特化
		//void myft(int tmpt, float tmpt2);

	public:
		template <typename U>
		class OtherC
		{
		public:
			void myfOC();
			/*{
				cout << "myfOC执行了" << endl;
			}*/
		};
	};

	//A<float>中有泛化版本的myft，因此不用在A<float>中声明如下的全特化版本。
	template <> //全特化
	void A<float>::myft(int tmpt, float tmpt2)
	{
		cout << "类A特化版本的myft(int,float)全特化版本" << endl;
		cout << tmpt << endl;
		cout << tmpt2 << endl;
	}

	//将myfOC实现在类外面
	template <typename U>
	void A<float>::OtherC<U>::myfOC()
	{
		cout << "myfOC执行了" << endl;
	}

}

int main()
{
	/*_nmsp1::A<float> a2(1, 2);
	a2.myft(3.1, 2);
	a2.myft(3, 2);
	a2.myft(3, 2.5f);*/

	/*_nmsp1::A<float> a3;
	a3.myft(3, 2.5f);
	a3.myft(3.1,2);*/

	/*_nmsp1::A<float>::OtherC<float> myobjc;
	myobjc.myfOC(); */

	_nmsp1::A<int>::OtherC<float> myobjc;
	myobjc.myfOC();

	return 0;
}


#ifndef BOOST_GEOMETRY_PROJECTIONS_ROUSS_HPP
#define BOOST_GEOMETRY_PROJECTIONS_ROUSS_HPP

// Boost.Geometry - extensions-gis-projections (based on PROJ4)
// This file is automatically generated. DO NOT EDIT.

// Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2017.
// Modifications copyright (c) 2017, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// PROJ4 is converted to Boost.Geometry by Barend Gehrels

// Last updated version of proj: 4.9.1

// Original copyright notice:

// Copyright (c) 2003, 2006   Gerald I. Evenden

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#include <boost/geometry/srs/projections/impl/base_static.hpp>
#include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
#include <boost/geometry/srs/projections/impl/projects.hpp>
#include <boost/geometry/srs/projections/impl/factory_entry.hpp>
#include <boost/geometry/srs/projections/impl/proj_mdist.hpp>

namespace boost { namespace geometry
{

namespace srs { namespace par4
{
    struct rouss {};

}} //namespace srs::par4

namespace projections
{
    #ifndef DOXYGEN_NO_DETAIL
    namespace detail { namespace rouss
    {
            template <typename T>
            struct par_rouss
            {
                T s0;
                T A1, A2, A3, A4, A5, A6;
                T B1, B2, B3, B4, B5, B6, B7, B8;
                T C1, C2, C3, C4, C5, C6, C7, C8;
                T D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11;
                MDIST<T> en;
            };

            // template class, using CRTP to implement forward/inverse
            template <typename CalculationType, typename Parameters>
            struct base_rouss_ellipsoid : public base_t_fi<base_rouss_ellipsoid<CalculationType, Parameters>,
                     CalculationType, Parameters>
            {

                typedef CalculationType geographic_type;
                typedef CalculationType cartesian_type;

                par_rouss<CalculationType> m_proj_parm;

                inline base_rouss_ellipsoid(const Parameters& par)
                    : base_t_fi<base_rouss_ellipsoid<CalculationType, Parameters>,
                     CalculationType, Parameters>(*this, par) {}

                // FORWARD(e_forward)  ellipsoid
                // Project coordinates from geographic (lon, lat) to cartesian (x, y)
                inline void fwd(geographic_type& lp_lon, geographic_type& lp_lat, cartesian_type& xy_x, cartesian_type& xy_y) const
                {
                    CalculationType s, al, cp, sp, al2, s2;

                    cp = cos(lp_lat);
                    sp = sin(lp_lat);
                    s = proj_mdist(lp_lat, sp, cp,  this->m_proj_parm.en) - this->m_proj_parm.s0;
                    s2 = s * s;
                    al = lp_lon * cp / sqrt(1. - this->m_par.es * sp * sp);
                    al2 = al * al;
                    xy_x = this->m_par.k0 * al*(1.+s2*(this->m_proj_parm.A1+s2*this->m_proj_parm.A4)-al2*(this->m_proj_parm.A2+s*this->m_proj_parm.A3+s2*this->m_proj_parm.A5
                                +al2*this->m_proj_parm.A6));
                    xy_y = this->m_par.k0 * (al2*(this->m_proj_parm.B1+al2*this->m_proj_parm.B4)+
                        s*(1.+al2*(this->m_proj_parm.B3-al2*this->m_proj_parm.B6)+s2*(this->m_proj_parm.B2+s2*this->m_proj_parm.B8)+
                        s*al2*(this->m_proj_parm.B5+s*this->m_proj_parm.B7)));
                }

                // INVERSE(e_inverse)  ellipsoid
                // Project coordinates from cartesian (x, y) to geographic (lon, lat)
                inline void inv(cartesian_type& xy_x, cartesian_type& xy_y, geographic_type& lp_lon, geographic_type& lp_lat) const
                {
                    CalculationType s, al, x = xy_x / this->m_par.k0, y = xy_y / this->m_par.k0, x2, y2;;

                    x2 = x * x;
                    y2 = y * y;
                    al = x*(1.-this->m_proj_parm.C1*y2+x2*(this->m_proj_parm.C2+this->m_proj_parm.C3*y-this->m_proj_parm.C4*x2+this->m_proj_parm.C5*y2-this->m_proj_parm.C7*x2*y)
                        +y2*(this->m_proj_parm.C6*y2-this->m_proj_parm.C8*x2*y));
                    s = this->m_proj_parm.s0 + y*(1.+y2*(-this->m_proj_parm.D2+this->m_proj_parm.D8*y2))+
                        x2*(-this->m_proj_parm.D1+y*(-this->m_proj_parm.D3+y*(-this->m_proj_parm.D5+y*(-this->m_proj_parm.D7+y*this->m_proj_parm.D11)))+
                        x2*(this->m_proj_parm.D4+y*(this->m_proj_parm.D6+y*this->m_proj_parm.D10)-x2*this->m_proj_parm.D9));
                    lp_lat=proj_inv_mdist(s, this->m_proj_parm.en);
                    s = sin(lp_lat);
                    lp_lon=al * sqrt(1. - this->m_par.es * s * s)/cos(lp_lat);
                }

                static inline std::string get_name()
                {
                    return "rouss_ellipsoid";
                }

            };

            // Roussilhe Stereographic
            template <typename Parameters, typename T>
            inline void setup_rouss(Parameters& par, par_rouss<T>& proj_parm)
            {
                T N0, es2, t, t2, R_R0_2, R_R0_4;

                if (!proj_mdist_ini(par.es, proj_parm.en))
                    BOOST_THROW_EXCEPTION( projection_exception(0) );
                es2 = sin(par.phi0);
                proj_parm.s0 = proj_mdist(par.phi0, es2, cos(par.phi0), proj_parm.en);
                t = 1. - (es2 = par.es * es2 * es2);
                N0 = 1./sqrt(t);
                R_R0_2 = t * t / par.one_es;
                R_R0_4 = R_R0_2 * R_R0_2;
                t = tan(par.phi0);
                t2 = t * t;
                proj_parm.C1 = proj_parm.A1 = R_R0_2 / 4.;
                proj_parm.C2 = proj_parm.A2 = R_R0_2 * (2 * t2 - 1. - 2. * es2) / 12.;
                proj_parm.A3 = R_R0_2 * t * (1. + 4. * t2)/ ( 12. * N0);
                proj_parm.A4 = R_R0_4 / 24.;
                proj_parm.A5 = R_R0_4 * ( -1. + t2 * (11. + 12. * t2))/24.;
                proj_parm.A6 = R_R0_4 * ( -2. + t2 * (11. - 2. * t2))/240.;
                proj_parm.B1 = t / (2. * N0);
                proj_parm.B2 = R_R0_2 / 12.;
                proj_parm.B3 = R_R0_2 * (1. + 2. * t2 - 2. * es2)/4.;
                proj_parm.B4 = R_R0_2 * t * (2. - t2)/(24. * N0);
                proj_parm.B5 = R_R0_2 * t * (5. + 4.* t2)/(8. * N0);
                proj_parm.B6 = R_R0_4 * (-2. + t2 * (-5. + 6. * t2))/48.;
                proj_parm.B7 = R_R0_4 * (5. + t2 * (19. + 12. * t2))/24.;
                proj_parm.B8 = R_R0_4 / 120.;
                proj_parm.C3 = R_R0_2 * t * (1. + t2)/(3. * N0);
                proj_parm.C4 = R_R0_4 * (-3. + t2 * (34. + 22. * t2))/240.;
                proj_parm.C5 = R_R0_4 * (4. + t2 * (13. + 12. * t2))/24.;
                proj_parm.C6 = R_R0_4 / 16.;
                proj_parm.C7 = R_R0_4 * t * (11. + t2 * (33. + t2 * 16.))/(48. * N0);
                proj_parm.C8 = R_R0_4 * t * (1. + t2 * 4.)/(36. * N0);
                proj_parm.D1 = t / (2. * N0);
                proj_parm.D2 = R_R0_2 / 12.;
                proj_parm.D3 = R_R0_2 * (2 * t2 + 1. - 2. * es2) / 4.;
                proj_parm.D4 = R_R0_2 * t * (1. + t2)/(8. * N0);
                proj_parm.D5 = R_R0_2 * t * (1. + t2 * 2.)/(4. * N0);
                proj_parm.D6 = R_R0_4 * (1. + t2 * (6. + t2 * 6.))/16.;
                proj_parm.D7 = R_R0_4 * t2 * (3. + t2 * 4.)/8.;
                proj_parm.D8 = R_R0_4 / 80.;
                proj_parm.D9 = R_R0_4 * t * (-21. + t2 * (178. - t2 * 26.))/720.;
                proj_parm.D10 = R_R0_4 * t * (29. + t2 * (86. + t2 * 48.))/(96. * N0);
                proj_parm.D11 = R_R0_4 * t * (37. + t2 * 44.)/(96. * N0);
            }

    }} // namespace detail::rouss
    #endif // doxygen

    /*!
        \brief Roussilhe Stereographic projection
        \ingroup projections
        \tparam Geographic latlong point type
        \tparam Cartesian xy point type
        \tparam Parameters parameter type
        \par Projection characteristics
         - Azimuthal
         - Ellipsoid
        \par Example
        \image html ex_rouss.gif
    */
    template <typename CalculationType, typename Parameters>
    struct rouss_ellipsoid : public detail::rouss::base_rouss_ellipsoid<CalculationType, Parameters>
    {
        inline rouss_ellipsoid(const Parameters& par) : detail::rouss::base_rouss_ellipsoid<CalculationType, Parameters>(par)
        {
            detail::rouss::setup_rouss(this->m_par, this->m_proj_parm);
        }
    };

    #ifndef DOXYGEN_NO_DETAIL
    namespace detail
    {

        // Static projection
        BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::par4::rouss, rouss_ellipsoid, rouss_ellipsoid)

        // Factory entry(s)
        template <typename CalculationType, typename Parameters>
        class rouss_entry : public detail::factory_entry<CalculationType, Parameters>
        {
            public :
                virtual base_v<CalculationType, Parameters>* create_new(const Parameters& par) const
                {
                    return new base_v_fi<rouss_ellipsoid<CalculationType, Parameters>, CalculationType, Parameters>(par);
                }
        };

        template <typename CalculationType, typename Parameters>
        inline void rouss_init(detail::base_factory<CalculationType, Parameters>& factory)
        {
            factory.add_to_factory("rouss", new rouss_entry<CalculationType, Parameters>);
        }

    } // namespace detail
    #endif // doxygen

} // namespace projections

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_PROJECTIONS_ROUSS_HPP