/*+ ARES/HADES/BORG Package -- ./libLSS/tools/fused_array.hpp Copyright (C) 2014-2020 Guilhem Lavaux Copyright (C) 2009-2020 Jens Jasche Additional contributions from: Guilhem Lavaux (2023) +*/ #ifndef __LIBLSS_FUSED_ARRAY_HPP #define __LIBLSS_FUSED_ARRAY_HPP #include #include "libLSS/tools/nary_arrays.hpp" #include "libLSS/tools/fused_assign.hpp" #include "libLSS/tools/ref_tools.hpp" namespace LibLSS { namespace FUSE_detail { namespace FusedArrayTypes { typedef boost::multi_array_types::size_type size_type; typedef boost::multi_array_types::index index; } // namespace FusedArrayTypes template struct BoostArray { enum { NumDims = ND }; typedef boost::multi_array type; typedef T element; }; template struct BoostRefArray { enum { NumDims = ND }; typedef boost::const_multi_array_ref type; typedef T element; }; template struct ConstantArray { enum { NumDims = 0 }; typedef T type; typedef T element; }; struct FusedArray_base { typedef FusedArrayTypes::size_type size_type; typedef FusedArrayTypes::index index; }; template < typename ArrayTuple, typename Operation, std::size_t N, std::size_t order, bool shaped> struct FusedArray_view; template struct FusedArray_view_scalar : FusedArray_base { static constexpr bool Shaped = ArrayTuple::Shaped; Operation op; const ArrayTuple &tuple; typedef typename ArrayTuple::element element; typedef FusedArrayTypes::size_type size_type; typedef boost::array subindex; subindex idx; inline FusedArray_view_scalar( Operation _op, const ArrayTuple &_tuple, const subindex &i) : op(_op), tuple(_tuple), idx(i) {} inline const element &apply() const { return details::apply_op(op, tuple, idx); }; inline operator element() const { return details::apply_op(op, tuple, idx); } }; // *************************** Begin of view with no shape support **************** template < typename ArrayTuple, typename Operation, std::size_t N, std::size_t order> struct FusedArray_view : FusedArray_base { static constexpr bool Shaped = ArrayTuple::Shaped; Operation op; ArrayTuple tuple; typedef boost::array subindex; typedef FusedArray_view subview; typedef FusedArray_view subview0; subindex idx; inline FusedArray_view( Operation _op, const ArrayTuple &_tuple, const subindex &i) : op(_op), tuple(_tuple), idx(i) {} template inline typename std::enable_if< subindex::static_size == T_sub::static_size, subview0>::type operator()(T_sub const &i) const { return subview0(op, tuple, i); } template inline typename std::enable_if< order != N && (N - order + T_sub::static_size == subindex::static_size), subview0>::type operator()(T_sub const &i) const { subindex j; for (unsigned int k = 0; k < N - order; k++) j[k] = idx[k]; for (unsigned int k = N - order; k < N; k++) j[k] = i[k - (N - order)]; return subview0(op, tuple, j); } inline subview operator[](size_type i) const { subindex idx0 = idx; idx0[N - order] = i; return subview(op, tuple, idx0); } }; template struct FusedArray_view : FusedArray_view_scalar { static constexpr bool Shaped = ArrayTuple::Shaped; typedef FusedArray_view_scalar super; typedef typename super::subindex subindex; inline FusedArray_view( Operation _op, const ArrayTuple &_tuple, const subindex &i) : FusedArray_view_scalar(_op, _tuple, i) {} }; // a (0,0) is never shaped. template struct FusedArray_view : FusedArray_base { static constexpr bool Shaped = ArrayTuple::Shaped; typedef typename ArrayTuple::element element; Operation op; const ArrayTuple &tuple; const index *indexes; const size_type *shapes; typedef boost::array subindex; typedef FusedArray_view subview; inline FusedArray_view( Operation _op, const ArrayTuple &_tuple, const subindex &i) : op(_op), tuple(_tuple) {} inline const element &apply() const { return details::apply_op(op, tuple, subindex()); }; inline operator element() const { return details::apply_op(op, tuple, subindex()); } inline subview operator[](size_type i) const { // Ignore everything return subview(op, tuple, subindex()); } }; // *************************** End of view with no shape support **************** // *************************** Begin of view with shape support **************** template < typename ArrayTuple, typename Operation, std::size_t N, std::size_t order> struct FusedArray_view : FusedArray_base { typedef typename ArrayTuple::element element; Operation op; ArrayTuple tuple; static constexpr bool Shaped = ArrayTuple::Shaped; typedef boost::array subindex_root; typedef boost::array subindex; typedef FusedArray_view subview; typedef FusedArray_view subview0; subindex_root idx; const index *v_index; const size_type *v_size; inline FusedArray_view( Operation _op, const ArrayTuple &_tuple, const subindex_root &i, const index *_index, const size_type *_size) : op(_op), tuple(_tuple), idx(i), v_index(_index), v_size(_size) {} template inline typename std::enable_if::type operator[](size_type i) const { subindex_root idx0 = idx; idx0[N - order] = i; return details::apply_op(op, tuple, idx0); } template inline typename std::enable_if::type operator[](size_type i) const { subindex_root idx0 = idx; idx0[N - order] = i; return subview(op, tuple, idx0, v_index + 1, v_size + 1); } inline subview0 operator()(subindex_root const &i) const { return subview0(op, tuple, i, 0, 0); } template inline typename std::enable_if::type operator()(subindex const &i) const { subindex_root idx0 = idx; for (unsigned int k = 0; k < order; k++) idx0[N - order + k] = i[k]; return subview0(op, tuple, idx0, 0, 0); } inline const size_type *shape() const { return v_size; } inline const index *index_bases() const { return v_index; } }; template struct FusedArray_view : FusedArray_view_scalar { typedef FusedArray_view_scalar super; static constexpr bool Shaped = ArrayTuple::Shaped; typedef typename super::subindex subindex; typedef typename super::index index; typedef typename super::size_type size_type; inline FusedArray_view( Operation _op, const ArrayTuple &_tuple, const subindex &i, const index *_index, const size_type *_size) : FusedArray_view_scalar(_op, _tuple, i) {} // Here we discard _index and _size as we have just a zero-dim array. }; // *************************** End of view with shape support **************** template struct NumDimDecrement { enum { value = N - 1 }; }; template <> struct NumDimDecrement<0> { enum { value = 0 }; }; template < typename ArrayTuple, typename Operation, bool ShapeTuple = ArrayTuple::Shaped> struct FusedArray; template struct FusedArray : FusedArray_base { enum { NumDims = ArrayTuple::NumDims }; enum { dimensionality = ArrayTuple::NumDims }; typedef typename ArrayTuple::element element; static constexpr bool Shaped = true; ArrayTuple const a; Operation const op; typedef boost::array subindex; typedef FusedArray_view< ArrayTuple, Operation, NumDims, NumDimDecrement::value, ArrayTuple::Shaped> subview; inline FusedArray(ArrayTuple p_a, Operation p_op) : a(p_a), op(p_op) {} inline subview operator[](FusedArrayTypes::size_type i) const { subindex idx; if (NumDims >= 1) idx[0] = i; return subview(op, a, idx, a.index_bases() + 1, a.shape() + 1); } inline element operator()(const subindex &idx) const { return details::apply_op(op, a, idx); } inline const size_type *shape() const { return a.shape(); } inline const index *index_bases() const { return a.index_bases(); } inline size_type num_elements() const { return a.num_elements(); } }; // No shape then strip all the shape related functions. template struct FusedArray : FusedArray_base { enum { NumDims = ArrayTuple::NumDims }; enum { dimensionality = ArrayTuple::NumDims }; typedef typename ArrayTuple::element element; static constexpr bool Shaped = false; ArrayTuple const a; Operation const op; typedef boost::array subindex; typedef FusedArray_view< ArrayTuple, Operation, NumDims, NumDimDecrement::value, ArrayTuple::Shaped> subview; inline FusedArray(ArrayTuple p_a, Operation p_op) : a(p_a), op(p_op) {} inline subview operator[](FusedArrayTypes::size_type i) const { subindex idx; if (NumDims >= 1) idx[0] = i; return subview(op, a, idx); } inline element operator()(const subindex &idx) const { return details::apply_op(op, a, idx); } }; // This is a new API using Variadic templates. The interface is incompatible with b_fused // thus a new name. The operator has to be put at the front of the list to // support an arbitrary number of arrays in the list. // This variant can only be used if there is at least one array // in the list, thus this specialization. // The no-array variant is as usual using the ArrayNullTuple variant. // This function is compatible both with temporary objects and stable objects. // The universal reference mechanism (Array1&& etc) grabs everything and produce // references, even to rvalues. However reference to rvalues is unsafe and likely produce // segv in most cases for our specific use. So we strip the rvalue reference and convert // it back to a normal copy in that case. That allows slicing, intricated fused calls etc. template using remove_const_ref = typename boost::remove_const< typename boost::remove_reference::type>::type; template < typename Return, typename Operation, typename Array1, typename... Arrays> inline auto b_va_fused(Operation op, Array1 &&a1, Arrays &&... arrays) { typedef typename boost::remove_reference::type BareArray1; typedef std::tuple< typename strip_rvalue_ref::type, typename strip_rvalue_ref::type...> btuple; typedef ArrayTuple< BareArray1::dimensionality, Return, btuple, DetectShaped::type>::Shaped> RealTuple; return FusedArray( RealTuple(btuple(a1, arrays...)), op); } template inline auto b_va_fused(Operation op) { typedef ArrayNullTuple RealTuple; return FusedArray(RealTuple(), op); } template < typename Return, typename Array1, typename Array2, typename Array3, typename Array4, typename Operation> inline auto b_fused( const Array1 &a1, const Array2 &a2, const Array3 &a3, const Array4 &a4, Operation op) { typedef typename std::tuple< const Array1 &, const Array2 &, const Array3 &, const Array4 &> tuple; typedef ArrayTuple RealTuple; return FusedArray( RealTuple(std::forward_as_tuple(a1, a2, a3, a4)), op); } template < typename Return, typename Array1, typename Array2, typename Array3, typename Operation> inline auto b_fused( const Array1 &a1, const Array2 &a2, const Array3 &a3, Operation op) { typedef typename std::tuple tuple; typedef ArrayTuple RealTuple; return FusedArray( RealTuple(std::forward_as_tuple(a1, a2, a3)), op); } template < typename Return, typename Array1, typename Array2, typename Operation> inline auto b_fused(const Array1 &a1, const Array2 &a2, Operation op) { typedef typename std::tuple tuple; typedef ArrayTuple RealTuple; return FusedArray( RealTuple(std::forward_as_tuple(a1, a2)), op); } template inline auto b_fused(const Array1 &a1, Operation op) { typedef typename std::tuple tuple; typedef ArrayTuple RealTuple; return FusedArray( RealTuple(std::forward_as_tuple(a1)), op); } template inline auto b_fused(Operation op) { typedef ArrayNullTuple RealTuple; return FusedArray(RealTuple(), op); } /** * This builds a virtual array with N-dimensions, whose elements * are of type Return. Each access to the element call the provided * operator, with the corresponding requested index. * The signature should thus be "op(size_t, ...)->Return" with number of arguments * corresponding to the number of dimensions. * This array is "infinite", i.e. it does not have any specified bound, which may be * required for some assignment operation. * * @param op a functor that must be Copy-Constructible * @return a Copy-Constructible expression */ template inline auto b_fused_idx(Operation op) { typedef ArrayNullTuple RealTuple; return FusedArray(RealTuple(), op); } /** * This builds a virtual array with N-dimensions, whose elements * are of type Return. Each access to the element call the provided * operator, with the corresponding requested index. * The signature should thus be "op(size_t, ...)->Return" with number of arguments * corresponding to the number of dimensions. * This array is finite with extents indicated by "f_extents". * * @param op a functor that must be Copy-Constructible. * @param f_extents extent of the new virtual array. * @return a Copy-Constructible expression */ template < typename Return, std::size_t N, typename ExtentType, typename Operation> inline auto b_fused_idx(Operation op, const ExtentType &f_extents) { typedef ArrayNullTupleExtent RealTuple; return FusedArray(RealTuple(f_extents), op); } /** * Same as the above, but with extents split into two lists for easier management. */ template < typename Return, std::size_t N, typename ShapeList, typename IndexList, typename Operation> inline auto b_fused_idx( Operation op, const ShapeList &_shapes, const IndexList &_indexes) { typedef ArrayNullTupleExtent RealTuple; return FusedArray(RealTuple(_shapes, _indexes), op); } } // namespace FUSE_detail using FUSE_detail::b_fused; using FUSE_detail::b_fused_idx; using FUSE_detail::b_va_fused; } // namespace LibLSS #endif