#ifdef COMPILATION// -*-indent-tabs-mode:t;c-basic-offset:4;tab-width:4;autowrap:nil;-*-
$CXXX $CXXFLAGS -O3 $0 -o $0.$X -lboost_unit_test_framework -lboost_timer \
`pkg-config --libs blas` \
`#-Wl,-rpath,/usr/local/Wolfram/Mathematica/12.0/SystemFiles/Libraries/Linux-x86-64 -L/usr/local/Wolfram/Mathematica/12.0/SystemFiles/Libraries/Linux-x86-64 -lmkl_intel_ilp64 -lmkl_sequential -lmkl_core` \
&&$0.$X&&rm $0.$X;exit
#endif
// © Alfredo A. Correa 2019-2020

#ifndef MULTI_ADAPTORS_BLAS_GEMM_HPP
#define MULTI_ADAPTORS_BLAS_GEMM_HPP

#include "../blas/core.hpp"

#include "../blas/numeric.hpp"
#include "../blas/operations.hpp"

#include "../blas/gemv.hpp"

#include "../../config/NODISCARD.hpp"
#include "../../config/MARK.hpp"

//#include "../../utility.hpp" // TODO include extensions only

namespace boost{
namespace multi{
namespace blas{

using core::gemm;

template<class It>
auto xbase_aux(It const& it, std::true_type const&)
->decltype(underlying(base(it))){
	return underlying(base(it));}

template<class It>
auto xbase_aux(It const& it, std::false_type const&)
->decltype(base(it)){
	return base(it);}

template<class It>
auto xbase(It const& it)
->decltype(xbase_aux(it, std::integral_constant<bool, is_conjugated<It>{}>{})){
	return xbase_aux(it, std::integral_constant<bool, is_conjugated<It>{}>{});}

template<class Context, class It2DA, class Size, class It2DB, class It2DC>
auto gemm_n(Context&& ctxt, typename It2DA::element alpha, It2DA a_first, Size a_count, It2DB b_first, typename It2DA::element beta, It2DC c_first)
->decltype(std::forward<Context>(ctxt).gemm('N', 'N', b_first->size(), a_count, a_first->size(), &alpha, xbase(b_first), b_first->size()  , xbase(a_first), a_first->size(), &beta, c_first.base(), c_first->size()  ), It2DC{})
try{
	assert( b_first->size() == c_first->size() );
	assert( a_first.stride()==1 or a_first->stride()==1 );
	assert( b_first.stride()==1 or b_first->stride()==1 );
	assert( c_first.stride()==1 or c_first->stride()==1 );

	if(a_count != 0){
		#define CTXT std::forward<Context>(ctxt)
		;;;;; if constexpr(!is_conjugated<It2DA>{} and !is_conjugated<It2DB>{}){
			;;;;; if(a_first->stride()==1 and b_first->stride()==1 and c_first->stride()==1){
				;;;; if( a_count==1 and b_first->size()==1 ){CTXT.gemm('N', 'N', b_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first->size()  , base(a_first), a_first->size() , &beta, base(c_first), c_first->size()  );}
				else if( a_count==1                        ){CTXT.gemm('N', 'N', b_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first. stride(), base(a_first), a_first->size()  , &beta, base(c_first), c_first->size()  );}
				else                                        {CTXT.gemm('N', 'N', b_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first. stride(), base(a_first), a_first. stride(), &beta, base(c_first), c_first. stride());}
			}else if(a_first->stride()==1 and b_first->stride()==1 and c_first. stride()==1){
				if  (a_count==1)        {CTXT.gemm('T', 'T', a_count, b_first->size(), a_first->size(), &alpha, base(a_first), a_first. stride(), base(b_first), b_first->size() , &beta, base(c_first), a_first->size()  );}
				else                    {CTXT.gemm('T', 'T', a_count, b_first->size(), a_first->size(), &alpha, base(a_first), a_first. stride(), base(b_first), b_first.stride(), &beta, base(c_first), c_first->stride());}
			}else if(a_first. stride()==1 and b_first->stride()==1 and c_first->stride()==1){
				if  (a_count==1)        {CTXT.gemm('N', 'T', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first. stride(), base(a_first), a_first->stride(), &beta, base(c_first), a_first->size()  );}
				else                    {CTXT.gemm('N', 'T', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first. stride(), base(a_first), a_first->stride(), &beta, base(c_first), c_first.stride());}
			}else if(a_first. stride()==1 and b_first->stride()==1 and c_first. stride()==1){
				if  (a_count==1)        {CTXT.gemm('N', 'T', a_count, b_first->size(), a_first->size(), &alpha, base(a_first), a_first->stride(), base(b_first), a_first->size()  , &beta, base(c_first), b_first->size()  );}
				else                    {CTXT.gemm('N', 'T', a_count, b_first->size(), a_first->size(), &alpha, base(a_first), a_first->stride(), base(b_first), b_first. stride(), &beta, base(c_first), c_first->stride());}
			}else if(a_first->stride()==1 and b_first.stride()==1 and c_first. stride()==1){
				;;;; if(a_count==1 and b_first->size()){CTXT.gemm('N', 'N', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first->size()  , base(a_first), a_first->size()  , &beta, base(c_first), c_first->stride());}
				else if(a_count==1)                    {CTXT.gemm('N', 'T', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first->stride(), base(a_first), a_first->size()  , &beta, base(c_first), c_first->stride());}
				else                                   {CTXT.gemm('N', 'T', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first->stride(), base(a_first), a_first.stride() , &beta, base(c_first), c_first->stride());}
			}else if(a_first->stride()==1 and b_first. stride()==1 and c_first->stride()==1){
				if  (a_count==1)        {CTXT.gemm('T', 'N', a_count, c_first->size(), a_first->size(), &alpha, base(b_first), b_first->stride(), base(a_first), a_first->size()  , &beta, base(c_first), c_first.stride());}
				else                    {CTXT.gemm('T', 'N', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first->stride(), base(a_first), a_first.stride(), &beta, base(c_first), c_first.stride());}
			}else if(a_first. stride()==1 and b_first.stride( )==1 and c_first. stride()==1){
				                        {CTXT.gemm('N', 'N', c_first->size(), a_count, a_first->size(), &alpha, base(a_first), a_first->stride(), base(b_first), b_first->stride(), &beta, base(c_first), c_first->stride());}
			}else if(a_first. stride()==1 and b_first.stride( )==1 and c_first->stride()==1){
				                        {CTXT.gemm('T', 'T', a_count, c_first->size(), a_first->size(), &alpha, base(b_first), b_first->stride(), base(a_first), a_first->stride(), &beta, base(c_first), c_first. stride());}
			}else assert(0);
		}else if constexpr(!is_conjugated<It2DA>{} and  is_conjugated<It2DB>{}){
			;;;;; if(a_first->stride()==1 and b_first->stride()==1 and c_first->stride()==1){
				if(b_first->size()==1)  {CTXT.gemm('N', 'C', c_first->size(), a_count, a_first->size(), &alpha, underlying(base(b_first)), b_first. stride(), base(a_first), a_count          , &beta, base(c_first), c_first.stride());}
				else                    {CTXT.gemm('N', 'C', c_first->size(), a_count, a_first->size(), &alpha, underlying(base(b_first)), b_first. stride(), base(a_first), a_first. stride(), &beta, base(c_first), c_first.stride());}
			}else if(a_first->stride()==1 and b_first. stride()==1 and c_first->stride()==1){
				if  (a_count==1)        {CTXT.gemm('C', 'N', a_count, c_first->size(), a_first->size(), &alpha, underlying(base(b_first)), b_first->stride(), base(a_first), a_first->size()  , &beta, base(c_first), c_first.stride());}
				else                    {CTXT.gemm('C', 'N', c_first->size(), a_count, a_first->size(), &alpha, underlying(base(b_first)), b_first->stride(), base(a_first), a_first.stride(), &beta, base(c_first), c_first.stride());}
			}else if(a_first->stride()==1 and b_first. stride()==1 and c_first. stride()==1){
				                        {CTXT.gemm('C', 'N', c_first->size(), a_count, a_first->size(), &alpha, underlying(base(b_first)), b_first->stride(), base(a_first), a_first. stride(), &beta, base(c_first), c_first->stride());}
			}else if(a_first. stride()==1 and b_first. stride()==1 and c_first. stride()==1){
				                        {CTXT.gemm('C', 'T', c_first->size(), a_count, a_first->size(), &alpha, underlying(base(b_first)), b_first->stride(), base(a_first), a_first->stride(), &beta, base(c_first), c_first->stride());}
			}else if(a_first. stride()==1 and b_first. stride()==1 and c_first->stride()==1){
				                        {CTXT.gemm('C', 'T', a_count, c_first->size(), a_first->size(), &alpha, underlying(base(b_first)), b_first->stride(), base(a_first), a_first->stride(), &beta, base(c_first), c_first. stride());}
			}else assert(0);
		}else if constexpr( is_conjugated<It2DA>{} and !is_conjugated<It2DB>{}){
			;;;;; if(a_first. stride()==1 and b_first->stride()==1 and c_first->stride()==1){
				if  (a_count==1)        {CTXT.gemm('N', 'C', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first. stride(), underlying(base(a_first)), a_first->stride(), &beta, base(c_first), a_first->size()  );}
				else                    {CTXT.gemm('N', 'C', c_first->size(), a_count, a_first->size(), &alpha, base(b_first), b_first. stride(), underlying(base(a_first)), a_first->stride(), &beta, base(c_first), c_first.stride());}
			}else assert(0);
		}else if constexpr( is_conjugated<It2DA>{} and  is_conjugated<It2DB>{}){
			;;;;; if(a_first. stride()==1 and b_first. stride()==1 and c_first->stride()==1){
				                        {CTXT.gemm('C', 'C', a_count, c_first->size(), a_first->size(), &alpha, underlying(base(b_first)), b_first->stride(), underlying(base(a_first)), a_first->stride(), &beta, base(c_first), c_first. stride());}
			}else assert(0);
		}
		#undef CTXT
	}
	return c_first + a_count;
}catch(std::logic_error& e){
	using std::to_string;
	throw std::logic_error{
		"couldn't do "+std::string(__PRETTY_FUNCTION__)+" of layout a_count="+std::to_string(a_count)
		+" a_strides="+to_string(a_first.stride())+","+to_string(a_first->stride())+" a->size="+to_string(a_first->size())
		+" b_strides="+to_string(b_first.stride())+","+to_string(b_first->stride())+" b->size="+to_string(b_first->size())
		+" c_strides="+to_string(c_first.stride())+","+to_string(c_first->stride())+" c->size="+to_string(c_first->size())
		+" because " + e.what()
	};
}

template<class It2DA, class Size, class It2DB, class It2DC, class Context = blas::context> // TODO automatic deduction of context
auto gemm_n(typename It2DA::element alpha, It2DA a_first, Size a_count, It2DB b_first, typename It2DA::element beta, It2DC c_first)
->decltype(gemm_n(Context{}, alpha, a_first, a_count, b_first, beta, c_first)){
	return gemm_n(Context{}, alpha, a_first, a_count, b_first, beta, c_first);}

template<class Context, class A, class B, class C>
C&& gemm(Context&& ctx, typename A::element alpha, A const& a, B const& b, typename A::element beta, C&& c)
//->decltype(ctx.gemm('N', 'T', size(~c), size(a), size(b), &alpha, gemm_base_aux(b), stride( b), gemm_base_aux(a), stride(~a), &beta, gemm_base_aux(c), size(b)) , std::forward<C>(c))
{
//	MULTI_MARK_SCOPE("multi::blas::gemm with context");

//	if(c.is_empty()){
//		assert(a.is_empty() and b.is_empty());
//		return std::forward<C>(c);
//	}

	assert( size( a) == size( c) );
	if(not a.is_empty()) assert( size(~a) == size( b) );
//	assert( size(~b) == size(~c) );

//	assert( stride(a)==1 or stride(~a)==1 );
//	assert( stride(b)==1 or stride(~b)==1 );
//	assert( stride(c)==1 or stride(~c)==1 );

//	if(stride(c)==1 and stride(~c)!=1){
//		blas::gemm(std::forward<Context>(ctx), alpha, ~b, ~a, beta, ~c);
//		return std::forward<C>(c);
//	}

	if constexpr(is_conjugated<C>{}) blas::gemm  (std::forward<Context>(ctx), conj(alpha), conj(a),           conj(b) , conj(beta), conj(c) );
	else{                            blas::gemm_n(std::forward<Context>(ctx),      alpha , begin(a), size(a), begin(b),      beta , begin(c));
//		;;;;; if constexpr(!is_conjugated<A>{} and !is_conjugated<B>{}){
//			;;;;; if(stride(~a)==1 and stride(~b)==1){
//				if(size(a)==1){std::forward<Context>(ctx).gemm('N', 'N', size(~c), size(a), size(b), &alpha, base(b), stride( b), base(a), size(b)   , &beta, base(c), size(b)  );}
//				else          {std::forward<Context>(ctx).gemm('N', 'N', size(~c), size(a), size(b), &alpha, base(b), stride( b), base(a), stride( a), &beta, base(c), stride(c));}
//			}else if(stride( a)==1 and stride(~b)==1){
//				if(size(a)==1){std::forward<Context>(ctx).gemm('N', 'T', size(~c), size(a), size(b), &alpha, base(b), stride( b), base(a), stride(~a), &beta, base(c), size(b)  );}
//				else          {std::forward<Context>(ctx).gemm('N', 'T', size(~c), size(a), size(b), &alpha, base(b), stride( b), base(a), stride(~a), &beta, base(c), stride(c));}
//			}else if(stride(~a)==1 and stride( b)==1){
//				if(size(a)==1){std::forward<Context>(ctx).gemm('T', 'N', size(~c), size(a), size(b), &alpha, base(b), stride(~b), base(a), size(b)   , &beta, base(c), stride(c));}
//				else          {std::forward<Context>(ctx).gemm('T', 'N', size(~c), size(a), size(b), &alpha, base(b), stride(~b), base(a), stride( a), &beta, base(c), stride(c));}
//			}else if(stride( a)==1 and stride( b)==1){
//				               std::forward<Context>(ctx).gemm('T', 'T', size(~c), size(a), size(b), &alpha, base(b), stride(~b), base(a), stride(~a), &beta, base(c), stride(c));
//			}else assert(0);
//		}else if constexpr(!is_conjugated<A>{} and  is_conjugated<B>{}){
//			;;;; if(stride(~a)==1 and stride( b)==1){std::forward<Context>(ctx).gemm('C', 'N', size(~c), size(a), size(b), &alpha, underlying(base(b)), stride(~b), base(a), stride( a), &beta, base(c), stride(c));}
//			else if(stride( a)==1 and stride( b)==1){std::forward<Context>(ctx).gemm('C', 'T', size(~c), size(a), size(b), &alpha, underlying(base(b)), stride(~b), base(a), stride(~a), &beta, base(c), stride(c));}
//			else assert(0);
//		}else if constexpr( is_conjugated<A>{} and !is_conjugated<B>{}){
//			;;;; if(stride( a)==1 and stride(~b)==1){std::forward<Context>(ctx).gemm('N', 'C', size(~c), size(a), size(b), &alpha, base(b), stride( b), underlying(base(a)), stride(~a), &beta, base(c), stride(c));}
//			else if(stride( a)==1 and stride( b)==1){std::forward<Context>(ctx).gemm('T', 'C', size(~c), size(a), size(b), &alpha, base(b), stride(~b), underlying(base(a)), stride(~a), &beta, base(c), stride(c));}
//			else assert(0);
//		}else if constexpr( is_conjugated<A>{} and  is_conjugated<B>{}){
//			;;;; if(stride( a)==1 and stride( b)==1){std::forward<Context>(ctx).gemm('C', 'C', size(~c), size(a), size(b), &alpha, underlying(base(b)), stride(~b), underlying(base(a)), stride(~a), &beta, base(c), stride(c));}
//			else assert(0&&"case not implemented in blas");
//		}
	}
	return std::forward<C>(c);
}

template<class A, class B, class C>
C&& gemm(typename A::element alpha, A const& a, B const& b, typename A::element beta, C&& c){
//->decltype(gemm('N', 'T', size(~c), size(a), size(b), &alpha, gemm_base_aux(b), stride( b), gemm_base_aux(a), stride(~a), &beta, gemm_base_aux(c), size(b)) , std::forward<C>(c)){
//	using multi::blas::default_allocator_of;
//	auto ctx = default_context_of(gemm_base_aux(a)); // ADL
	return gemm(blas::context{}, alpha, a, b, beta, std::forward<C>(c));
}

//template<class A2D, class B2D, class C2D = typename A2D::decay_type>
//NODISCARD("because input arguments are const")
//auto gemm(typename A2D::element a, A2D const& A, B2D const& B){
//	assert(get_allocator(A) == get_allocator(B));
//	return gemm(a, A, B, 0., C2D({size(A), size(rotated(B))}, get_allocator(A)));
//}

//template<class Context, class A2D, class B2D, class C2D = typename A2D::decay_type>
//NODISCARD("because input arguments are const")
//auto gemm(Context&& ctx, typename A2D::element a, A2D const& A, B2D const& B)
//->std::decay_t<decltype(gemm(std::forward<Context>(ctx), a, A, B, 0., C2D({size(A), size(rotated(B))}, get_allocator(A))))>{
//	assert(get_allocator(A) == get_allocator(B));
//	return gemm(std::forward<Context>(ctx), a, A, B, 0., C2D({size(A), size(rotated(B))}, get_allocator(A)));
//}

//template<class A2D, class B2D>
//auto gemm(A2D const& A, B2D const& B)
//->decltype(gemm(1., A, B)){
//	return gemm(1., A, B);}

//template<class Context, class A2D, class B2D, class = std::enable_if_t<blas::is_context<Context>{}> >
//auto gemm(Context&& ctx, A2D const& A, B2D const& B)
//->decltype(gemm(std::forward<Context>(ctx), 1., A, B)){
//	return gemm(std::forward<Context>(ctx), 1., A, B);}

template<class ContextPtr, class Scalar, class ItA, class ItB, class DecayType>
class gemm_range;

template<class Ext>
struct gemm_reference{
	Ext x;
	Ext const& extensions() const{return x;}
	friend Ext const& extensions(gemm_reference const& self){return self.extensions();}
};

template<class ContextPtr, class Scalar, class ItA, class ItB>
class gemm_iterator{
	ContextPtr ctxtp_;
	Scalar s_;
	ItA a_it_;
	ItB b_begin_;
	gemm_iterator(ContextPtr ctxtp, Scalar s, ItA a_it, ItB b_begin) : ctxtp_{ctxtp}, s_{s}, a_it_{a_it}, b_begin_{b_begin}{}
	template<class ContextPtr2, class Scalar2, class ItA2, class ItB2, class DecayType2>
	friend class gemm_range;
public:
	gemm_iterator(gemm_iterator const&) = default;
	using difference_type = typename std::iterator_traits<ItA>::difference_type;
	using value_type = typename std::iterator_traits<ItA>::value_type;
	using pointer = void;
	using reference = void;
	using iterator_category = std::random_access_iterator_tag;
//	using iterator_category = std::output_iterator_tag;
//	friend difference_type distance(gemv_iterator const& a, gemv_iterator const& b){assert(a.v_first_ == b.v_first_);
//		return b.m_it_ - a.m_it_;
//	}
	friend difference_type operator-(gemm_iterator const& a, gemm_iterator const& b){assert(a.b_begin_ == b.b_begin_);
		return a.a_it_ - b.a_it_;
	}
	friend bool operator==(gemm_iterator const& a, gemm_iterator const& b){return a.a_it_ == b.a_it_;}
	friend bool operator!=(gemm_iterator const& a, gemm_iterator const& b){return a.a_it_ != b.a_it_;}

	template<class ItOut>
	friend auto copy(gemm_iterator const& first, gemm_iterator const& last, ItOut d_first)
	->decltype(blas::gemm_n(*std::declval<ContextPtr>(), std::declval<Scalar>(), std::declval<ItA>(), std::declval<ItA>() - std::declval<ItA>(), std::declval<ItB>(), 0., d_first)) try{assert( first.s_ == last.s_ );
		return blas::gemm_n(*first.ctxtp_              , first.s_              , first.a_it_        , last.a_it_ - first.a_it_                 , first.b_begin_     , 0., d_first);
	}catch(std::exception const& e){
		throw std::logic_error(
			"in " + std::string(__PRETTY_FUNCTION__) + "\nCouldn't decay product of arrays of size " + std::to_string(last - first) +"x"+ std::to_string(first.a_it_->size()) + " and " +
			std::to_string(first.a_it_->size())+ "x" +std::to_string(first.b_begin_->size()) + " into " + std::to_string(last - first) +"x" + std::to_string(first.b_begin_->size()) +
			"\nbecause\n"+e.what()
		);
	}

	template<class ItOut>
	friend auto uninitialized_copy(gemm_iterator const& first, gemm_iterator const& last, ItOut const& d_first)
	->decltype(blas::gemm_n(*std::declval<ContextPtr>(), std::declval<Scalar>(), std::declval<ItA>(), std::declval<ItA>() - std::declval<ItA>(), std::declval<ItB>(), 0., d_first)) try{assert( first.s_ == last.s_ );
		return blas::gemm_n(*first.ctxtp_              , first.s_              , first.a_it_        , last.a_it_ - first.a_it_                 , first.b_begin_     , 0., d_first);
	}catch(std::exception const& e){
		throw std::logic_error(
			"in " + std::string(__PRETTY_FUNCTION__) + "\nCouldn't decay product of arrays of size " + std::to_string(last - first) +"x"+ std::to_string(first.a_it_->size()) + " and " +
			std::to_string(first.a_it_->size())+ "x" +std::to_string(first.b_begin_->size()) + " into " + std::to_string(last - first) +"x" + std::to_string(first.b_begin_->size()) +
			"\nbecause\n"+e.what()
		);
	}

	gemm_reference<decltype(b_begin_->extensions())> operator*() const{return {b_begin_->extensions()};}
};

template<class ContextPtr, class Scalar, class ItA, class ItB, class DecayType>
class gemm_range{
	ContextPtr ctxtp_;
	Scalar s_;
	ItA a_begin_;
	ItA a_end_;
	ItB b_begin_;
public:
	gemm_range(gemm_range const&) = delete;
	gemm_range(ContextPtr ctxtp, Scalar s, ItA a_first, ItA a_last, ItB b_first) : ctxtp_{ctxtp}, s_{s}, a_begin_{a_first}, a_end_{a_last}, b_begin_{b_first}{}
	using iterator = gemm_iterator<ContextPtr, Scalar, ItA, ItB>;
	using decay_type = DecayType;
	using size_type = typename decay_type::size_type;
	iterator begin() const{return {ctxtp_, s_, a_begin_, b_begin_};}
	iterator end()   const{return {ctxtp_, s_, a_end_  , b_begin_};}
	friend auto begin(gemm_range const& self){return self.begin();}
	friend auto end  (gemm_range const& self){return self.end  ();}
	size_type size() const{return a_end_ - a_begin_;}
	typename decay_type::extensions_type extensions() const{return size()*b_begin_->extensions();}
	friend auto extensions(gemm_range const& self){return self.extensions();}
//	operator decay_type() const{return decay_type(*this);} // do not use curly { }
	decay_type operator+() const{return *this;}
};

template<class ContextPtr, class Scalar, class A2D, class B2D, class=std::enable_if_t<is_context<decltype(*ContextPtr{})>{}> >
gemm_range<ContextPtr, Scalar, typename A2D::const_iterator, typename B2D::const_iterator, typename A2D::decay_type/*B2D*/>
gemm(ContextPtr ctxtp, Scalar s, A2D const& a, B2D const& b){
	return {ctxtp, s, begin(a), end(a), begin(b)};
}

//#pragma warning (disable:1011)
//#pragma diag_suppress 0117 //"implicit_return_from_non_void_function"
//#pragma diag_suppress 940 //"implicit_return_from_non_void_function"
template<               class Scalar, class A2D, class B2D>
auto gemm(                Scalar s, A2D const& a, B2D const& b){
	;;;; if constexpr(not is_conjugated<A2D>{})
		return blas::gemm(blas::default_context_of(a.base()), s, a, b);
	else if constexpr(    is_conjugated<A2D>{})
		return blas::gemm(blas::default_context_of(underlying(a.base())), s, a, b);
}

namespace operators{
	template<class A2D, class B2D>
	auto operator*(A2D const& A, B2D const& B)
	->decltype(+blas::gemm(1., A, B)){
		return +blas::gemm(1., A, B);}
}

}}}

#if not __INCLUDE_LEVEL__ // _TEST_MULTI_ADAPTORS_BLAS_GEMM

#define BOOST_TEST_MODULE "C++ Unit Tests for Multi gemm"
#define BOOST_TEST_DYN_LINK
#include<boost/test/unit_test.hpp>

#include "../../array.hpp"
#include "../../utility.hpp"

#include<complex>
#include<cassert>
#include<iostream>
#include<numeric>
#include<algorithm>
#include<random>

#include <boost/timer/timer.hpp>

#include "../blas/axpy.hpp"
#include "../blas/dot.hpp"
#include "../blas/nrm2.hpp"

namespace multi = boost::multi;

#endif
#endif


// Copyright 2019 Hans Dembinski
//
// Distributed under 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)

//[ guide_custom_accumulators_with_metadata

#include <boost/histogram.hpp>
#include <cmath>
#include <iostream>
#include <sstream>
#include <string>

int main() {
  using namespace boost::histogram;

  // derive custom accumulator from one of the builtins
  struct accumulator_with_metadata : accumulators::count<> {
    std::string meta; // custom meta data

    // arbitrary additional data and interface could be added here
  };

  // make 1D histogram with custom accmulator
  auto h = make_histogram_with(dense_storage<accumulator_with_metadata>(),
                               axis::integer<>(1, 4));

  // fill some weighted entries
  auto x = {1, 0, 2, 1};
  h.fill(x);

  // assigning meta data to two bins
  h[0].meta = "Foo";
  h[2].meta = "Bar";

  std::ostringstream os;
  for (auto&& x : indexed(h))
    os << x.bin() << " value " << x->value() << " meta " << x->meta << "\n";

  std::cout << os.str() << std::flush;
  assert(os.str() == "1 value 2 meta Foo\n"
                     "2 value 1 meta \n"
                     "3 value 0 meta Bar\n");
}

//]


/********************************************************************************
 *
 * This file is part of the Geneva library collection. The following license
 * applies to this file:
 *
 * ------------------------------------------------------------------------------
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ------------------------------------------------------------------------------
 *
 * Note that other files in the Geneva library collection may use a different
 * license. Please see the licensing information in each file.
 *
 ********************************************************************************
 *
 * Geneva was started by Dr. Rüdiger Berlich and was later maintained together
 * with Dr. Ariel Garcia under the auspices of Gemfony scientific. For further
 * information on Gemfony scientific, see http://www.gemfomy.eu .
 *
 * The majority of files in Geneva was released under the Apache license v2.0
 * in February 2020.
 *
 * See the NOTICE file in the top-level directory of the Geneva library
 * collection for a list of contributors and copyright information.
 *
 ********************************************************************************/

#pragma once

// Global checks, defines and includes needed for all of Geneva
#include "common/GGlobalDefines.hpp"

// Standard header files go here
#include <iostream>
#include <cmath>
#include <sstream>
#include <vector>
#include <tuple>
#include <type_traits>

// Boost header files go here
#include <boost/math/constants/constants.hpp>

// Geneva header files go here
#include "common/GParserBuilder.hpp"
#include "common/GCommonMathHelperFunctions.hpp"
#include "hap/GRandomT.hpp"
#include "geneva/GDoubleCollection.hpp"
#include "geneva/GConstrainedDoubleCollection.hpp"
#include "geneva/GDoubleObjectCollection.hpp"
#include "geneva/GConstrainedDoubleObjectCollection.hpp"
#include "geneva/GConstrainedDoubleObject.hpp"
#include "geneva/GDoubleGaussAdaptor.hpp"
#include "geneva/GDoubleBiGaussAdaptor.hpp"
#include "geneva/GParameterSet.hpp"
#include "geneva/GParameterSetMultiConstraint.hpp"
#include "geneva/GParameterSetFactory.hpp"

namespace Gem {
namespace Geneva {

/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
/**
 * This enum denotes the possible demo function types
 */
enum class solverFunction : Gem::Common::ENUMBASETYPE {
	 PARABOLA = 0,
	 NOISYPARABOLA = 1,
	 ROSENBROCK = 2,
	 ACKLEY = 3,
	 RASTRIGIN = 4,
	 SCHWEFEL = 5,
	 SALOMON = 6,
	 NEGPARABOLA = 7
};

const solverFunction MAXDEMOFUNCTION = solverFunction::NEGPARABOLA;

// Make sure solverFunction can be streamed
/** @brief Puts a Gem::Geneva::solverFunction into a stream. Needed also for boost::lexical_cast<> */
G_API_INDIVIDUALS std::ostream &operator<<(std::ostream &, const Gem::Geneva::solverFunction &);

/** @brief Reads a Gem::Geneva::solverFunction from a stream. Needed also for boost::lexical_cast<> */
G_API_INDIVIDUALS std::istream &operator>>(std::istream &, Gem::Geneva::solverFunction &);

/**
 * This enum describes different parameter types that may be used to fill the object with data
 */
enum class parameterType : Gem::Common::ENUMBASETYPE {
	 USEGDOUBLECOLLECTION = 0,
	 USEGCONSTRAINEDOUBLECOLLECTION = 1,
	 USEGDOUBLEOBJECTCOLLECTION = 2,
	 USEGCONSTRAINEDDOUBLEOBJECTCOLLECTION = 3,
	 USEGCONSTRAINEDDOUBLEOBJECT = 4
};

// Make sure parameterType can be streamed
/** @brief Puts a Gem::Geneva::parameterType into a stream. Needed also for boost::lexical_cast<> */
G_API_INDIVIDUALS std::ostream &operator<<(std::ostream &, const Gem::Geneva::parameterType &);

/** @brief Reads a Gem::Geneva::parameterType from a stream. Needed also for boost::lexical_cast<> */
G_API_INDIVIDUALS std::istream &operator>>(std::istream &, Gem::Geneva::parameterType &);

/**
 * This enum describes several ways of initializing the data collections
 */
enum class initMode : Gem::Common::ENUMBASETYPE {
	 INITRANDOM = 0 // random values for all variables
	 , INITPERIMETER = 1 // Uses a parameter set on the perimeter of the allowed or common value range
};

// Make sure initMode can be streamed
/** @brief Puts a Gem::Geneva::initMode into a stream. Needed also for boost::lexical_cast<> */
G_API_INDIVIDUALS std::ostream &operator<<(std::ostream &, const Gem::Geneva::initMode &);

/** @brief Reads a Gem::Geneva::initMode from a stream. Needed also for boost::lexical_cast<> */
G_API_INDIVIDUALS std::istream &operator>>(std::istream &, Gem::Geneva::initMode &);

/******************************************************************************/
// A number of default settings for the factory
const double GFI_DEF_ADPROB = 1.0;
const double GFI_DEF_ADAPTADPROB = 0.1;
const double GFI_DEF_MINADPROB = 0.05;
const double GFI_DEF_MAXADPROB = 1.;
const std::uint32_t GFI_DEF_ADAPTIONTHRESHOLD = 1;
const bool GFI_DEF_USEBIGAUSSIAN = false;
const double GFI_DEF_SIGMA1 = 0.025;
const double GFI_DEF_SIGMASIGMA1 = 0.2;
const double GFI_DEF_MINSIGMA1 = 0.001;
const double GFI_DEF_MAXSIGMA1 = 1;
const double GFI_DEF_SIGMA2 = 0.025;
const double GFI_DEF_SIGMASIGMA2 = 0.2;
const double GFI_DEF_MINSIGMA2 = 0.001;
const double GFI_DEF_MAXSIGMA2 = 1;
const double GFI_DEF_DELTA = 0.05;
const double GFI_DEF_SIGMADELTA = 0.2;
const double GFI_DEF_MINDELTA = 0.001;
const double GFI_DEF_MAXDELTA = 1.;
const std::size_t GFI_DEF_PARDIM = 2;
const double GFI_DEF_MINVAR = -10.;
const double GFI_DEF_MAXVAR = 10.;
const bool GFI_DEF_USECONSTRAINEDDOUBLECOLLECTION = false;
const parameterType GFI_DEF_PARAMETERTYPE = parameterType::USEGCONSTRAINEDDOUBLEOBJECT;
const initMode GFI_DEF_INITMODE = initMode::INITPERIMETER;
const solverFunction GO_DEF_EVALFUNCTION = solverFunction::PARABOLA;
const double GFI_DEF_CROSSOVERPROB = 0.5;

/******************************************************************************/
// Forward declaraion
class GFunctionIndividualFactory;

/******************************************************************************/
/**
 * This individual searches for a minimum of a number of predefined functions, each capable
 * of processing their input in multiple dimensions.
 */
class GFunctionIndividual : public GParameterSet
{
	 ///////////////////////////////////////////////////////////////////////
	 friend class boost::serialization::access;

	 template<class Archive>
	 void serialize(Archive &ar, const unsigned int) {
		 ar
		 & BOOST_SERIALIZATION_BASE_OBJECT_NVP(GParameterSet)
		 & BOOST_SERIALIZATION_NVP(demoFunction_);
	 }

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

public:
	 using FACTORYTYPE = GFunctionIndividualFactory;

	 /** @brief The default constructor */
	 G_API_INDIVIDUALS GFunctionIndividual() = default;
	 /** @brief Initialization with the desired demo function */
	 explicit G_API_INDIVIDUALS GFunctionIndividual(const solverFunction &);
	 /** @brief A standard copy constructor */
	 G_API_INDIVIDUALS GFunctionIndividual(const GFunctionIndividual& cp) = default;

	 /** @brief The standard destructor */
	 G_API_INDIVIDUALS ~GFunctionIndividual() override = default;

	 /** @brief Allows to set the demo function */
	 G_API_INDIVIDUALS void setDemoFunction(solverFunction);
	 /** @brief Allows to retrieve the current demo function */
	 G_API_INDIVIDUALS solverFunction getDemoFunction() const;

	 /** @brief Allows to cross check the parameter size */
	 G_API_INDIVIDUALS std::size_t getParameterSize() const;

	 //---------------------------------------------------------------------------
	 /**
	  * This function converts the function id to a string representation. This is a convenience
	  * function that is mostly used in GArgumentParser.cpp of various Geneva examples.
	  *
	  * @param df The id of the desired function individual
	  * @return A string representing the name of the current function
	  */
	 static G_API_INDIVIDUALS std::string getStringRepresentation(const solverFunction &df) {
		 std::string result;

		 // Set up a single function individual, depending on the expected function type
		 switch (df) {
			 case solverFunction::PARABOLA:
				 result = "Parabola";
				 break;
			 case solverFunction::NOISYPARABOLA:
				 result = "Berlich noisy parabola";
				 break;
			 case solverFunction::ROSENBROCK:
				 result = "Rosenbrock";
				 break;
			 case solverFunction::ACKLEY:
				 result = "Ackley";
				 break;
			 case solverFunction::RASTRIGIN:
				 result = "Rastrigin";
				 break;
			 case solverFunction::SCHWEFEL:
				 result = "Schwefel";
				 break;
			 case solverFunction::SALOMON:
				 result = "Salomon";
				 break;
			 case solverFunction::NEGPARABOLA:
				 result = "Negative parabola";
				 break;
		 }

		 return result;
	 }

	 //---------------------------------------------------------------------------
	 /**
	  * Retrieves a string in ROOT format (see http://root.cern.ch) of the 2D version of a
	  * given function.
	  *
	  * @param df The id of the desired function individual
	  * @return A string suitable for plotting a 2D version of this function with the ROOT analysis framework
	  */
	 static G_API_INDIVIDUALS std::string get2DROOTFunction(const solverFunction &df) {
		 std::string result;

		 // Set up a single function individual, depending on the expected function type
		 switch (df) {
			 case solverFunction::PARABOLA:
				 result = "x^2 + y^2";
				 break;
			 case solverFunction::NOISYPARABOLA:
				 result = "(cos(x^2 + y^2) + 2.) * (x^2 + y^2)";
				 break;
			 case solverFunction::ROSENBROCK:
				 result = "100.*(x^2 - y)^2 + (1 - x)^2";
				 break;
			 case solverFunction::ACKLEY:
				 result = "exp(-0.2)*sqrt(x^2 + y^2) + 3.*(cos(2.*x) + sin(2.*y))";
				 break;
			 case solverFunction::RASTRIGIN:
				 result = "20.+(x^2 - 10.*cos(2*pi*x)) + (y^2 - 10.*cos(2*pi*y))";
				 break;
			 case solverFunction::SCHWEFEL:
				 result = "-0.5*(x*sin(sqrt(abs(x))) + y*sin(sqrt(abs(y))))";
				 break;
			 case solverFunction::SALOMON:
				 result = "-cos(2.*pi*sqrt(x^2 + y^2)) + 0.1*sqrt(x^2 + y^2) + 1.";
				 break;
			 case solverFunction::NEGPARABOLA:
				 result = "-(x^2 + y^2)";
				 break;
		 }

		 return result;
	 }

	 //---------------------------------------------------------------------------
	 /**
	  * Retrieves the minimum x-value(s) of a given (2D) demo function
	  *
	  * @param df The id of the desired function individual
	  * @return The x-coordinate(s) of the global optimium in 2D
	  */
	 static G_API_INDIVIDUALS std::vector<double> getXMin(const solverFunction &df) {
		 std::vector<double> result;

		 // Set up a single function individual, depending on the expected function type
		 switch (df) {
			 case solverFunction::PARABOLA:
				 result.push_back(0.);
				 break;
			 case solverFunction::NOISYPARABOLA:
				 result.push_back(0.);
				 break;
			 case solverFunction::ROSENBROCK:
				 result.push_back(1.);
				 break;
			 case solverFunction::ACKLEY:
				 // two global optima
				 result.push_back(-1.5096201);
				 result.push_back(1.5096201);
				 break;
			 case solverFunction::RASTRIGIN:
				 result.push_back(0.);
				 break;
			 case solverFunction::SCHWEFEL:
				 result.push_back(420.968746);
				 break;
			 case solverFunction::SALOMON:
				 result.push_back(0.);
				 break;
			 case solverFunction::NEGPARABOLA:
				 result.push_back(0.);
				 break;
		 }

		 return result;
	 }

	 //---------------------------------------------------------------------------
	 /**
	  * Retrieves the minimum y-value(s) of a given (2D) demo function
	  *
	  * @param df The id of the desired function individual
	  * @return The y-coordinate(s) of the global optimium in 2D
	  */
	 static G_API_INDIVIDUALS std::vector<double> getYMin(const solverFunction &df) {
		 std::vector<double> result;

		 // Set up a single function individual, depending on the expected function type
		 switch (df) {
			 case solverFunction::PARABOLA:
				 result.push_back(0.);
				 break;
			 case solverFunction::NOISYPARABOLA:
				 result.push_back(0.);
				 break;
			 case solverFunction::ROSENBROCK:
				 result.push_back(1.);
				 break;
			 case solverFunction::ACKLEY:
				 result.push_back(-0.7548651);
				 break;
			 case solverFunction::RASTRIGIN:
				 result.push_back(0.);
				 break;
			 case solverFunction::SCHWEFEL:
				 result.push_back(420.968746);
				 break;
			 case solverFunction::SALOMON:
				 result.push_back(0.);
				 break;
			 case solverFunction::NEGPARABOLA:
				 result.push_back(0.);
				 break;
		 }

		 return result;
	 }

protected:
	 //---------------------------------------------------------------------------
	 /** @brief Adds local configuration options to a GParserBuilder object */
	 G_API_INDIVIDUALS void addConfigurationOptions_(Gem::Common::GParserBuilder &) override;
	 /** @brief Loads the data of another GFunctionIndividual */
	 G_API_INDIVIDUALS void load_(const GObject *) final;

	/** @brief Allow access to this classes compare_ function */
	friend void Gem::Common::compare_base_t<GFunctionIndividual>(
		GFunctionIndividual const &
		, GFunctionIndividual const &
		, Gem::Common::GToken &
	);

	/** @brief Searches for compliance with expectations with respect to another object of the same type */
	G_API_INDIVIDUALS void compare_(
		const GObject & // the other object
		, const Gem::Common::expectation & // the expectation for this object, e.g. equality
		, const double & // the limit for allowed deviations of floating point types
	) const final;

	 /** @brief The actual value calculation takes place here */
	 G_API_INDIVIDUALS double fitnessCalculation() final;

	 //---------------------------------------------------------------------------

	 /** @brief Applies modifications to this object. */
	 G_API_INDIVIDUALS bool modify_GUnitTests_() override;
	 /** @brief Performs self tests that are expected to succeed. */
	 G_API_INDIVIDUALS void specificTestsNoFailureExpected_GUnitTests_() override;
	 /** @brief Performs self tests that are expected to fail. */
	 G_API_INDIVIDUALS void specificTestsFailuresExpected_GUnitTests_() override;

private:
	 //---------------------------------------------------------------------------
	 /** @brief Creates a deep clone of this object */
	 G_API_INDIVIDUALS GObject *clone_() const final;

	 //---------------------------------------------------------------------------
	 // Data

	 solverFunction demoFunction_ = solverFunction::PARABOLA; ///< Specifies which demo function should be used
};

/******************************************************************************/
/**
 * Provide an easy way to print the individual's content
 */
G_API_INDIVIDUALS std::ostream &operator<<(std::ostream &, const Gem::Geneva::GFunctionIndividual &);

G_API_INDIVIDUALS std::ostream &operator<<(std::ostream &, std::shared_ptr <Gem::Geneva::GFunctionIndividual>);

/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
/**
 * A factory for GFunctionIndividual objects
 */
class GFunctionIndividualFactory
	: public GParameterSetFactory
{
	 ///////////////////////////////////////////////////////////////////////
	 friend class boost::serialization::access;

	 template<class Archive>
	 void serialize(Archive &ar, const unsigned int) {
		 ar
		 & BOOST_SERIALIZATION_BASE_OBJECT_NVP(GParameterSetFactory)
		 & BOOST_SERIALIZATION_NVP(adProb_)
		 & BOOST_SERIALIZATION_NVP(adaptAdProb_)
		 & BOOST_SERIALIZATION_NVP(minAdProb_)
		 & BOOST_SERIALIZATION_NVP(maxAdProb_)
		 & BOOST_SERIALIZATION_NVP(adaptionThreshold_)
		 & BOOST_SERIALIZATION_NVP(useBiGaussian_)
		 & BOOST_SERIALIZATION_NVP(sigma1_)
		 & BOOST_SERIALIZATION_NVP(sigmaSigma1_)
		 & BOOST_SERIALIZATION_NVP(minSigma1_)
		 & BOOST_SERIALIZATION_NVP(maxSigma1_)
		 & BOOST_SERIALIZATION_NVP(sigma2_)
		 & BOOST_SERIALIZATION_NVP(sigmaSigma2_)
		 & BOOST_SERIALIZATION_NVP(minSigma2_)
		 & BOOST_SERIALIZATION_NVP(maxSigma2_)
		 & BOOST_SERIALIZATION_NVP(delta_)
		 & BOOST_SERIALIZATION_NVP(sigmaDelta_)
		 & BOOST_SERIALIZATION_NVP(minDelta_)
		 & BOOST_SERIALIZATION_NVP(maxDelta_)
		 & BOOST_SERIALIZATION_NVP(parDim_)
		 & BOOST_SERIALIZATION_NVP(minVar_)
		 & BOOST_SERIALIZATION_NVP(maxVar_)
		 & BOOST_SERIALIZATION_NVP(pT_)
		 & BOOST_SERIALIZATION_NVP(iM_);
	 }

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

public:
	 /** @brief The standard constructor */
	 explicit G_API_INDIVIDUALS GFunctionIndividualFactory(boost::filesystem::path const&);
	 /** @brief The copy constructor */
	 G_API_INDIVIDUALS GFunctionIndividualFactory(const GFunctionIndividualFactory &cp) = default;

	 /** @brief The destructor */
	 G_API_INDIVIDUALS ~GFunctionIndividualFactory() override = default;

	 //---------------------------------------------------------------------------
	 // Getters and setters

	 /** @brief Allows to retrieve the adaptionThreshold_ variable */
	 G_API_INDIVIDUALS std::uint32_t getAdaptionThreshold() const;
	 /** @brief Set the value of the adaptionThreshold_ variable */
	 G_API_INDIVIDUALS void setAdaptionThreshold(std::uint32_t adaptionThreshold);

	 /** @brief Allows to retrieve the adProb_ variable */
	 G_API_INDIVIDUALS double getAdProb() const;
	 /** @brief Set the value of the adProb_ variable */
	 G_API_INDIVIDUALS void setAdProb(double adProb);

	 /** @brief Allows to retrieve the iM_ variable */
	 G_API_INDIVIDUALS initMode getIM() const;
	 /** @brief Set the value of the iM_ variable */
	 G_API_INDIVIDUALS void setIM(initMode im);

	 /** @brief Allows to retrieve the parDim_ variable */
	 G_API_INDIVIDUALS std::size_t getParDim() const;
	 /** @brief (Re-)Set the dimension of the function */
	 G_API_INDIVIDUALS void setParDim(std::size_t);

	 /** @brief Allows to retrieve the pT_ variable */
	 G_API_INDIVIDUALS parameterType getPT() const;
	 /** @brief Set the value of the pT_ variable */
	 G_API_INDIVIDUALS void setPT(parameterType pt);

	 /** @brief Allows to retrieve the useBiGaussian_ variable */
	 G_API_INDIVIDUALS bool getUseBiGaussian() const;
	 /** @brief Set the value of the useBiGaussian_ variable */
	 G_API_INDIVIDUALS void setUseBiGaussian(bool useBiGaussian);

	 /** @brief Allows to retrieve the minVar_ variable */
	 G_API_INDIVIDUALS double getMinVar() const;
	 /** @brief Allows to retrieve the maxVar_ variable */
	 G_API_INDIVIDUALS double getMaxVar() const;
	 /** @brief Extract the minimum and maximum boundaries of the variables */
	 G_API_INDIVIDUALS std::tuple<double, double> getVarBoundaries() const;
	 /** @brief Set the minimum and maximum boundaries of the variables */
	 G_API_INDIVIDUALS void setVarBoundaries(std::tuple<double, double>);

	 /** @brief Allows to retrieve the delta_ variable */
	 G_API_INDIVIDUALS double getDelta() const;
	 /** @brief Set the value of the delta_ variable */
	 G_API_INDIVIDUALS void setDelta(double delta);
	 /** @brief Allows to retrieve the minDelta_ variable */
	 G_API_INDIVIDUALS double getMinDelta() const;
	 /** @brief Allows to retrieve the maxDelta_ variable */
	 G_API_INDIVIDUALS double getMaxDelta() const;
	 /** @brief Allows to retrieve the allowed value range of delta */
	 G_API_INDIVIDUALS std::tuple<double, double> getDeltaRange() const;
	 /** @brief Allows to set the allowed value range of delta */
	 G_API_INDIVIDUALS void setDeltaRange(std::tuple<double, double>);

	 /** @brief Allows to retrieve the minSigma1_ variable */
	 G_API_INDIVIDUALS double getMinSigma1() const;
	 /** @brief Allows to retrieve the maxSigma1_ variable */
	 G_API_INDIVIDUALS double getMaxSigma1() const;
	 /** @brief Allows to retrieve the allowed value range of sigma1_ */
	 G_API_INDIVIDUALS std::tuple<double, double> getSigma1Range() const;
	 /** @brief Allows to set the allowed value range of sigma1_ */
	 G_API_INDIVIDUALS void setSigma1Range(std::tuple<double, double>);

	 /** @brief Allows to retrieve the minSigma2_ variable */
	 G_API_INDIVIDUALS double getMinSigma2() const;
	 /** @brief Allows to retrieve the maxSigma2_ variable */
	 G_API_INDIVIDUALS double getMaxSigma2() const;
	 /** @brief Allows to retrieve the allowed value range of sigma2_ */
	 G_API_INDIVIDUALS std::tuple<double, double> getSigma2Range() const;
	 /** @brief Allows to set the allowed value range of sigma2_ */
	 G_API_INDIVIDUALS void setSigma2Range(std::tuple<double, double>);

	 /** @brief Allows to retrieve the sigma1_ variable */
	 G_API_INDIVIDUALS double getSigma1() const;
	 /** @brief Set the value of the sigma1_ variable */
	 G_API_INDIVIDUALS void setSigma1(double sigma1);

	 /** @brief Allows to retrieve the sigma2_ variable */
	 G_API_INDIVIDUALS double getSigma2() const;
	 /** @brief Set the value of the sigma2_ variable */
	 G_API_INDIVIDUALS void setSigma2(double sigma2);

	 /** @brief Allows to retrieve the sigmaDelta_ variable */
	 G_API_INDIVIDUALS double getSigmaDelta() const;
	 /** @brief Set the value of the sigmaDelta_ variable */
	 G_API_INDIVIDUALS void setSigmaDelta(double sigmaDelta);

	 /** @brief Allows to retrieve the sigmaSigma1_ variable */
	 G_API_INDIVIDUALS double getSigmaSigma1() const;
	 /** @brief Set the value of the sigmaSigma1_ variable */
	 G_API_INDIVIDUALS void setSigmaSigma1(double sigmaSigma1);

	 /** @brief Allows to retrieve the sigmaSigma2_ variable */
	 G_API_INDIVIDUALS double getSigmaSigma2() const;
	 /** @brief Set the value of the sigmaSigma2_ variable */
	 G_API_INDIVIDUALS void setSigmaSigma2(double sigmaSigma2);

	 /** @brief Allows to retrieve the rate of evolutionary adaption of adProb_ */
	 G_API_INDIVIDUALS double getAdaptAdProb() const;
	 /** @brief Allows to specify an adaption factor for adProb_ (or 0, if you do not want this feature) */
	 G_API_INDIVIDUALS void setAdaptAdProb(double adaptAdProb);

	 /** @brief Allows to retrieve the allowed range for adProb_ variation */
	 G_API_INDIVIDUALS std::tuple<double, double> getAdProbRange() const;
	 /** @brief Allows to set the allowed range for adaption probability variation */
	 G_API_INDIVIDUALS void setAdProbRange(double minAdProb, double maxAdProb);

	 // End of public getters and setters
	 //--------------------------------------------------------------------------

	 /** @brief Loads the data of another GFunctionIndividualFactory object */
	 G_API_INDIVIDUALS void load(std::shared_ptr <Gem::Common::GFactoryT<GParameterSet>>) override;
	 /** @brief Creates a deep clone of this object */
	 G_API_INDIVIDUALS std::shared_ptr <Gem::Common::GFactoryT<GParameterSet>> clone() const override;

protected:
	 /** @brief Allows to describe local configuration options in derived classes */
	 G_API_INDIVIDUALS void describeLocalOptions_(Gem::Common::GParserBuilder &) override;
	 /** @brief Allows to act on the configuration options received from the configuration file */
	 G_API_INDIVIDUALS void postProcess_(std::shared_ptr<GParameterSet> &) override;

private:
     /** @brief Creates individuals of this type */
     G_API_INDIVIDUALS std::shared_ptr <GParameterSet> getObject_(Gem::Common::GParserBuilder &, const std::size_t &) override;

	 /** @brief Set the value of the minVar_ variable */
	 void setMinVar(double minVar);

	 /** @brief Set the value of the maxVar_ variable */
	 void setMaxVar(double maxVar);

	 /** @brief Set the value of the minDelta_ variable */
	 void setMinDelta(double minDelta);

	 /** @brief Set the value of the maxDelta_ variable */
	 void setMaxDelta(double maxDelta);

	 /** @brief Set the value of the minSigma1_ variable */
	 void setMinSigma1(double minSigma1);

	 /** @brief Set the value of the maxSigma1_ variable */
	 void setMaxSigma1(double maxSigma1);

	 /** @brief Set the value of the minSigma2_ variable */
	 void setMinSigma2(double minSigma2);

	 /** @brief Set the value of the maxSigma2_ variable */
	 void setMaxSigma2(double maxSigma2);

	 /** @brief The default constructor; Only needed for (de-)serialization purposes. */
	 GFunctionIndividualFactory();

	 Gem::Common::GOneTimeRefParameterT<double> adProb_{GFI_DEF_ADPROB};
	 Gem::Common::GOneTimeRefParameterT<double> adaptAdProb_{GFI_DEF_ADAPTADPROB};
	 Gem::Common::GOneTimeRefParameterT<double> minAdProb_{GFI_DEF_MINADPROB};
	 Gem::Common::GOneTimeRefParameterT<double> maxAdProb_{GFI_DEF_MAXADPROB};
	 Gem::Common::GOneTimeRefParameterT<std::uint32_t> adaptionThreshold_{GFI_DEF_ADAPTIONTHRESHOLD};
	 Gem::Common::GOneTimeRefParameterT<bool> useBiGaussian_{GFI_DEF_USEBIGAUSSIAN};
	 Gem::Common::GOneTimeRefParameterT<double> sigma1_{GFI_DEF_SIGMA1};
	 Gem::Common::GOneTimeRefParameterT<double> sigmaSigma1_{GFI_DEF_SIGMASIGMA1};
	 Gem::Common::GOneTimeRefParameterT<double> minSigma1_{GFI_DEF_MINSIGMA1};
	 Gem::Common::GOneTimeRefParameterT<double> maxSigma1_{GFI_DEF_MAXSIGMA1};
	 Gem::Common::GOneTimeRefParameterT<double> sigma2_{GFI_DEF_SIGMA2};
	 Gem::Common::GOneTimeRefParameterT<double> sigmaSigma2_{GFI_DEF_SIGMASIGMA2};
	 Gem::Common::GOneTimeRefParameterT<double> minSigma2_{GFI_DEF_MINSIGMA2};
	 Gem::Common::GOneTimeRefParameterT<double> maxSigma2_{GFI_DEF_MAXSIGMA2};
	 Gem::Common::GOneTimeRefParameterT<double> delta_{GFI_DEF_DELTA};
	 Gem::Common::GOneTimeRefParameterT<double> sigmaDelta_{GFI_DEF_SIGMADELTA};
	 Gem::Common::GOneTimeRefParameterT<double> minDelta_{GFI_DEF_MINDELTA};
	 Gem::Common::GOneTimeRefParameterT<double> maxDelta_{GFI_DEF_MAXDELTA};
	 Gem::Common::GOneTimeRefParameterT<std::size_t> parDim_{GFI_DEF_PARDIM};
	 Gem::Common::GOneTimeRefParameterT<double> minVar_{GFI_DEF_MINVAR};
	 Gem::Common::GOneTimeRefParameterT<double> maxVar_{GFI_DEF_MAXVAR};
	 Gem::Common::GOneTimeRefParameterT<parameterType> pT_{GFI_DEF_PARAMETERTYPE};
	 Gem::Common::GOneTimeRefParameterT<initMode> iM_{GFI_DEF_INITMODE};
};

/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
/**
 * A simple constraint checker searching for valid solutions that fulfill
 * a given constraint. Here, the sum of all double variables needs to be smaller
 * than a given constant.
 */
class GDoubleSumConstraint : public GParameterSetConstraint {
	 ///////////////////////////////////////////////////////////////////////
	 friend class boost::serialization::access;

	 template<typename Archive>
	 void serialize(Archive &ar, const unsigned int) {
		 using boost::serialization::make_nvp;
		 ar
		 & BOOST_SERIALIZATION_BASE_OBJECT_NVP(GParameterSetConstraint)
		 & BOOST_SERIALIZATION_NVP(C_);
	 }
	 ///////////////////////////////////////////////////////////////////////
public:

	 /** @brief The default constructor */
	 G_API_INDIVIDUALS GDoubleSumConstraint() = default;
	 /** @brief Initialization with the constant */
	 explicit G_API_INDIVIDUALS GDoubleSumConstraint(const double &);
	 /** @brief The copy constructor */
	 G_API_INDIVIDUALS GDoubleSumConstraint(const GDoubleSumConstraint &cp) = default;

	 /** @brief The destructor */
	 G_API_INDIVIDUALS ~GDoubleSumConstraint() override = default;

protected:
	 G_API_INDIVIDUALS double check_(const GParameterSet *) const override;

	 /** @brief Adds local configuration options to a GParserBuilder object */
	 G_API_INDIVIDUALS void addConfigurationOptions_(Gem::Common::GParserBuilder&) override;
	 /** @brief Loads the data of another GParameterSetMultiConstraint */
	 G_API_INDIVIDUALS void load_(const GObject *) override;

	/** @brief Allow access to this classes compare_ function */
	friend void Gem::Common::compare_base_t<GDoubleSumConstraint>(
		GDoubleSumConstraint const &
		, GDoubleSumConstraint const &
		, Gem::Common::GToken &
	);

	/** @brief Searches for compliance with expectations with respect to another object of the same type */
	G_API_INDIVIDUALS void compare_(
		const GObject & // the other object
		, const Gem::Common::expectation & // the expectation for this object, e.g. equality
		, const double & // the limit for allowed deviations of floating point types
	) const final;

private:
	 /** @brief Creates a deep clone of this object */
	 G_API_INDIVIDUALS GObject *clone_() const override;

	 double C_ = 1.; ///< The constant that should not be exceeded by the sum of parameters
};

/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
/**
 * A constraint checker trying to enforce a condition x+y+z=C (note the equal
 * sign!) for double variables
 */
class GDoubleSumGapConstraint : public GParameterSetConstraint {
	 ///////////////////////////////////////////////////////////////////////
	 friend class boost::serialization::access;

	 template<typename Archive>
	 void serialize(Archive &ar, const unsigned int) {
		 using boost::serialization::make_nvp;
		 ar
		 & BOOST_SERIALIZATION_BASE_OBJECT_NVP(GParameterSetConstraint)
		 & BOOST_SERIALIZATION_NVP(C_)
		 & BOOST_SERIALIZATION_NVP(gap_);
	 }
	 ///////////////////////////////////////////////////////////////////////
public:

	 /** @brief The default constructor */
	 G_API_INDIVIDUALS GDoubleSumGapConstraint() = default;
	 /** @brief Initialization with the constant */
	 G_API_INDIVIDUALS GDoubleSumGapConstraint(const double &, const double &);
	 /** @brief The copy constructor */
	 G_API_INDIVIDUALS GDoubleSumGapConstraint(const GDoubleSumGapConstraint& cp) = default;

	 /** @brief The destructor */
	 G_API_INDIVIDUALS ~GDoubleSumGapConstraint() override = default;

protected:
	 G_API_INDIVIDUALS double check_(const GParameterSet *) const override;

	 /** @brief Adds local configuration options to a GParserBuilder object */
	 G_API_INDIVIDUALS void addConfigurationOptions_(Gem::Common::GParserBuilder &) override;
	 /** @brief Loads the data of another GParameterSetMultiConstraint */
	 G_API_INDIVIDUALS void load_(const GObject *) override;

	/** @brief Allow access to this classes compare_ function */
	friend void Gem::Common::compare_base_t<GDoubleSumGapConstraint>(
		GDoubleSumGapConstraint const &
		, GDoubleSumGapConstraint const &
		, Gem::Common::GToken &
	);

	/** @brief Searches for compliance with expectations with respect to another object of the same type */
	G_API_INDIVIDUALS void compare_(
		const GObject & // the other object
		, const Gem::Common::expectation & // the expectation for this object, e.g. equality
		, const double & // the limit for allowed deviations of floating point types
	) const final;

private:
	 /** @brief Creates a deep clone of this object */
	 G_API_INDIVIDUALS GObject *clone_() const override;

	 double C_ = 1.; ///< The constant that should not be exceeded by the sum of parameters
	 double gap_ = 0.5; ///< A tolerance around C_ that is still considered to be valid
};

/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
/**
 * A simple constraint checker searching for valid solutions that fulfill
 * a given constraint. Here, valid solutions lie in a sphere around 0
 */
class GSphereConstraint : public GParameterSetConstraint {
	 ///////////////////////////////////////////////////////////////////////
	 friend class boost::serialization::access;

	 template<typename Archive>
	 void serialize(Archive &ar, const unsigned int) {
		 using boost::serialization::make_nvp;
		 ar
		 &BOOST_SERIALIZATION_BASE_OBJECT_NVP(GParameterSetConstraint);
	 }
	 ///////////////////////////////////////////////////////////////////////
public:

	 /** @brief The default constructor */
	 G_API_INDIVIDUALS GSphereConstraint() = default;
	 /** @brief Initialization with the diameter */
	 explicit G_API_INDIVIDUALS GSphereConstraint(const double &cp);
	 /** @brief The copy constructor */
	 G_API_INDIVIDUALS GSphereConstraint(const GSphereConstraint &) = default;

	 /** @brief The destructor */
	 G_API_INDIVIDUALS ~GSphereConstraint() override = default;

protected:
	 G_API_INDIVIDUALS double check_(const GParameterSet *) const override;

	 /** @brief Adds local configuration options to a GParserBuilder object */
	 G_API_INDIVIDUALS void addConfigurationOptions_(Gem::Common::GParserBuilder &) override;
	 /** @brief Loads the data of another GParameterSetMultiConstraint */
	 G_API_INDIVIDUALS void load_(const GObject *) override;

	/** @brief Allow access to this classes compare_ function */
	friend void Gem::Common::compare_base_t<GSphereConstraint>(
		GSphereConstraint const &
		, GSphereConstraint const &
		, Gem::Common::GToken &
	);

	/** @brief Searches for compliance with expectations with respect to another object of the same type */
	G_API_INDIVIDUALS void compare_(
		const GObject & // the other object
		, const Gem::Common::expectation & // the expectation for this object, e.g. equality
		, const double & // the limit for allowed deviations of floating point types
	) const final;

private:
	 /** @brief Creates a deep clone of this object */
	 G_API_INDIVIDUALS GObject *clone_() const override;

	 /** @brief The diameter of the sphere */
	 double diameter_ = 1.;
};

/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
} /* namespace Geneva */
} /* namespace Gem */

BOOST_CLASS_EXPORT_KEY(Gem::Geneva::GFunctionIndividual)
BOOST_CLASS_EXPORT_KEY(Gem::Geneva::GFunctionIndividualFactory)
BOOST_CLASS_EXPORT_KEY(Gem::Geneva::GDoubleSumConstraint)
BOOST_CLASS_EXPORT_KEY(Gem::Geneva::GDoubleSumGapConstraint)
BOOST_CLASS_EXPORT_KEY(Gem::Geneva::GSphereConstraint)


#pragma once

#include <functional>
#include <memory>
#include <vector>
#include <boost/serialization/access.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>

#include "Utility/Vector3D.hpp"

namespace CraftWorld {
	template<typename Type>
	class Grid {
			friend boost::serialization::access;

			/**
			 * Serializes the Chunk.
			 * @tparam ArchiveType The type of archive used for serialization.
			 * @param archive The archive used for serialization.
			 * @param version The version of the serialization algorithm.
			 */
			template<typename ArchiveType>
			void serialize(ArchiveType& archive, const unsigned int& version) {
				archive & BOOST_SERIALIZATION_NVP(entities);
			}

		public:
			/**
			 * The entities contained within the Grid.
			 */
			std::vector<std::vector<std::vector<std::shared_ptr<Type>>>> entities;

			/**
			 * Creates a new Grid.
			 * @param size The amount of entities the Grid should be able to contain on each axis.
			 */
			Grid(const Utility::Vector3D<int>& size = { 0, 0, 0 }) : entities(
				std::vector<std::vector<std::vector<std::shared_ptr<Type>>>>(
					size.x,
					std::vector<std::vector<std::shared_ptr<Type>>>(
						size.y,
						std::vector<std::shared_ptr<Type>>(
							size.z,
							std::shared_ptr<Type>()
						)
					)
				)
			) {
			}

			/**
			 * Swaps the entities at the two positions.
			 * @param first The first position.
			 * @param second The second position.
			 */
			void swap(const Utility::Vector3D<int>& first, const Utility::Vector3D<int>& second) {
				std::shared_ptr<Type> temporary = entities[second.x][second.y][second.z];
				entities[second.x][second.y][second.z] = entities[first.x][first.y][first.z];
				entities[first.x][first.y][first.z] = temporary;
			}

			/**
			 * Gets the amount of entities in the Grid.
			 * @return The amount of entities.
			 */
			Utility::Vector3D<int> getSize() const {
				Utility::Vector3D<int> result;

				result.x = static_cast<int>(entities.size());

				for(const auto& yAxis : entities) {
					if(yAxis.size() > result.y) {
						result.y = static_cast<int>(yAxis.size());
					}

					for(const auto& zAxis : yAxis) {
						if(zAxis.size() > result.z) {
							result.z = static_cast<int>(zAxis.size());
						}
					}
				}

				return result;
			}

			/**
			 * Loops all entities in the grid.
			 * @param function The function to perform for all entities.
			 */
			void forEach(const std::function<void(std::shared_ptr<Type>&)>& function) {
				for(auto& yAxis : entities) {
					for(auto& zAxis : yAxis) {
						for(auto& entity : zAxis) {
							function(entity);
						}
					}
				}
			}
	};
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <typeinfo>

#include <boost/type_traits/is_same.hpp>
#include <boost/numeric/mtl/mtl.hpp>

template <typename Matrix>
void compressed2D_check(const Matrix&)
{
    throw "Matrix should be compressed2D!\n";
}

template <typename Value, typename Parameter>
void compressed2D_check(const mtl::compressed2D<Value, Parameter>&) {}

template <typename Matrix>
void dense2D_check(const Matrix&)
{
    throw "Matrix should be dense2D!\n";
}

template <typename Value, typename Parameter>
void dense2D_check(const mtl::dense2D<Value, Parameter>&) {}

int main(int, char**)
{
    using namespace std;
    using namespace mtl;
    using mtl::io::tout;

#if defined(MTL_WITH_VARIADIC_TEMPLATE) && defined(MTL_WITH_STATICASSERT) && defined(MTL_WITH_TEMPLATE_ALIAS)
    matrix<double, sparse> B;
    tout << "Type of B is " << typeid(B).name() << '\n';
    compressed2D_check(B);
    static_assert(traits::is_row_major<decltype(B)>::value, "Matrix must be row-major!");

    matrix<double, sparse, as_size_type<unsigned> > C;
    tout << "Type of C is " << typeid(C).name() << '\n';
    compressed2D_check(C);

    matrix<double, sparse, column_major> D;
    tout << "Type of D is " << typeid(D).name() << '\n';
    compressed2D_check(D);
    static_assert(!traits::is_static<decltype(D)>::value, "Matrix must not have static size!");
    static_assert(!traits::is_row_major<decltype(D)>::value, "Matrix must be column-major!");

    matrix<double, sparse, column_major, as_size_type<int>, dim<3, 5> > E;
    tout << "Type of E is " << typeid(E).name() << '\n';
    compressed2D_check(E);
    static_assert(traits::is_static<decltype(E)>::value, "Matrix must have static size!");
    static_assert(boost::is_same<mtl::Collection<decltype(E)>::size_type, int>::value,
		  "Size type must be int!");

    matrix<double> F;
    tout << "Type of F is " << typeid(F).name() << '\n';
    dense2D_check(F);
    static_assert(traits::is_row_major<decltype(F)>::value, "Matrix must be row-major!");

    matrix<double, as_size_type<unsigned> > G;
    tout << "Type of G is " << typeid(G).name() << '\n';
    dense2D_check(G);

    matrix<double, column_major> I;
    tout << "Type of I is " << typeid(I).name() << '\n';
    dense2D_check(I);
    static_assert(!traits::is_static<decltype(I)>::value, "Matrix must not have static size!");
    static_assert(!traits::is_row_major<decltype(I)>::value, "Matrix must be column-major!");

    matrix<double, column_major, as_size_type<int>, dim<3, 5> > J;
    tout << "Type of J is " << typeid(J).name() << '\n';
    dense2D_check(J);
    static_assert(traits::is_static<decltype(J)>::value, "Matrix must have static size!");

    matrix<double, dim<3, 3> > K;
    tout << "Type of K is " << typeid(K).name() << '\n';
    dense2D_check(K);
    static_assert(traits::is_static<decltype(K)>::value, "Matrix must have static size!");

    matrix<double, banded>     L;
    tout << "Type of L is " << typeid(L).name() << '\n';
    static_assert(traits::is_sparse<decltype(L)>::value, "Matrix must be sparse!"); // Will be changed in the future

    matrix<double, coordinate>     M(7, 8);
    tout << "Type of M is " << typeid(M).name() << '\n';
    static_assert(traits::is_sparse<decltype(M)>::value, "Matrix must be sparse!");

    matrix<double, ellpack>     N;
    tout << "Type of N is " << typeid(N).name() << '\n';
    static_assert(traits::is_sparse<decltype(N)>::value, "Matrix must be sparse!");

    matrix<double, morton>     O;
    tout << "Type of O is " << typeid(O).name() << '\n';
    static_assert(!traits::is_sparse<decltype(O)>::value, "Matrix must be dense!");

    matrix<double, morton, dim<3, 5> >     P;
    tout << "Type of P is " << typeid(P).name() << '\n';
    static_assert(!traits::is_sparse<decltype(P)>::value, "Matrix must be dense!");

#else
    std::cout << "Test deactivated due to missing C++11 features.\n";
#endif

    return 0;
}


#ifndef PRINT_HPP
#define PRINT_HPP

#include <iostream>
#include <iomanip>
#include <boost/numeric/bindings/begin.hpp>
#include <boost/numeric/bindings/end.hpp>
#include <boost/numeric/bindings/size.hpp>
#include <boost/numeric/bindings/trans_tag.hpp>
#include <boost/numeric/bindings/data_order.hpp>
#include <boost/numeric/bindings/blas/detail/default_order.hpp>

template<typename Vec>
class printable_vec_cl {
  const Vec &v;
public:
  explicit printable_vec_cl(const Vec &v) : v(v) {
  }
  const Vec & get() const {
    return v;
  }
};

template<typename Vec>
printable_vec_cl<Vec> print_vec(const Vec &v) {
  return printable_vec_cl<Vec>(v);
}

template<typename Mat>
class printable_mat_cl {
  const Mat &v;
public:
  explicit printable_mat_cl(const Mat &v) : v(v) {
  }
  const Mat & get() const {
    return v;
  }
};

template<typename Mat>
printable_mat_cl<Mat> print_mat(const Mat &v) {
  return printable_mat_cl<Mat>(v);
}

template<typename CharT, typename Traits, typename Vec>
std::basic_ostream<CharT, Traits> & operator<<(std::basic_ostream<CharT, Traits>& out,
					       const printable_vec_cl<Vec> &v) {
  namespace bindings=boost::numeric::bindings;
  auto i=bindings::begin(v.get());
  auto i_end=bindings::end(v.get());
  for (; i!=i_end; ++i)
    out << std::setprecision(3) << std::fixed << (*i) << "  ";
  return out;
}

template<typename CharT, typename Traits, typename Mat>
std::basic_ostream<CharT, Traits> & operator<<(std::basic_ostream<CharT, Traits>& out,
					       const printable_mat_cl<Mat> &M) {
  namespace bindings=boost::numeric::bindings;
  typedef typename bindings::blas::detail::default_order< Mat >::type order;
  typedef typename bindings::result_of::trans_tag< Mat, order >::type trans;
  for (int i=0; i<bindings::size_row_op(M.get(), trans()); ++i) {
    for (int j=0; j<bindings::size_column_op(M.get(), trans()); ++j)
      out << std::setprecision(3) << std::fixed << M.get()(i, j) << "  ";
    out << '\n';
  }
  return out;
}

#endif


#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/karma.hpp>

#include "quoted_printable.hpp"

namespace wandbox {
namespace quoted_printable {

	namespace qi = boost::spirit::qi;
	namespace karma = boost::spirit::karma;

	std::string decode(const std::string &r) {
		auto ite = r.begin();
		std::string ret;
		qi::parse(ite, r.end(), *(qi::omit[qi::lit('\n')] | (qi::char_ - '=') | (qi::lit("=\n")) | (qi::lit('=') > qi::uint_parser<char, 16, 2, 2>())), ret);
		return ret;
	}
	std::string encode(const std::string &r) {
		if (r.begin() == r.end()) return {};
		std::string ret;
		karma::generate(back_inserter(ret), karma::repeat(1, 76)[&karma::char_('=') << "=3D" | karma::print | ('=' << karma::upper[karma::right_align(2, '0')[karma::uint_generator<unsigned char, 16>()]])] % "=\n", r);
		return ret;
	}
}
}


#include "../reflect/common.hpp"
#include "cpgf/goutmain.h"
#include <ct/interop/cpgf.hpp>
#ifdef HAVE_OPENCV
#include <ct/types/opencv.hpp>
#endif

#ifdef HAVE_EIGEN3
#include <Eigen/Core>
#endif

namespace ct
{
    template <class T, int ROWS, int COLS, int OPTS, int MAXROWS, int MAXCOLS>
    struct Reflect<Eigen::Matrix<T, ROWS, COLS, OPTS, MAXROWS, MAXCOLS>>
    {
        using DataType = Eigen::Matrix<T, ROWS, COLS, OPTS, MAXROWS, MAXCOLS>;
        using Base = VariadicTypedef<void>;

        static constexpr index_t I0 = 0;
        static constexpr const index_t REFLECT_COUNT_BEGIN = __COUNTER__;

        static constexpr const char* getTypeName() { return "Eigen::Matrix<T, ROWS, COLS, OPTS, MAXROWS, MAXCOLS>"; }

        MEMBER_FUNCTION(data,
                        static_cast<const T* (DataType::*)() const>(&DataType::data),
                        static_cast<T* (DataType::*)()>(&DataType::data))
        MEMBER_FUNCTION(rows)
        MEMBER_FUNCTION(cols)
REFLECT_END
;
}

G_AUTO_RUN_BEFORE_MAIN()
{
    using namespace cpgf;
    ct::registerReflectedClass<ReflectedStruct>();
    ct::registerReflectedClass<Inherited>();

    ct::registerReflectedClass<Composite>();
    ct::registerReflectedClass<TestA>();
    ct::registerReflectedClass<TestB>();
    ct::registerReflectedClass<TestC>();
    ct::registerReflectedClass<TestVec>();

#ifdef HAVE_OPENCV
    ct::registerReflectedClass<cv::Rect2f>("Rect2f");
    ct::registerReflectedClass<cv::Rect2i>("Rect2i");
#endif
#ifdef HAVE_EIGEN3
    ct::registerReflectedClass<Eigen::Matrix2f>("Eigen::Matrix2f");
#endif
}

template <class T>
void test()
{
    using namespace cpgf;
    auto type = createMetaType<T>();
    auto cls = findMetaClass(type);
    if (cls)
    {
        std::cout << cls << std::endl;
    }
}

int main()
{
    test<ReflectedStruct>();
    test<Inherited>();
    test<Composite>();
    test<TestA>();
    test<TestB>();
    test<TestC>();
    test<TestVec>();

#ifdef HAVE_OPENCV
    test<cv::Rect2f>();
    test<cv::Rect2i>();
#endif
#ifdef HAVE_EIGEN3
    test<Eigen::Matrix2f>();
#endif
}


#include <boost/program_options.hpp>
#include <cstdio>
#include <iostream>
// own includes ------------------------------------------------------------
#include <fft/fft2_r2c.hpp>
#include "base/eigen2hdf.hpp"
#include "base/init.hpp"
#include "base/timer.hpp"
#include "fft/planner.hpp"

using namespace std;

namespace po = boost::program_options;

// --------------------------------------------------------------------------------
void test_r2c(po::variables_map vm)
{
  RDTSCTimer timer;
  typedef PlannerR2C planner_t;
  typedef FFTr2c<planner_t> fft_t;
  typedef fft_t::array_t array_t;
  typedef fft_t::complex_array_t complex_array_t;

  const int nrep = vm["nrep"].as<int>();
  std::vector<int> N0 = {64, 128, 256, 512, 1024, 2048};

  fft_t fft;
  auto& planner = fft.get_plan();
  if (vm.count("measure")) {
    cout << "FFTW_MEASURE"
         << "\n";
    planner.set_flags(FFTW_MEASURE);
  } else {
    cout << "FFTW_ESTIMATE"
         << "\n";
    planner.set_flags(FFTW_ESTIMATE);
  }

  for (unsigned int i = 0; i < N0.size(); ++i) {
    planner.create_and_get_plan(N0[i], N0[i], planner_t::FWD, ft_type::R2C);
    planner.create_and_get_plan(N0[i], N0[i], planner_t::INV, ft_type::R2C);
  }

  for (unsigned int i = 0; i < N0.size(); ++i) {
    int n = N0[i];
    array_t X(n, n);
    X.setRandom();
    complex_array_t Y(n, n);
    timer.start();
    for (int jj = 0; jj < nrep; ++jj) {
      fft.ft(Y, X, false);
    }
    auto tlap = timer.stop();
    timer.start();
    array_t X2(n, n);
    for (int jj = 0; jj < nrep; ++jj) {
      fft.ift(X2, Y);
    }
    auto tlap_inv = timer.stop();

    timer.print(cout, tlap / nrep, "FFT size " + boost::lexical_cast<string>(n));
    timer.print(cout, tlap_inv / nrep, "IFFT size " + boost::lexical_cast<string>(n));
    auto Diff = X2 - X;
    cout << "diff: " << Diff.abs().sum() << "\n";
  }
}

// --------------------------------------------------------------------------------
void test_c2c(po::variables_map vm)
{
  RDTSCTimer timer;
  typedef PlannerR2C planner_t;
  typedef FFTr2c<planner_t> fft_t;
  typedef fft_t::complex_array_t complex_array_t;

  std::vector<int> N0 = {64, 128, 256, 512, 1024, 2048};
  const int nrep = vm["nrep"].as<int>();
  fft_t fft;
  auto& planner = fft.get_plan();
  if (vm.count("measure")) {
    cout << "FFTW_MEASURE"
         << "\n";
    planner.set_flags(FFTW_MEASURE);
  } else {
    cout << "FFTW_ESTIMATE"
         << "\n";
    planner.set_flags(FFTW_ESTIMATE);
  }

  for (unsigned int i = 0; i < N0.size(); ++i) {
    planner.create_and_get_plan(N0[i], N0[i], planner_t::FWD, ft_type::C2C);
    planner.create_and_get_plan(N0[i], N0[i], planner_t::INV, ft_type::C2C);
  }
  planner.print(cout);

  for (unsigned int i = 0; i < N0.size(); ++i) {
    int n = N0[i];
    complex_array_t X(n, n);
    X.setRandom();
    complex_array_t Y(n, n);
    timer.start();
    for (int jj = 0; jj < nrep; ++jj) {
      fft.ft(Y, X, false);
    }
    auto tlap = timer.stop();
    complex_array_t X2(n, n);
    timer.start();
    for (int jj = 0; jj < nrep; ++jj) {
      fft.ift(X2, Y);
    }
    auto tlap_inv = timer.stop();
    auto Diff = X2 - X;
    cout << "diff: " << Diff.abs().sum() << "\n";

    timer.print(cout, tlap / nrep, "FFT size " + boost::lexical_cast<string>(n));
    timer.print(cout, tlap_inv / nrep, "IFFT size " + boost::lexical_cast<string>(n));
  }
}

int main(int argc, char* argv[])
{
  SOURCE_INFO();
  po::options_description options("options");
  int nrep;
  options.add_options()
      ("help", "produce help message")
      ("measure", "use FFTW_MEASURE")
      ("nrep", po::value<int>(&nrep)->default_value(1), "num. repetitions");
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, options), vm);
  po::notify(vm);

  if (vm.count("help")) {
    std::cout << options << "\n";
    return 0;
  }

  cout << "nrep: " << nrep << "\n";

  cout << "==================== complex<->complex fft..."
       << "\n";
  test_c2c(vm);
  cout << "==================== real<->complex fft..."
       << "\n";
  test_r2c(vm);

  return 0;
}


#include <iostream>
#include <algorithm>
#include <cstring>
#include <boost/intrusive/rbtree.hpp>

#include "maxdd.h"

using namespace boost::intrusive;

struct maxddnode : public set_base_hook<optimize_size<true> > {
    maxddnode(int t, double x): _t(t), _x(x) {}
    int _t;
    double _x;
    friend inline std::ostream& operator<<(std::ostream&, const maxddnode&);
    friend bool operator<(const maxddnode&, const maxddnode&);
    friend bool operator>(const maxddnode&, const maxddnode&);
    friend bool operator==(const maxddnode&, const maxddnode&);
    friend double operator-(const maxddnode&, const maxddnode&);
};

std::ostream& operator<<( std::ostream& os, const maxddnode& x) {
    os << x._t << "," << x._x;
    return os;
};

struct delete_disposer{
    void operator()(maxddnode *node){
        delete node;
    }
};

bool operator<(const maxddnode&  lhs, const maxddnode& rhs) { return lhs._x < rhs._x; }
bool operator>(const maxddnode&  lhs, const maxddnode& rhs) { return lhs._x > rhs._x; }
bool operator==(const maxddnode& lhs, const maxddnode& rhs) { return lhs._x == rhs._x; }
double operator-(const maxddnode& lhs, const maxddnode& rhs) { return lhs._x - rhs._x; }

typedef rbtree<maxddnode> tree_t;

void best_before_maxdd(double *x, int r, double threshold, int dir, int *results){
    tree_t * tree = new tree_t();
    int link[r];
    memset(link, 0, sizeof(int) * r);
    for (int i=0; i<r; i++){
        maxddnode *node = new maxddnode(i, x[i] * dir);
        for (tree_t::const_iterator it = tree->cbegin(); it != tree->lower_bound(*node); it++){
            link[it->_t] = i;
        }
        tree->erase_and_dispose(tree->cbegin(), tree->lower_bound(*node), delete_disposer());

        maxddnode target(-1, x[i] * dir + threshold);
        for (tree_t::const_iterator it = tree->lower_bound(target); it!=tree->cend(); it++){
            results[it->_t] = i;
        }
        tree->erase_and_dispose(tree->lower_bound(target), tree->cend(), delete_disposer());
        tree->insert_equal(*node);
    }
    maxddnode node(r-1, x[r-1] * dir);
    for (tree_t::const_iterator it = tree->cbegin(); it!=tree->cend(); it++){
        results[it->_t] = r-1;
    }
    tree->clear_and_dispose(delete_disposer());
    for (int i = r-1; i>=0; i--){
        if (link[i] > 0){
            results[i] = results[link[i]];
        }
    }
    delete tree;
}


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*
    This is an example illustrating the use of the deep learning tools from the
    dlib C++ Library.  In it, we will show how to use the loss_metric layer to do
    metric learning on images.

    The main reason you might want to use this kind of algorithm is because you
    would like to use a k-nearest neighbor classifier or similar algorithm, but
    you don't know a good way to calculate the distance between two things.  A
    popular example would be face recognition.  There are a whole lot of papers
    that train some kind of deep metric learning algorithm that embeds face
    images in some vector space where images of the same person are close to each
    other and images of different people are far apart.  Then in that vector
    space it's very easy to do face recognition with some kind of k-nearest
    neighbor classifier.

    In this example we will use a version of the ResNet network from the
    dnn_imagenet_ex.cpp example to learn to map images into some vector space where
    pictures of the same person are close and pictures of different people are far
    apart.

    You might want to read the simpler introduction to the deep metric learning
    API, dnn_metric_learning_ex.cpp, before reading this example.  You should
    also have read the examples that introduce the dlib DNN API before
    continuing.  These are dnn_introduction_ex.cpp and dnn_introduction2_ex.cpp.

*/

#include <dlib/dnn.h>
#include <dlib/image_io.h>
#include <dlib/misc_api.h>

using namespace dlib;
using namespace std;

// ----------------------------------------------------------------------------------------

// We will need to create some functions for loading data.  This program will
// expect to be given a directory structured as follows:
//    top_level_directory/
//        person1/
//            image1.jpg
//            image2.jpg
//            image3.jpg
//        person2/
//            image4.jpg
//            image5.jpg
//            image6.jpg
//        person3/
//            image7.jpg
//            image8.jpg
//            image9.jpg
//
// The specific folder and image names don't matter, nor does the number of folders or
// images.  What does matter is that there is a top level folder, which contains
// subfolders, and each subfolder contains images of a single person.

// This function spiders the top level directory and obtains a list of all the
// image files.
std::vector<std::vector<string>> load_objects_list (
    const string& dir
)
{
    std::vector<std::vector<string>> objects;
    for (auto subdir : directory(dir).get_dirs())
    {
        std::vector<string> imgs;
        for (auto img : subdir.get_files())
            imgs.push_back(img);

        if (imgs.size() != 0)
            objects.push_back(imgs);
    }
    return objects;
}

// This function takes the output of load_objects_list() as input and randomly
// selects images for training.  It should also be pointed out that it's really
// important that each mini-batch contain multiple images of each person.  This
// is because the metric learning algorithm needs to consider pairs of images
// that should be close (i.e. images of the same person) as well as pairs of
// images that should be far apart (i.e. images of different people) during each
// training step.
void load_mini_batch (
    const size_t num_people,     // how many different people to include
    const size_t samples_per_id, // how many images per person to select.
    dlib::rand& rnd,
    const std::vector<std::vector<string>>& objs,
    std::vector<matrix<rgb_pixel>>& images,
    std::vector<unsigned long>& labels
)
{
    images.clear();
    labels.clear();
    DLIB_CASSERT(num_people <= objs.size(), "The dataset doesn't have that many people in it.");

    std::vector<bool> already_selected(objs.size(), false);
    matrix<rgb_pixel> image;
    for (size_t i = 0; i < num_people; ++i)
    {
        size_t id = rnd.get_random_32bit_number()%objs.size();
        // don't pick a person we already added to the mini-batch
        while(already_selected[id])
            id = rnd.get_random_32bit_number()%objs.size();
        already_selected[id] = true;

        for (size_t j = 0; j < samples_per_id; ++j)
        {
            const auto& obj = objs[id][rnd.get_random_32bit_number()%objs[id].size()];
            load_image(image, obj);
            images.push_back(std::move(image));
            labels.push_back(id);
        }
    }

    // You might want to do some data augmentation at this point.  Here we do some simple
    // color augmentation.
    for (auto&& crop : images)
        disturb_colors(crop,rnd);

    // All the images going into a mini-batch have to be the same size.  And really, all
    // the images in your entire training dataset should be the same size for what we are
    // doing to make the most sense.
    DLIB_CASSERT(images.size() > 0);
    for (auto&& img : images)
    {
        DLIB_CASSERT(img.nr() == images[0].nr() && img.nc() == images[0].nc(),
            "All the images in a single mini-batch must be the same size.");
    }
}

// ----------------------------------------------------------------------------------------

// The next page of code defines a ResNet network.  It's basically copied
// and pasted from the dnn_imagenet_ex.cpp example, except we replaced the loss
// layer with loss_metric and make the network somewhat smaller.

template <template <int,template<typename>class,int,typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual = add_prev1<block<N,BN,1,tag1<SUBNET>>>;

template <template <int,template<typename>class,int,typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual_down = add_prev2<avg_pool<2,2,2,2,skip1<tag2<block<N,BN,2,tag1<SUBNET>>>>>>;

template <int N, template <typename> class BN, int stride, typename SUBNET>
using block  = BN<con<N,3,3,1,1,relu<BN<con<N,3,3,stride,stride,SUBNET>>>>>;

template <int N, typename SUBNET> using res       = relu<residual<block,N,bn_con,SUBNET>>;
template <int N, typename SUBNET> using ares      = relu<residual<block,N,affine,SUBNET>>;
template <int N, typename SUBNET> using res_down  = relu<residual_down<block,N,bn_con,SUBNET>>;
template <int N, typename SUBNET> using ares_down = relu<residual_down<block,N,affine,SUBNET>>;

// ----------------------------------------------------------------------------------------

template <typename SUBNET> using level0 = res_down<256,SUBNET>;
template <typename SUBNET> using level1 = res<256,res<256,res_down<256,SUBNET>>>;
template <typename SUBNET> using level2 = res<128,res<128,res_down<128,SUBNET>>>;
template <typename SUBNET> using level3 = res<64,res<64,res<64,res_down<64,SUBNET>>>>;
template <typename SUBNET> using level4 = res<32,res<32,res<32,SUBNET>>>;

template <typename SUBNET> using alevel0 = ares_down<256,SUBNET>;
template <typename SUBNET> using alevel1 = ares<256,ares<256,ares_down<256,SUBNET>>>;
template <typename SUBNET> using alevel2 = ares<128,ares<128,ares_down<128,SUBNET>>>;
template <typename SUBNET> using alevel3 = ares<64,ares<64,ares<64,ares_down<64,SUBNET>>>>;
template <typename SUBNET> using alevel4 = ares<32,ares<32,ares<32,SUBNET>>>;

// training network type
using net_type = loss_metric<fc_no_bias<128,avg_pool_everything<
                            level0<
                            level1<
                            level2<
                            level3<
                            level4<
                            max_pool<3,3,2,2,relu<bn_con<con<32,7,7,2,2,
                            input_rgb_image
                            >>>>>>>>>>>>;

// testing network type (replaced batch normalization with fixed affine transforms)
using anet_type = loss_metric<fc_no_bias<128,avg_pool_everything<
                            alevel0<
                            alevel1<
                            alevel2<
                            alevel3<
                            alevel4<
                            max_pool<3,3,2,2,relu<affine<con<32,7,7,2,2,
                            input_rgb_image
                            >>>>>>>>>>>>;

// ----------------------------------------------------------------------------------------

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        cout << "Give a folder as input.  It should contain sub-folders of images and we will " << endl;
        cout << "learn to distinguish between these sub-folders with metric learning.  " << endl;
        cout << "For example, you can run this program on the very small examples/johns dataset" << endl;
        cout << "that comes with dlib by running this command:" << endl;
        cout << "   ./dnn_metric_learning_on_images_ex johns" << endl;
        return 1;
    }

    auto objs = load_objects_list(argv[1]);

    cout << "objs.size(): "<< objs.size() << endl;

    std::vector<matrix<rgb_pixel>> images;
    std::vector<unsigned long> labels;

    net_type net;

    dnn_trainer<net_type> trainer(net, sgd(0.0001, 0.9));
    trainer.set_learning_rate(0.1);
    trainer.be_verbose();
    trainer.set_synchronization_file("face_metric_sync", std::chrono::minutes(5));
    // I've set this to something really small to make the example terminate
    // sooner.  But when you really want to train a good model you should set
    // this to something like 10000 so training doesn't terminate too early.
    trainer.set_iterations_without_progress_threshold(300);

    // If you have a lot of data then it might not be reasonable to load it all
    // into RAM.  So you will need to be sure you are decompressing your images
    // and loading them fast enough to keep the GPU occupied.  I like to do this
    // using the following coding pattern: create a bunch of threads that dump
    // mini-batches into dlib::pipes.
    dlib::pipe<std::vector<matrix<rgb_pixel>>> qimages(4);
    dlib::pipe<std::vector<unsigned long>> qlabels(4);
    auto data_loader = [&qimages, &qlabels, &objs](time_t seed)
    {
        dlib::rand rnd(time(0)+seed);
        std::vector<matrix<rgb_pixel>> images;
        std::vector<unsigned long> labels;
        while(qimages.is_enabled())
        {
            try
            {
                load_mini_batch(5, 5, rnd, objs, images, labels);
                qimages.enqueue(images);
                qlabels.enqueue(labels);
            }
            catch(std::exception& e)
            {
                cout << "EXCEPTION IN LOADING DATA" << endl;
                cout << e.what() << endl;
            }
        }
    };
    // Run the data_loader from 5 threads.  You should set the number of threads
    // relative to the number of CPU cores you have.
    std::thread data_loader1([data_loader](){ data_loader(1); });
    std::thread data_loader2([data_loader](){ data_loader(2); });
    std::thread data_loader3([data_loader](){ data_loader(3); });
    std::thread data_loader4([data_loader](){ data_loader(4); });
    std::thread data_loader5([data_loader](){ data_loader(5); });

    // Here we do the training.  We keep passing mini-batches to the trainer until the
    // learning rate has dropped low enough.
    while(trainer.get_learning_rate() >= 1e-4)
    {
        qimages.dequeue(images);
        qlabels.dequeue(labels);
        trainer.train_one_step(images, labels);
    }

    // Wait for training threads to stop
    trainer.get_net();
    cout << "done training" << endl;

    // Save the network to disk
    net.clean();
    serialize("metric_network_renset.dat") << net;

    // stop all the data loading threads and wait for them to terminate.
    qimages.disable();
    qlabels.disable();
    data_loader1.join();
    data_loader2.join();
    data_loader3.join();
    data_loader4.join();
    data_loader5.join();

    // Now, just to show an example of how you would use the network, let's check how well
    // it performs on the training data.
    dlib::rand rnd(time(0));
    load_mini_batch(5, 5, rnd, objs, images, labels);

    // Normally you would use the non-batch-normalized version of the network to do
    // testing, which is what we do here.
    anet_type testing_net = net;

    // Run all the images through the network to get their vector embeddings.
    std::vector<matrix<float,0,1>> embedded = testing_net(images);

    // Now, check if the embedding puts images with the same labels near each other and
    // images with different labels far apart.
    int num_right = 0;
    int num_wrong = 0;
    for (size_t i = 0; i < embedded.size(); ++i)
    {
        for (size_t j = i+1; j < embedded.size(); ++j)
        {
            if (labels[i] == labels[j])
            {
                // The loss_metric layer will cause images with the same label to be less
                // than net.loss_details().get_distance_threshold() distance from each
                // other.  So we can use that distance value as our testing threshold.
                if (length(embedded[i]-embedded[j]) < testing_net.loss_details().get_distance_threshold())
                    ++num_right;
                else
                    ++num_wrong;
            }
            else
            {
                if (length(embedded[i]-embedded[j]) >= testing_net.loss_details().get_distance_threshold())
                    ++num_right;
                else
                    ++num_wrong;
            }
        }
    }

    cout << "num_right: "<< num_right << endl;
    cout << "num_wrong: "<< num_wrong << endl;

}


/*    Copyright (c) 2010-2015, Delft University of Technology
 *    All rights reserved.
 *
 *    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 the Delft University of Technology 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 HOLDER 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.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      110131    E. Iorfida        First creation of the code.
 *      110202    E. Iorfida        Modified structure of the code, unique
 *                                  base class for launch and capture paths.
 *      110208    E. Iorfida        Deleted execute( ) function. Modified getDeltaV into
 *                                  computeDeltaV.
 *      110214    E. Iorfida        Code updated with the modification made in .h/.cpp files
 *                                  about radius of central body.
 *      110627    K. Kumar          Updated to use new predefined planets code.
 *      120416    T. Secretin       Boostified unit test.
 *      120531    P. Musegaas       Complete update of unit test after changing code completely.
 *                                  Changed examples to more accurate ones, verifyable in reader.
 *                                  Added hand calculation test for non-circular parking orbit.
 *      120625    P. Musegaas       Minor changes.
 *
 *    References
 *      Wakker, K. F. (2007), Lecture Notes Astrodynamics II (Chapter 18), TU Delft course AE4-874,
 *          Delft University of technology, Delft, The Netherlands.
 *
 *    Notes
 *
 */

#define BOOST_TEST_MAIN

#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/unit_test.hpp>

#include "Tudat/Astrodynamics/MissionSegments/escapeAndCapture.h"

namespace tudat
{
namespace unit_tests
{

//! Test patched conics implementation.
BOOST_AUTO_TEST_SUITE( test_escape_and_capture )

//! Test delta-V computation for the escape phase.
BOOST_AUTO_TEST_CASE( testDeltaVEscape )
{
    // Set tolerance.
    const double tolerance = 1.0e-15;

    // Expected test result based on Table 18.2 of [Wakker, 2007]. Escape velocity to go to Mars
    // starting from a parking orbit around Earth at 185 km altitude. Result calculated with more
    // precision separately using Equation 18.25 of [Wakker, 2007].
    const double expectedDeltaVEscape = 3614.64460281887;

    // Set required parameters.
    const double gravitationalParameterEarth = 3.986e14;
    const double semiMajorAxis = 6.378e6 + 1.85e5;
    const double eccentricity = 0.0;
    const double excessVelocity = 2944.61246668719;

    // Compute delta-V of escape phase.
    const double deltaVEscape = mission_segments::computeEscapeOrCaptureDeltaV(
                gravitationalParameterEarth, semiMajorAxis, eccentricity, excessVelocity );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaVEscape, deltaVEscape, tolerance );
}

//! Test delta-V computation for the capture phase.
BOOST_AUTO_TEST_CASE( testDeltaVCapture )
{
    // Set tolerance.
    const double tolerance = 1.0e-15;

    // Expected test result based on Table 18.2 of [Wakker, 2007]. Capture velocity to arrive at
    // a parking orbit around Mars of 1.1 times the planetary radius starting from Earth. Result
    // calculated with more precision separately using Equation 18.25 of [Wakker, 2007].
    const double expectedDeltaVCapture = 2087.1062716740798;

    // Set required parameters.
    const double gravitationalParameterMars = 4.2830e13;
    const double semiMajorAxis = 1.1 * 3.3895e6;
    const double eccentricity = 0.0;
    const double excessVelocity = 2648.83359973278;

    // Compute delta-V of escape phase.
    const double deltaVCapture = mission_segments::computeEscapeOrCaptureDeltaV(
                gravitationalParameterMars, semiMajorAxis, eccentricity, excessVelocity );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaVCapture, deltaVCapture, tolerance );
}

//! Test delta-V computation for an escape from an eccentric parking orbit.
BOOST_AUTO_TEST_CASE( testDeltaVEscapeElliptical )
{
    // Set tolerance.
    const double tolerance = 1.0e-15;

    // Departure from 200 km perigee, 1800 km apogee parking orbit from Earth to Mars transfer.
    // Result calculated to high precision separately using Equation 18.25 of [Wakker, 2007].
    const double expectedDeltaVEscape = 3200.2178506657729;

    // Set required parameters.
    const double gravitationalParameterEarth = 3.986e14;
    const double semiMajorAxis = 6.378e6 + 1.0e6;
    const double eccentricity = 0.108430468961778;
    const double excessVelocity = 2944.61246668719;

    // Compute delta-V of escape phase.
    const double deltaVEscape = mission_segments::computeEscapeOrCaptureDeltaV(
                gravitationalParameterEarth, semiMajorAxis, eccentricity, excessVelocity );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaVEscape, deltaVEscape, tolerance );
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests
} // namespace tudat


// Copyright 2014 Jonathan Graehl - http://graehl.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRAEHL__SHARED__ACCUMULATE_HPP
#define GRAEHL__SHARED__ACCUMULATE_HPP

#include <boost/integer_traits.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>
#include <limits>

namespace graehl {

template <class A> inline
void set_multiply_identity(A &i)
{
  i = 1;
}

template <class A> inline
void set_add_identity(A &i)
{
  i = 0;
}

//FIXME: use boost::integer_traits, std::numeric_limits, boost::is_integral, is_floating_point
template <class A> inline
void set_min_identity(A &i, boost::enable_if<boost::is_integral<A> >* d = 0)
{
  i = boost::integer_traits<A>::max;
}

template <class A> inline
void set_max_identity(A &i, boost::enable_if<boost::is_integral<A> >* d = 0)
{
  i = boost::integer_traits<A>::min;
}

template <class A> inline
void set_min_identity(A &i, boost::enable_if<boost::is_float<A> >* d = 0)
{
  i = std::numeric_limits<A>::max();
}

template <class A> inline
void set_max_identity(A &i, boost::enable_if<boost::is_float<A> >* d = 0)
{
  i=-std::numeric_limits<A>::max();
}

struct accumulate_multiply
{
  template <class A>
  void operator()(A &sum) const
  {
    set_multiply_identity(sum);
  }

  template <class A, class X>
  void operator()(A &sum, X const& x) const
  {
    sum *= x;
  }
};

struct accumulate_sum
{
  template <class A>
  void operator()(A &sum) const
  {
    set_add_identity(sum);
  }

  template <class A, class X>
  void operator()(A &sum, X const& x) const
  {
    sum += x;
  }
};

struct accumulate_max
{
  template <class A>
  void operator()(A &sum) const
  {
    set_max_identity(sum);
  }
  template <class A, class X>
  void operator()(A &sum, X const& x) const
  {
    if (sum<x)
      sum = x;
  }
};

struct accumulate_min
{
  template <class A>
  void operator()(A &sum) const
  {
    set_min_identity(sum);
  }
  template <class A, class X>
  void operator()(A &sum, X const& x) const
  {
    if (x < sum)
      sum = x;
  }
};

}//graehl

#endif