borg_public/libLSS/tools/fused_cond.hpp
2023-05-29 10:41:03 +02:00

149 lines
5.3 KiB
C++

/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fused_cond.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef _LIBLSS_FUSED_CONDITIONAL_HPP
#define _LIBLSS_FUSED_CONDITIONAL_HPP
#include <functional>
#include "libLSS/tools/fused_array.hpp"
#include "libLSS/tools/ref_tools.hpp"
namespace LibLSS {
namespace FusedCondDetails {
using size_type = FUSE_detail::FusedArrayTypes::size_type;
using index = FUSE_detail::FusedArrayTypes::index;
template<typename T>
struct ArrayDim {
typedef typename std::remove_reference<T>::type Array;
static constexpr size_t D = Array::dimensionality;
};
template<typename T>
using ArrayIndex = boost::array<index, ArrayDim<T>::D>;
// This functor allows invokes another functor with the indexes wrapped in a boost::array
template<typename Return, typename Tuple>
struct MakeSubIndexOp {
Tuple const t;
inline MakeSubIndexOp(Tuple const&& _t) : t(_t) {}
inline MakeSubIndexOp(Tuple const& _t) : t(_t) {}
template<typename... Indexes>
inline Return operator()(Indexes... idx) const {
boost::array<index, sizeof...(Indexes)> i = { idx... };
if (std::get<0>(t)(i)) {
return std::get<1>(t)(i);
} else {
return std::get<2>(t)(i);
}
}
};
// This is an auxiliary function to make life easier
template<typename Return, typename Tuple>
inline MakeSubIndexOp<Return, Tuple>
make_subindex_op(const Tuple&& t) {
return MakeSubIndexOp<Return, Tuple>(std::forward<Tuple const>(t));
}
// Again this is an auxiliary struct that includes a fake begin/end calls.
template<typename _T>
struct wrap_ptr {
typedef typename std::remove_reference<_T>::type T;
T const *s, *e;
inline wrap_ptr(T const *_start, T const *_end)
: s(_start), e(_end) { }
inline T const *begin() const { return s; }
inline T const *end() const { return e; }
};
// This is the main instrumentation to build the lazy branching on a pair of arrays
template<typename Return, typename CondArray, typename Array1, typename Array2>
struct CondHelper {
// If we have rvalue ref we strip the reference to ensure
// the value is copied in the tuple. If we are provided
// with a ref just keep it that way to avoid copying the dense
// array.
typedef std::tuple<
typename strip_rvalue_ref<CondArray>::type,
typename strip_rvalue_ref<Array1>::type,
typename strip_rvalue_ref<Array2>::type
> btuple;
typedef typename std::remove_reference<CondArray>::type PureCond;
typedef typename std::remove_reference<Array1>::type PureArray1;
typedef typename std::remove_reference<Array2>::type PureArray2;
// This create a functor on the 3 array
static inline auto make_op(CondArray cond, Array1 a1, Array2 a2)
-> decltype(FusedCondDetails::make_subindex_op<Return, btuple>(
btuple(std::forward<CondArray>(cond), std::forward<Array1>(a1), std::forward<Array2>(a2))
)) {
return FusedCondDetails::make_subindex_op<Return, btuple>(
btuple(std::forward<CondArray>(cond), std::forward<Array1>(a1), std::forward<Array2>(a2))
);
}
// This wraps the shapes
template<typename A>
static inline
auto build_shape(A a)
-> decltype(wrap_ptr<decltype(a.shape()[0])>(a.shape(), a.shape() + ArrayDim<A>::D)) {
return wrap_ptr<decltype(a.shape()[0])>(a.shape(), a.shape() + ArrayDim<A>::D);
}
// Similar for index_bases
template<typename A>
static inline
auto build_index_base(A a)
-> decltype(wrap_ptr<decltype(a.index_bases()[0])>(a.index_bases(), a.index_bases() + ArrayDim<A>::D)) {
return wrap_ptr<decltype(a.index_bases()[0])>(a.index_bases(), a.index_bases() + ArrayDim<A>::D);
}
// This is the main builder, creates a new virtual array out of the 3-array
static inline auto make(CondArray condition, Array1 array1, Array2 array2)
-> decltype( b_fused_idx<Return, ArrayDim<CondArray>::D>(
make_op(std::forward<CondArray>(condition), std::forward<Array1>(array1), std::forward<Array2>(array2)),
build_shape(condition),
build_index_base(condition))
) {
return b_fused_idx<Return, ArrayDim<CondArray>::D>(
make_op(std::forward<CondArray>(condition), std::forward<Array1>(array1), std::forward<Array2>(array2)),
build_shape(condition),
build_index_base(condition)
);
}
};
}
// Finally a helper function that makes life real easy to the caller.
// Universal references are probed: if they are rvalue ref, then a copy is
// made, otherwise we just keep the lvalue ref.
template<typename Return, typename CondArray, typename Array1, typename Array2>
inline auto b_cond_fused(CondArray&& condition, Array1&& array1, Array2&& array2)
{
return FusedCondDetails::CondHelper<
Return,
decltype(condition),
decltype(array1),
decltype(array2)
>::make(std::forward<CondArray>(condition),std::forward<Array1>(array1),std::forward<Array2>(array2));
}
}
#endif