cnorxz/src/include/multi_array_operation.h

600 lines
17 KiB
C
Raw Normal View History

2017-02-16 11:20:40 +01:00
// -*- C++ -*-
#ifndef __multi_array_operation_h__
#define __multi_array_operation_h__
#include <cstdlib>
#include <tuple>
2017-03-22 21:51:54 +01:00
#include <cmath>
2017-08-30 17:56:38 +02:00
#include <map>
#include <utility>
2017-02-16 11:20:40 +01:00
#include "base_def.h"
2017-12-18 11:19:04 +01:00
#include "mbase_def.h"
2018-02-13 15:38:03 +01:00
//#include "block/block.h"
//#include "operation_utils.h"
2017-12-18 13:13:13 +01:00
#include "ranges/rheader.h"
#include "pack_num.h"
2017-02-16 11:20:40 +01:00
2017-12-25 13:44:55 +01:00
#include "ranges/index_info.h"
#include "arith.h"
2017-12-25 13:44:55 +01:00
2017-02-16 11:20:40 +01:00
namespace MultiArrayTools
{
namespace
{
using namespace MultiArrayHelper;
}
template <typename T, class OperationClass>
2017-08-11 11:30:27 +02:00
class OperationTemplate
{
public:
2017-08-27 17:52:50 +02:00
2017-12-25 13:44:55 +01:00
OperationClass& THIS() { return static_cast<OperationClass&>(*this); }
const OperationClass& THIS() const { return static_cast<OperationClass const&>(*this); }
2017-08-11 11:30:27 +02:00
template <class Second>
2017-08-26 17:18:42 +02:00
auto operator+(const Second& in) const
-> Operation<T,plus<T>,OperationClass,Second>;
2017-08-26 17:18:42 +02:00
template <class Second>
auto operator-(const Second& in) const
-> Operation<T,minus<T>,OperationClass,Second>;
2017-08-26 17:18:42 +02:00
template <class Second>
auto operator*(const Second& in) const
-> Operation<T,multiplies<T>,OperationClass,Second>;
2017-08-26 17:18:42 +02:00
template <class Second>
auto operator/(const Second& in) const
-> Operation<T,divides<T>,OperationClass,Second>;
2017-11-05 18:46:38 +01:00
2017-11-02 21:20:31 +01:00
template <class IndexType>
auto c(std::shared_ptr<IndexType>& ind) const
-> Contraction<T,OperationClass,IndexType>;
2017-11-05 18:46:38 +01:00
2017-08-11 15:26:10 +02:00
private:
2017-12-25 13:44:55 +01:00
friend OperationClass;
OperationTemplate() = default;
2017-08-11 11:30:27 +02:00
};
template <typename T, class OpClass, class... Ranges>
2018-01-05 13:56:16 +01:00
class OperationMaster
{
public:
2018-01-07 23:08:16 +01:00
class AssignmentExpr
{
private:
AssignmentExpr() = default;
OperationMaster& mM;
const OpClass& mSec;
2018-01-07 23:08:16 +01:00
public:
static constexpr size_t LAYER = 0;
static constexpr size_t SIZE = OpClass::SIZE;
typedef decltype(mSec.rootSteps()) ExtType;
AssignmentExpr(OperationMaster& m, const OpClass& sec);
2018-01-07 23:08:16 +01:00
AssignmentExpr(const AssignmentExpr& in) = default;
2018-01-07 23:08:16 +01:00
AssignmentExpr(AssignmentExpr&& in) = default;
2018-02-13 15:38:03 +01:00
inline void operator()(size_t start = 0) const;
inline void operator()(size_t start, ExtType last) const;
2018-01-07 23:08:16 +01:00
2018-02-12 18:26:56 +01:00
auto rootSteps(std::intptr_t iPtrNum = 0) const -> ExtType;
2018-01-07 23:08:16 +01:00
};
2017-08-27 17:52:50 +02:00
typedef T value_type;
2017-08-10 15:12:26 +02:00
typedef OperationBase<T> OB;
typedef ContainerRange<T,Ranges...> CRange;
2017-08-10 15:12:26 +02:00
typedef typename MultiRange<Ranges...>::IndexType IndexType;
OperationMaster(MutableMultiArrayBase<T,Ranges...>& ma, const OpClass& second,
2017-08-11 15:26:10 +02:00
std::shared_ptr<typename CRange::IndexType>& index);
2017-12-24 18:14:07 +01:00
OperationMaster(MutableMultiArrayBase<T,Ranges...>& ma, const OpClass& second,
std::shared_ptr<typename CRange::IndexType>& index,
const IndexInfo* blockIndex);
2017-02-27 17:00:51 +01:00
2018-02-13 16:54:13 +01:00
inline void set(size_t pos, T val) { mDataPtr[pos] = val; }
inline void add(size_t pos, T val) { mDataPtr[pos] += val; }
inline T get(size_t pos) const;
private:
2017-12-25 13:44:55 +01:00
std::shared_ptr<IndexType> mkIndex(std::shared_ptr<typename CRange::IndexType>& index);
2017-12-24 18:14:07 +01:00
void performAssignment(std::intptr_t blockIndexNum);
OpClass const& mSecond;
MutableMultiArrayBase<T,Ranges...>& mArrayRef;
2018-02-13 15:38:03 +01:00
T* mDataPtr;
2017-08-11 11:30:27 +02:00
std::shared_ptr<IndexType> mIndex;
2017-12-25 13:44:55 +01:00
IndexInfo mIInfo;
2017-02-16 11:20:40 +01:00
};
2017-08-10 15:12:26 +02:00
2017-08-10 15:12:26 +02:00
template <typename T, class... Ranges>
class ConstOperationRoot : /*public OperationBase<T>,*/
public OperationTemplate<T,ConstOperationRoot<T,Ranges...> >
{
public:
2017-08-27 17:52:50 +02:00
typedef T value_type;
2017-08-10 15:12:26 +02:00
typedef OperationBase<T> OB;
typedef OperationTemplate<T,ConstOperationRoot<T,Ranges...> > OT;
typedef ContainerRange<T,Ranges...> CRange;
2017-08-10 15:12:26 +02:00
typedef typename CRange::IndexType IndexType;
static constexpr size_t SIZE = 1;
ConstOperationRoot(const MultiArrayBase<T,Ranges...>& ma,
2017-08-10 15:12:26 +02:00
const std::shared_ptr<typename Ranges::IndexType>&... indices);
2018-02-12 18:26:56 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T get(ET pos) const;
2018-02-13 15:38:03 +01:00
MExt<void> rootSteps(std::intptr_t iPtrNum = 0) const; // nullptr for simple usage with decltype
template <class Expr>
Expr loop(Expr exp) const;
private:
2017-12-25 13:44:55 +01:00
std::shared_ptr<IndexType>
mkIndex(const MultiArrayBase<T,Ranges...>& ma,
const std::shared_ptr<typename Ranges::IndexType>&... indices);
MultiArrayBase<T,Ranges...> const& mArrayRef;
2018-02-13 15:38:03 +01:00
const T* mDataPtr;
2017-08-10 15:12:26 +02:00
std::shared_ptr<IndexType> mIndex;
2017-12-25 13:44:55 +01:00
IndexInfo mIInfo;
};
2017-09-11 12:54:24 +02:00
2017-08-10 15:12:26 +02:00
template <typename T, class... Ranges>
2018-01-05 13:56:16 +01:00
class OperationRoot : public OperationTemplate<T,OperationRoot<T,Ranges...> >
{
public:
2017-08-27 17:52:50 +02:00
typedef T value_type;
2017-08-10 15:12:26 +02:00
typedef OperationBase<T> OB;
typedef OperationTemplate<T,OperationRoot<T,Ranges...> > OT;
typedef ContainerRange<T,Ranges...> CRange;
2017-08-10 15:12:26 +02:00
typedef typename CRange::IndexType IndexType;
static constexpr size_t SIZE = 1;
OperationRoot(MutableMultiArrayBase<T,Ranges...>& ma,
2017-08-10 15:12:26 +02:00
const std::shared_ptr<typename Ranges::IndexType>&... indices);
2017-08-11 11:30:27 +02:00
template <class OpClass>
OperationMaster<T,OpClass,Ranges...> operator=(const OpClass& in);
2018-02-12 18:26:56 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T get(ET pos) const;
2018-02-13 15:38:03 +01:00
MExt<void> rootSteps(std::intptr_t iPtrNum = 0) const; // nullptr for simple usage with decltype
template <class Expr>
Expr loop(Expr exp) const;
2017-05-24 19:01:02 +02:00
private:
2017-12-25 13:44:55 +01:00
std::shared_ptr<IndexType>
mkIndex(const MultiArrayBase<T,Ranges...>& ma,
const std::shared_ptr<typename Ranges::IndexType>&... indices);
MutableMultiArrayBase<T,Ranges...>& mArrayRef;
2018-02-13 15:38:03 +01:00
T* mDataPtr;
2017-08-10 15:12:26 +02:00
std::shared_ptr<IndexType> mIndex;
2017-12-25 13:44:55 +01:00
IndexInfo mIInfo;
2017-02-16 11:20:40 +01:00
};
template <class Op>
size_t sumRootNum()
{
return typename Op::rootNum();
}
template <class Op1, class Op2, class... Ops>
size_t sumRootNum()
{
return typename Op1::rootNum() + sumRootNum<Op2,Ops...>();
}
template <size_t N>
struct RootSumN
{
template <class Op1, class... Ops>
struct rs
{
2018-02-13 15:38:03 +01:00
static constexpr size_t SIZE = Op1::SIZE + RootSumN<N-1>::template rs<Ops...>::SIZE;
};
};
template <>
struct RootSumN<0>
{
template <class Op1>
struct rs
{
2018-02-13 15:38:03 +01:00
static constexpr size_t SIZE = Op1::SIZE;
};
};
template <class... Ops>
struct RootSum
{
2018-02-13 15:38:03 +01:00
static constexpr size_t SIZE = RootSumN<sizeof...(Ops)-1>::template rs<Ops...>::SIZE;
};
2017-09-11 12:54:24 +02:00
2017-08-10 15:12:26 +02:00
template <typename T, class OpFunction, class... Ops>
2018-01-05 13:56:16 +01:00
class Operation : public OperationTemplate<T,Operation<T,OpFunction,Ops...> >
2017-03-22 11:44:33 +01:00
{
public:
2017-08-27 17:52:50 +02:00
typedef T value_type;
2017-08-10 15:12:26 +02:00
typedef OperationBase<T> OB;
typedef OperationTemplate<T,Operation<T,OpFunction,Ops...> > OT;
2017-08-10 15:12:26 +02:00
typedef OpFunction F;
2018-02-13 15:38:03 +01:00
static constexpr size_t SIZE = RootSum<Ops...>::SIZE;
private:
2018-02-13 15:38:03 +01:00
std::tuple<Ops...> mOps;
public:
2018-02-13 21:36:41 +01:00
typedef decltype(PackNum<sizeof...(Ops)-1>::template mkSteps<Ops...>(0, mOps)) ETuple;
2017-08-11 15:26:10 +02:00
Operation(const Ops&... ops);
2017-03-22 11:44:33 +01:00
2018-02-13 15:38:03 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T get(ET pos) const;
auto rootSteps(std::intptr_t iPtrNum = 0) const // nullptr for simple usage with decltype
2018-02-13 15:38:03 +01:00
-> decltype(PackNum<sizeof...(Ops)-1>::mkSteps(iPtrNum, mOps));
template <class Expr>
auto loop(Expr exp) const
-> decltype(PackNum<sizeof...(Ops)-1>::mkLoop( mOps, exp));
2017-03-22 11:44:33 +01:00
};
2017-11-04 22:49:55 +01:00
2017-11-02 21:20:31 +01:00
template <typename T, class Op, class IndexType>
2017-11-04 22:49:55 +01:00
class Contraction : public OperationTemplate<T,Contraction<T,Op,IndexType> >
{
public:
typedef T value_type;
2017-11-02 21:20:31 +01:00
typedef OperationTemplate<T,Contraction<T,Op,IndexType> > OT;
static constexpr size_t SIZE = Op::SIZE;
private:
const Op& mOp;
std::shared_ptr<IndexType> mInd;
public:
typedef decltype(mOp.rootSteps(0)) ETuple;
2017-11-02 21:20:31 +01:00
Contraction(const Op& op, std::shared_ptr<IndexType> ind);
2018-02-13 15:38:03 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T get(ET pos) const;
auto rootSteps(std::intptr_t iPtrNum = 0) const // nullptr for simple usage with decltype
-> decltype(mOp.rootSteps(iPtrNum));
template <class Expr>
2018-02-12 18:26:56 +01:00
auto loop(Expr exp) const -> decltype(mInd->iforh(exp));
};
2017-08-11 15:26:10 +02:00
}
2017-02-16 11:20:40 +01:00
/* ========================= *
* --- TEMPLATE CODE --- *
* ========================= */
namespace MultiArrayTools
{
namespace
{
using namespace MultiArrayHelper;
}
/***************************
* OperationTemplate *
***************************/
template <typename T, class OperationClass>
template <class Second>
auto OperationTemplate<T,OperationClass>::operator+(const Second& in) const
-> Operation<T,plus<T>,OperationClass,Second>
{
return Operation<T,plus<T>,OperationClass,Second>(THIS(), in);
}
template <typename T, class OperationClass>
template <class Second>
auto OperationTemplate<T,OperationClass>::operator-(const Second& in) const
-> Operation<T,minus<T>,OperationClass,Second>
{
return Operation<T,minus<T>,OperationClass,Second>(THIS(), in);
}
template <typename T, class OperationClass>
template <class Second>
auto OperationTemplate<T,OperationClass>::operator*(const Second& in) const
-> Operation<T,multiplies<T>,OperationClass,Second>
{
return Operation<T,multiplies<T>,OperationClass,Second>(THIS(), in);
}
template <typename T, class OperationClass>
template <class Second>
auto OperationTemplate<T,OperationClass>::operator/(const Second& in) const
-> Operation<T,divides<T>,OperationClass,Second>
{
return Operation<T,divides<T>,OperationClass,Second>(THIS(), in);
}
template <typename T, class OperationClass>
template <class IndexType>
auto OperationTemplate<T,OperationClass>::c(std::shared_ptr<IndexType>& ind) const
-> Contraction<T,OperationClass,IndexType>
{
2017-12-25 13:44:55 +01:00
return Contraction<T,OperationClass,IndexType>(THIS(), ind);
}
/*****************************************
* OperationMaster::AssignmentExpr *
*****************************************/
template <typename T, class OpClass, class... Ranges>
OperationMaster<T,OpClass,Ranges...>::AssignmentExpr::
AssignmentExpr(OperationMaster& m, const OpClass& sec) :
mM(m), mSec(sec) {}
template <typename T, class OpClass, class... Ranges>
inline void OperationMaster<T,OpClass,Ranges...>::AssignmentExpr::
2018-02-13 15:38:03 +01:00
operator()(size_t start, ExtType last) const
{
2018-02-13 16:54:13 +01:00
mM.add(start, mSec.template get<ExtType>(last) );
}
template <typename T, class OpClass, class... Ranges>
typename OperationMaster<T,OpClass,Ranges...>::AssignmentExpr::ExtType
OperationMaster<T,OpClass,Ranges...>::AssignmentExpr::
2018-02-12 18:26:56 +01:00
rootSteps(std::intptr_t iPtrNum) const
{
return mSec.rootSteps(iPtrNum);
}
/*************************
* OperationMaster *
*************************/
template <typename T, class OpClass, class... Ranges>
OperationMaster<T,OpClass,Ranges...>::
OperationMaster(MutableMultiArrayBase<T,Ranges...>& ma, const OpClass& second,
std::shared_ptr<typename CRange::IndexType>& index) :
2018-02-13 15:38:03 +01:00
mSecond(second), mArrayRef(ma), mDataPtr(mArrayRef.data()),
mIndex(mkIndex(index)), mIInfo(*mIndex)
{
2018-02-13 15:38:03 +01:00
performAssignment(0);
2017-12-24 18:14:07 +01:00
}
template <typename T, class OpClass, class... Ranges>
OperationMaster<T,OpClass,Ranges...>::
OperationMaster(MutableMultiArrayBase<T,Ranges...>& ma, const OpClass& second,
std::shared_ptr<typename CRange::IndexType>& index,
const IndexInfo* blockIndex) :
2018-02-13 15:38:03 +01:00
mSecond(second), mArrayRef(ma), mDataPtr(mArrayRef.data()),
mIndex(mkIndex(index)), mIInfo(*mIndex)
2017-12-24 18:14:07 +01:00
{
2018-02-13 15:38:03 +01:00
performAssignment(0);
2017-12-24 18:14:07 +01:00
}
2017-12-25 13:44:55 +01:00
template <typename T, class OpClass, class... Ranges>
std::shared_ptr<typename OperationMaster<T,OpClass,Ranges...>::IndexType>
OperationMaster<T,OpClass,Ranges...>::
mkIndex(std::shared_ptr<typename CRange::IndexType>& index)
{
MultiRangeFactory<Ranges...> mrf( index->range() );
std::shared_ptr<MultiRange<Ranges...> > mr =
std::dynamic_pointer_cast<MultiRange<Ranges...> >( mrf.create() );
auto i = std::make_shared<IndexType>( mr->begin() );
(*i) = *index;
return i;
}
2017-12-24 18:14:07 +01:00
2017-12-25 13:44:55 +01:00
template <typename T, class OpClass, class... Ranges>
2017-12-24 18:14:07 +01:00
void OperationMaster<T,OpClass,Ranges...>::performAssignment(std::intptr_t blockIndexNum)
{
AssignmentExpr ae(*this, mSecond); // Expression to be executed within loop
const auto loop = mSecond.template loop<decltype(mIndex->ifor(ae))>( mIndex->ifor(ae) );
// hidden Loops outside ! -> auto vectorizable
loop(); // execute overall loop(s) and so internal hidden loops and so the inherited expressions
}
template <typename T, class OpClass, class... Ranges>
inline T OperationMaster<T,OpClass,Ranges...>::get(size_t pos) const
{
2018-02-13 15:38:03 +01:00
return mDataPtr[pos];
}
/****************************
* ConstOperationRoot *
****************************/
template <typename T, class... Ranges>
ConstOperationRoot<T,Ranges...>::
ConstOperationRoot(const MultiArrayBase<T,Ranges...>& ma,
const std::shared_ptr<typename Ranges::IndexType>&... indices) :
2018-02-13 15:38:03 +01:00
mArrayRef(ma), mDataPtr(mArrayRef.data()),
mIndex( mkIndex(ma,indices...) ), mIInfo(*mIndex)
2017-12-25 13:44:55 +01:00
{}
template <typename T, class... Ranges>
std::shared_ptr<typename ConstOperationRoot<T,Ranges...>::IndexType>
ConstOperationRoot<T,Ranges...>::
mkIndex(const MultiArrayBase<T,Ranges...>& ma,
const std::shared_ptr<typename Ranges::IndexType>&... indices)
{
2017-12-25 13:44:55 +01:00
auto i = std::make_shared<IndexType>( ma.range() );
(*mIndex)(indices...);
2017-12-25 13:44:55 +01:00
return i;
}
template <typename T, class... Ranges>
2018-02-12 18:26:56 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T ConstOperationRoot<T,Ranges...>::get(ET pos) const
{
2018-02-13 15:38:03 +01:00
return mDataPtr[pos.val()];
}
template <typename T, class... Ranges>
2018-02-13 15:38:03 +01:00
MExt<void> ConstOperationRoot<T,Ranges...>::rootSteps(std::intptr_t iPtrNum) const
{
2018-02-14 00:38:44 +01:00
return MExt<void>(getStepSize( mIndex->info(), iPtrNum ));
//return MExt<void>(getStepSize( getRootIndices( mIndex->info() ), iPtrNum ));
}
template <typename T, class... Ranges>
template <class Expr>
Expr ConstOperationRoot<T,Ranges...>::loop(Expr exp) const
{
return exp;
}
/***********************
* OperationRoot *
***********************/
template <typename T, class... Ranges>
OperationRoot<T,Ranges...>::
OperationRoot(MutableMultiArrayBase<T,Ranges...>& ma,
const std::shared_ptr<typename Ranges::IndexType>&... indices) :
2018-02-13 15:38:03 +01:00
mArrayRef(ma), mDataPtr(mArrayRef.data()),
mIndex( mkIndex( ma, indices... ) ), mIInfo(*mIndex)
2017-12-25 13:44:55 +01:00
{}
template <typename T, class... Ranges>
std::shared_ptr<typename OperationRoot<T,Ranges...>::IndexType>
OperationRoot<T,Ranges...>::
mkIndex(const MultiArrayBase<T,Ranges...>& ma,
const std::shared_ptr<typename Ranges::IndexType>&... indices)
{
2017-12-25 13:44:55 +01:00
auto i = std::make_shared<IndexType>( ma.range() );
(*mIndex)(indices...);
2017-12-25 13:44:55 +01:00
return i;
}
2017-12-25 13:44:55 +01:00
template <typename T, class... Ranges>
template <class OpClass>
OperationMaster<T,OpClass,Ranges...> OperationRoot<T,Ranges...>::operator=(const OpClass& in)
{
return OperationMaster<T,OpClass,Ranges...>(mArrayRef, in, mIndex);
}
template <typename T, class... Ranges>
2018-02-12 18:26:56 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T OperationRoot<T,Ranges...>::get(ET pos) const
{
2018-02-13 15:38:03 +01:00
return mDataPtr[pos.val()];
}
template <typename T, class... Ranges>
2018-02-13 15:38:03 +01:00
MExt<void> OperationRoot<T,Ranges...>::rootSteps(std::intptr_t iPtrNum) const
{
2018-02-14 00:38:44 +01:00
return MExt<void>(getStepSize( mIndex->info(), iPtrNum ));
//return MExt<void>(getStepSize( getRootIndices( mIndex->info() ), iPtrNum ));
}
template <typename T, class... Ranges>
template <class Expr>
Expr OperationRoot<T,Ranges...>::loop(Expr exp) const
{
return exp;
}
/*******************
* Operation *
*******************/
template <typename T, class OpFunction, class... Ops>
Operation<T,OpFunction,Ops...>::Operation(const Ops&... ops) :
mOps(ops...) {}
template <typename T, class OpFunction, class... Ops>
2018-02-13 15:38:03 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T Operation<T,OpFunction,Ops...>::get(ET pos) const
{
2018-02-14 00:38:44 +01:00
typedef std::tuple<Ops...> OpTuple;
2018-02-13 15:38:03 +01:00
return PackNum<sizeof...(Ops)-1>::
template mkOpExpr<SIZE,T,ET,OpTuple,OpFunction>(pos, mOps);
}
template <typename T, class OpFunction, class... Ops>
auto Operation<T,OpFunction,Ops...>::rootSteps(std::intptr_t iPtrNum) const
2018-02-13 15:38:03 +01:00
-> decltype(PackNum<sizeof...(Ops)-1>::mkSteps(iPtrNum, mOps))
{
2018-02-13 15:38:03 +01:00
return PackNum<sizeof...(Ops)-1>::mkSteps(iPtrNum, mOps);
}
template <typename T, class OpFunction, class... Ops>
template <class Expr>
auto Operation<T,OpFunction,Ops...>::loop(Expr exp) const
-> decltype(PackNum<sizeof...(Ops)-1>::mkLoop( mOps, exp ))
{
return PackNum<sizeof...(Ops)-1>::mkLoop( mOps, exp );
}
/*********************
* Contraction *
*********************/
template <typename T, class Op, class IndexType>
Contraction<T,Op,IndexType>::Contraction(const Op& op, std::shared_ptr<IndexType> ind) :
mOp(op),
mInd(ind) {}
// forward loop !!!!
template <typename T, class Op, class IndexType>
2018-02-13 15:38:03 +01:00
template <class ET>
2018-02-13 16:54:13 +01:00
inline T Contraction<T,Op,IndexType>::get(ET pos) const
{
2018-02-13 15:38:03 +01:00
return mOp.template get<ET>(pos);
}
template <typename T, class Op, class IndexType>
auto Contraction<T,Op,IndexType>::rootSteps(std::intptr_t iPtrNum) const
-> decltype(mOp.rootSteps(iPtrNum))
{
return mOp.rootSteps(iPtrNum);
}
template <typename T, class Op, class IndexType>
template <class Expr>
auto Contraction<T,Op,IndexType>::loop(Expr exp) const -> decltype(mInd->iforh(exp))
{
return mInd->iforh(exp);
}
}
2017-02-16 11:20:40 +01:00
#endif