/*+ ARES/HADES/BORG Package -- ./libLSS/tools/fused_cond.hpp Copyright (C) 2014-2020 Guilhem Lavaux Copyright (C) 2009-2020 Jens Jasche Additional contributions from: Guilhem Lavaux (2023) +*/ #ifndef _LIBLSS_FUSED_CONDITIONAL_HPP #define _LIBLSS_FUSED_CONDITIONAL_HPP #include #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 struct ArrayDim { typedef typename std::remove_reference::type Array; static constexpr size_t D = Array::dimensionality; }; template using ArrayIndex = boost::array::D>; // This functor allows invokes another functor with the indexes wrapped in a boost::array template struct MakeSubIndexOp { Tuple const t; inline MakeSubIndexOp(Tuple const&& _t) : t(_t) {} inline MakeSubIndexOp(Tuple const& _t) : t(_t) {} template inline Return operator()(Indexes... idx) const { boost::array 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 inline MakeSubIndexOp make_subindex_op(const Tuple&& t) { return MakeSubIndexOp(std::forward(t)); } // Again this is an auxiliary struct that includes a fake begin/end calls. template 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 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::type, typename strip_rvalue_ref::type, typename strip_rvalue_ref::type > btuple; typedef typename std::remove_reference::type PureCond; typedef typename std::remove_reference::type PureArray1; typedef typename std::remove_reference::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( btuple(std::forward(cond), std::forward(a1), std::forward(a2)) )) { return FusedCondDetails::make_subindex_op( btuple(std::forward(cond), std::forward(a1), std::forward(a2)) ); } // This wraps the shapes template static inline auto build_shape(A a) -> decltype(wrap_ptr(a.shape(), a.shape() + ArrayDim::D)) { return wrap_ptr(a.shape(), a.shape() + ArrayDim::D); } // Similar for index_bases template static inline auto build_index_base(A a) -> decltype(wrap_ptr(a.index_bases(), a.index_bases() + ArrayDim::D)) { return wrap_ptr(a.index_bases(), a.index_bases() + ArrayDim::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::D>( make_op(std::forward(condition), std::forward(array1), std::forward(array2)), build_shape(condition), build_index_base(condition)) ) { return b_fused_idx::D>( make_op(std::forward(condition), std::forward(array1), std::forward(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 inline auto b_cond_fused(CondArray&& condition, Array1&& array1, Array2&& array2) { return FusedCondDetails::CondHelper< Return, decltype(condition), decltype(array1), decltype(array2) >::make(std::forward(condition),std::forward(array1),std::forward(array2)); } } #endif