// // rarray - Runtime arrays: template classes for pointer-based, // runtime, reference counted, multi-dimensional // arrays. Documentation in rarraydoc.pdf // // Copyright (c) 2013-2024 Ramses van Zon // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #ifndef RARRAY_H_ #define RARRAY_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __cplusplus >= 201103L #include #include #include #include #define RA_VERSION "v2.7.0" #define RA_VERSION_NUMBER 2007000 #ifdef RA_BOUNDSCHECK #define RA_CHECKORSAY(a, b) if (not(a)) throw std::out_of_range(std::string(b) + " in function " + std::string(__PRETTY_FUNCTION__) + " (rarray:" + std::to_string(__LINE__) + ")") #define RA_noboundscheck false #else #define RA_CHECKORSAY(a, b) #define RA_noboundscheck true #endif #if !defined(RA_FORCE_inline) # if defined(_MSC_VER) # if _MSC_VER >= 1900 # define RA_FORCE_inline [[msvc::forceinline]] inline # else # define RA_FORCE_inline __forceinline inline # endif # elif defined(__INTEL_COMPILER) # define RA_FORCE_inline __forceinline inline # elif defined(__clang__) # if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) # define RA_FORCE_inline __attribute__((always_inline)) inline # else # define RA_FORCE_inline [[gnu::always_inline]] inline # endif # elif defined(__GNUC__) # if __GNUC__ < 4 || (GNU_C == 4 && __GNUC_MINOR < 8) # define RA_FORCE_inline __attribute__((always_inline)) inline # else # define RA_FORCE_inline [[gnu::always_inline]] inline # endif # else # define RA_FORCE_inline inline # endif #endif namespace ra { using size_type = ssize_t; using index_type = ssize_t; using rank_type = int; } // namespace ra namespace ra { namespace detail { template class shared_buffer { public: using size_type = ::ra::size_type; inline shared_buffer() noexcept { uninit(); } explicit inline shared_buffer(size_type asize) : data_(nullptr), orig_(nullptr), size_(0), refs_(nullptr) { auto to_be_data = std::unique_ptr(new T[asize]); refs_ = new std::atomic(1); data_ = to_be_data.release(); orig_ = data_; size_ = asize; } inline shared_buffer(size_type asize, T* adata) noexcept(RA_noboundscheck) : data_(adata), orig_(nullptr), size_(asize), refs_(nullptr) { RA_CHECKORSAY(adata, "nullptr given as data"); } inline shared_buffer(const shared_buffer& other) noexcept : data_(other.data_), orig_(other.orig_), size_(other.size_), refs_(other.refs_) { incref(); } inline shared_buffer(shared_buffer&& from) noexcept : data_(from.data_), orig_(from.orig_), size_(from.size_), refs_(from.refs_) { from.uninit(); } inline auto operator=(const shared_buffer& other) noexcept -> shared_buffer& { if (this != &other) { decref(); data_ = other.data_; orig_ = other.orig_; size_ = other.size_; refs_ = other.refs_; incref(); } return *this; } inline void operator=(shared_buffer&& from) noexcept { decref(); data_ = from.data_; orig_ = from.orig_; size_ = from.size_; refs_ = from.refs_; from.uninit(); } inline ~shared_buffer() noexcept { decref(); } inline auto operator[](size_type index) const noexcept(RA_noboundscheck) -> const T& { RA_CHECKORSAY(index >= 0 && index < size(), "element not in buffer"); return data_[index]; } inline auto operator[](size_type index) noexcept(RA_noboundscheck) -> T& { RA_CHECKORSAY(index >= 0 && index < size(), "element not in buffer"); return data_[index]; } inline auto at(size_type index) const -> const T& { if (index < 0 || index >= size_) throw std::out_of_range("shared_buffer::at"); return data_[index]; } inline auto at(size_type index) -> T& { if (index < 0 || index >= size_) throw std::out_of_range("shared_buffer::at"); return data_[index]; } inline auto slice(size_type from, size_type to) -> shared_buffer { if (from < 0 || to < 0 || from > size_ || to > size_) throw std::out_of_range("shared_buffer::slice"); shared_buffer result(*this); result.data_ += from; result.size_ = 0; if (from <= to) result.size_ = to - from; return result; } inline auto slice(size_type from, size_type to) const -> const shared_buffer { if (from < 0 || to < 0 || from > size_ || to > size_) throw std::out_of_range("shared_buffer::slice"); shared_buffer result(*this); result.data_ += from; result.size_ = 0; if (from <= to) result.size_ = to - from; return result; } inline auto size() const noexcept -> size_type { return size_; } inline auto copy() const -> shared_buffer { return shared_buffer(size_, cbegin(), cend()); } using iterator = T*; using const_iterator = const T*; inline auto begin() noexcept -> iterator { return data_; } inline auto end() noexcept -> iterator { return data_+size_; } inline auto begin() const noexcept -> const_iterator { return data_; } inline auto end() const noexcept -> const_iterator { return data_+size_; } inline auto cbegin() const noexcept -> const_iterator { return data_; } inline auto cend() const noexcept -> const_iterator { return data_+size_; } using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; inline auto rbegin() -> reverse_iterator { return std::reverse_iterator(data_+size_); } inline auto rend() -> reverse_iterator { return std::reverse_iterator(data_); } inline auto rbegin() const -> const_reverse_iterator { return std::reverse_iterator(data_+size_); } inline auto rend() const -> const_reverse_iterator { return std::reverse_iterator(data_); } inline auto crbegin() const -> const_reverse_iterator { return std::reverse_iterator(data_+size_); } inline auto crend() const -> const_reverse_iterator { return std::reverse_iterator(data_); } inline void resize(size_type newsize, bool keep_content = false) { if ( newsize < size_ && refs_ && (*refs_) == 1 ) { size_ = newsize; } else { auto newrefs = new std::atomic(1); T* newdata; try { newdata = new T[newsize]; } catch (...) { delete newrefs; throw; } if (keep_content) { try { size_type n = ((size_ < newsize)?size_:newsize); for (size_type i = 0; i < n; i++) newdata[i] = data_[i]; } catch (...) { delete newrefs; delete[] newdata; throw; } } decref(); data_ = newdata; orig_ = newdata; size_ = newsize; refs_ = newrefs; } } inline void fill(const T& value) { for (size_type i = 0; i < size_; i++) data_[i] = value; } void assign(size_type count, const T& value) { resize(count); fill(value); } template::value>::type> inline void assign(InputIt first, InputIt last) { resize(last-first); T* data = data_; for (InputIt it = first; it != last; it++) *(data++) = *it; } inline void assign(std::initializer_list ilist) { assign(ilist.begin(), ilist.end()); } private: T* data_; T* orig_; size_type size_; std::atomic* refs_; inline void uninit() noexcept { data_ = nullptr; orig_ = nullptr; size_ = 0; refs_ = nullptr; } inline void incref() noexcept { if (refs_) (*refs_)++; } inline void decref() noexcept { if (refs_) { if (--(*refs_) == 0) { delete[] orig_; delete refs_; uninit(); } } } template inline shared_buffer(size_type asize, InputIt first, InputIt last) : data_(nullptr), orig_(nullptr), size_(0), refs_(nullptr) { using noconstT = typename std::remove_const::type; #ifndef __ibmxl__ auto to_be_data = std::unique_ptr(new T[asize]{*first}); #else auto to_be_data = std::unique_ptr(const_cast(new typename std::remove_const::type[asize])); #endif std::copy(first, last, const_cast(to_be_data.get())); refs_ = new std::atomic(1); data_ = to_be_data.release(); orig_ = data_; size_ = asize; } }; } // namespace detail } // namespace ra namespace ra { namespace detail { class Offsets { public: inline explicit Offsets(const std::vector& extent) : rank_(static_cast(extent.size())), ndataoffsets_(0) { if (rank_ > 0) { size_type noffsets = 0; ndataoffsets_ = 1; for (rank_type i = rank_ - 1; i--; ) noffsets = extent[i]*(1 + noffsets); for (rank_type i = 0 ; i < rank_-1; i++) ndataoffsets_ *= extent[i]; offsets_.reserve(noffsets); offsets_.resize(noffsets); if (noffsets > 1) { size_type offsetnum = 0; size_type extenttot = extent[0]; for (rank_type i = 1; i < rank_ - 1; i++) { for (size_type j = 0; j < extenttot; j++) offsets_[offsetnum+j] = offsetnum + extenttot + j*extent[i]; offsetnum += extenttot; extenttot *= extent[i]; } ndataoffsets_ = extenttot; for (index_type j = 0; j < ndataoffsets_; j++) offsets_[offsetnum + j] = j*extent[rank_ - 1]; } } } template inline auto apply_offsets(T* data) const -> void*** { static_assert(sizeof(T*) == sizeof(void*) && sizeof(T*) == sizeof(void**), "rarray's Offsets requires all pointers to have the same size"); auto noffsets = (size_type)offsets_.size(); if (ndataoffsets_ == 0 && noffsets == 0) { return nullptr; } else if (ndataoffsets_ == 1 && noffsets == 0) { return reinterpret_cast(const_cast::type*>(data)); } else { void*** offsets = new void**[noffsets]; size_type i = 0; for (; i < noffsets - ndataoffsets_; i++) offsets[i] = reinterpret_cast(offsets) + offsets_[i]; for (; i < noffsets; i++) offsets[i] = reinterpret_cast( const_cast::type*>(data) + offsets_[i]); return offsets; } } inline auto get_num_data_offsets() const noexcept -> size_type { return ndataoffsets_; } inline auto get_num_offsets() const noexcept -> size_type { return (size_type)offsets_.size(); } inline auto get_rank() const noexcept -> rank_type { return rank_; } private: rank_type rank_; std::vector offsets_; size_type ndataoffsets_; }; } // namespace detail } // namespace ra namespace ra { namespace detail { template struct PointerArray { using type = typename PointerArray::type const*; using noconst_type = typename PointerArray::noconst_type*; }; template struct PointerArray { using type = T*; using noconst_type = T*; }; template struct PointerArray { using type = T&; using noconst_type = T&; }; template struct DataFromPtrs; template struct DataArrayFromPtrs; template class shared_shape { public: using ptrs_type = typename PointerArray::type; using size_type = ::ra::size_type; inline shared_shape() noexcept { uninit(); } inline shared_shape(const std::array& anextent, T* adata) : extent_(anextent), ptrs_(nullptr), refs_(nullptr), orig_(nullptr) { ra::detail::Offsets P({extent_.begin(), extent_.end()}); auto to_be_orig = std::unique_ptr(P.apply_offsets(adata)); if (R > 1) refs_ = new std::atomic(1); orig_ = to_be_orig.release(); ptrs_ = reinterpret_cast(orig_); if (R == 1) orig_ = nullptr; ndataoffsets_ = P.get_num_data_offsets(); } inline shared_shape(const shared_shape& other) noexcept : extent_(other.extent_), ptrs_(other.ptrs_), refs_(other.refs_), orig_(other.orig_), ndataoffsets_(other.ndataoffsets_) { incref(); } inline shared_shape(shared_shape&& other) noexcept : extent_(other.extent_), ptrs_(other.ptrs_), refs_(other.refs_), orig_(other.orig_), ndataoffsets_(other.ndataoffsets_) { other.uninit(); } inline auto operator=(const shared_shape& other) noexcept -> shared_shape& { if (this != &other) { decref(); extent_ = other.extent_; ptrs_ = other.ptrs_; refs_ = other.refs_; orig_ = other.orig_; ndataoffsets_ = other.ndataoffsets_; incref(); } return *this; } inline void operator=(shared_shape&& other) noexcept { decref(); extent_ = other.extent_; ptrs_ = other.ptrs_; refs_ = other.refs_; orig_ = other.orig_; ndataoffsets_ = other.ndataoffsets_; other.uninit(); } inline ~shared_shape() noexcept { decref(); } inline auto copy() const -> shared_shape { return shared_shape(extent_, data()); } inline void relocate(T* newdata) { if (R == 1) { ptrs_ = reinterpret_cast(newdata); } else if (R > 1) { std::ptrdiff_t shift = reinterpret_cast(newdata) - reinterpret_cast(data()); if (shift != 0) { if (refs_ && *refs_ > 1) *this = this->copy(); char** data_array = DataArrayFromPtrs::call(ptrs_); for (size_type i = 0; i < ndataoffsets_; i++) data_array[i] += shift; } } } inline void reshape(const std::array& newextent) { if (newextent != extent_) { if (size() == std::accumulate(&newextent[0], &newextent[R], 1, std::multiplies())) *this = shared_shape(newextent, data()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } } inline auto ptrs() const noexcept -> ptrs_type { return ptrs_; } inline auto data() const noexcept -> T* { return DataFromPtrs::call(ptrs_); } inline auto size() const noexcept -> size_type { return ndataoffsets_ * extent_[R-1]; } inline auto extent(rank_type i) const -> size_type { if (i < 0 || i >= R) throw std::out_of_range("shared_shape::extent(int)"); return extent_[i]; } inline auto extent() const noexcept -> const std::array& { return extent_; } inline auto at(size_type index) const -> shared_shape { if (R < 1 || index < 0 || index >= extent_[0]) throw std::out_of_range("shared_shape::at"); shared_shape result; if (R > 1) { for (rank_type i = 0; i < R-1; ++i) result.extent_[i] = extent_[i+1]; result.ptrs_ = ptrs_[index]; result.refs_ = refs_; result.orig_ = orig_; result.ndataoffsets_ = ndataoffsets_/extent_[0]; incref(); } return result; } inline auto slice(size_type beginindex, size_type endindex) const -> shared_shape { if (R < 1 || beginindex < 0 || beginindex >= extent_[0] || endindex < 0 || endindex > extent_[0]) throw std::out_of_range("shared_shape::slice"); shared_shape result; if (R > 1 && beginindex < endindex) { result.extent_[0] = endindex - beginindex; for (rank_type i = 1; i < R; ++i) result.extent_[i] = extent_[i]; result.ptrs_ = ptrs_ + beginindex; result.refs_ = refs_; result.orig_ = orig_; result.ndataoffsets_ = result.extent_[0]*(ndataoffsets_/extent_[0]); incref(); } return result; } private: std::array extent_; ptrs_type ptrs_; std::atomic* refs_; void*** orig_; size_type ndataoffsets_; inline void uninit() noexcept { ptrs_ = nullptr; orig_ = nullptr; refs_ = nullptr; ndataoffsets_ = 0; extent_.fill(0); } inline void incref() const noexcept { if (refs_) (*refs_)++; } inline void decref() noexcept { if (refs_) { if (--(*refs_) == 0) { if (R > 1) delete[] orig_; delete refs_; uninit(); } } } template friend class shared_shape; }; template class shared_shape { public: using ptrs_type = T; using size_type = ra::size_type; private: std::array extent_; ptrs_type ptrs_; std::atomic* refs_; void*** orig_; size_type ndataoffsets_; inline void incref() const noexcept {} template friend class shared_shape; }; template struct DataFromPtrs { static inline auto call(typename PointerArray::type ptrs) noexcept -> T* { return DataFromPtrs::call(*ptrs); } }; template struct DataFromPtrs { static inline auto call(typename PointerArray::type ptrs) noexcept -> T* { return reinterpret_cast(ptrs); } }; template struct DataArrayFromPtrs { static inline auto call(typename PointerArray::type ptrs) noexcept -> char** { return DataArrayFromPtrs::call(*ptrs); } }; template struct DataArrayFromPtrs { static inline auto call(typename PointerArray::type ptrs) noexcept -> char** { return reinterpret_cast(const_cast::type, 2>::noconst_type>(ptrs)); } }; template struct DataArrayFromPtrs { static inline auto call(typename PointerArray::type ptrs) noexcept -> char** { return nullptr; } }; } // namespace detail } // namespace ra namespace ra { enum class RESIZE { NO, ALLOWED }; enum class MISSING { SKIP, DEFAULT, REPEAT }; namespace detail { template class CommaOp; template class Bracket; template class ConstBracket; #define ExOp class template class Expr; template void init_shape_fill(unsigned N, size_type* sz, const T& l) {} template void init_shape_fill(unsigned N, size_type* sz, const std::initializer_list& l) { sz[0] = std::max(sz[0], static_cast(l.size())); if (N >= 2) for (const auto& lsub : l) init_shape_fill(N - 1, sz + 1, lsub); } template auto init_shape(const std::initializer_list& l) -> std::array { std::array sz{0}; init_shape_fill(N, &sz[0], l); return sz; } template struct init_list_prop { enum { rank = 0 }; using type = T; }; template struct init_list_prop> { enum { rank = init_list_prop::rank + 1 }; using type = typename init_list_prop::type; }; } // namespace detail template class rarray { public: using value_type = T; using difference_type = ::ra::size_type; using size_type = ::ra::size_type; using iterator = T*; using const_iterator = const T*; using parray_t = typename detail::PointerArray::type; using noconst_parray_t = typename detail::PointerArray::noconst_type; inline rarray() : buffer_(), shape_() {} template::type> inline explicit rarray(size_type n0) : buffer_(n0), shape_({n0}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1) : buffer_(n0*n1), shape_({n0, n1}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2) : buffer_(n0*n1*n2), shape_({n0, n1, n2}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3) : buffer_(n0*n1*n2*n3), shape_({n0, n1, n2, n3}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4) : buffer_(n0*n1*n2*n3*n4), shape_({n0, n1, n2, n3, n4}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5) : buffer_(n0*n1*n2*n3*n4*n5), shape_({n0, n1, n2, n3, n4, n5}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6) : buffer_(n0*n1*n2*n3*n4*n5*n6), shape_({n0, n1, n2, n3, n4, n5, n6}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7), shape_({n0, n1, n2, n3, n4, n5, n6, n7}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7*n8), shape_({n0, n1, n2, n3, n4, n5, n6, n7, n8}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7*n8*n9), shape_({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9}, buffer_.begin()) {} template::type> inline rarray(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9, size_type n10) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7*n8*n9*n10), shape_({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10}, buffer_.begin()) {} explicit inline rarray(const size_type* anextent) : buffer_(std::accumulate(anextent, anextent+R, 1, std::multiplies())), shape_(reinterpret_cast&>(*anextent), buffer_.begin()) {} template::type> inline rarray(T* buffer, size_type n0) : buffer_(n0, buffer), shape_({n0}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1) : buffer_(n0*n1, buffer), shape_({n0, n1}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2) : buffer_(n0*n1*n2, buffer), shape_({n0, n1, n2}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3) : buffer_(n0*n1*n2*n3, buffer), shape_({n0, n1, n2, n3}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4) : buffer_(n0*n1*n2*n3*n4, buffer), shape_({n0, n1, n2, n3, n4}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5) : buffer_(n0*n1*n2*n3*n4*n5, buffer), shape_({n0, n1, n2, n3, n4, n5}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6) : buffer_(n0*n1*n2*n3*n4*n5*n6, buffer), shape_({n0, n1, n2, n3, n4, n5, n6}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7, buffer), shape_({n0, n1, n2, n3, n4, n5, n6, n7}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7*n8, buffer), shape_({n0, n1, n2, n3, n4, n5, n6, n7, n8}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7*n8*n9, buffer), shape_({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9}, buffer) {} template::type> inline rarray(T* buffer, size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9, size_type n10) : buffer_(n0*n1*n2*n3*n4*n5*n6*n7*n8*n9*n10, buffer), shape_({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10}, buffer) {} inline rarray(T* buffer, const size_type* anextent) : buffer_(std::accumulate(anextent, anextent+R, 1, std::multiplies()), buffer), shape_(reinterpret_cast&>(*anextent), buffer) {} template::type> inline explicit rarray(T (&a)[Z]) : buffer_(Z, a), shape_({Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[Y][Z]) : buffer_(Y*Z, *a), shape_({Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[X][Y][Z]) : buffer_(X*Y*Z, **a), shape_({X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[W][X][Y][Z]) : buffer_(W*X*Y*Z, ***a), shape_({W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[V][W][X][Y][Z]) : buffer_(V*W*X*Y*Z, ****a), shape_({V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[U][V][W][X][Y][Z]) : buffer_(U*V*W*X*Y*Z, *****a), shape_({U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[T_][U][V][W][X][Y][Z]) : buffer_(T_*U*V*W*X*Y*Z, ******a), shape_({T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[S][T_][U][V][W][X][Y][Z]) : buffer_(S*T_*U*V*W*X*Y*Z, *******a), shape_({S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[R_][S][T_][U][V][W][X][Y][Z]) : buffer_(R_*S*T_*U*V*W*X*Y*Z, ********a), shape_({R_, S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[Q][R_][S][T_][U][V][W][X][Y][Z]) : buffer_(Q*R_*S*T_*U*V*W*X*Y*Z, *********a), shape_({Q, R_, S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&a)[P][Q][R_][S][T_][U][V][W][X][Y][Z]) : buffer_(P*Q*R_*S*T_*U*V*W*X*Y*Z, **********a), shape_({P, Q, R_, S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[Z]) : buffer_(Z, a), shape_({Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[Y][Z]) : buffer_(Y*Z, *a), shape_({Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[X][Y][Z]) : buffer_(X*Y*Z, **a), shape_({X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[W][X][Y][Z]) : buffer_(W*X*Y*Z, ***a), shape_({W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[V][W][X][Y][Z]) : buffer_(V*W*X*Y*Z, ****a), shape_({V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[U][V][W][X][Y][Z]) : buffer_(U*V*W*X*Y*Z, *****a), shape_({U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[T_][U][V][W][X][Y][Z]) : buffer_(T_*U*V*W*X*Y*Z, ******a), shape_({T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[S][T_][U][V][W][X][Y][Z]) : buffer_(S*T_*U*V*W*X*Y*Z, *******a), shape_({S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[R_][S][T_][U][V][W][X][Y][Z]) : buffer_(R_*S*T_*U*V*W*X*Y*Z, ********a), shape_({R_, S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[Q][R_][S][T_][U][V][W][X][Y][Z]) : buffer_(Q*R_*S*T_*U*V*W*X*Y*Z, *********a), shape_({Q, R_, S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} template::type> inline explicit rarray(T (&&a)[P][Q][R_][S][T_][U][V][W][X][Y][Z]) : buffer_(P*Q*R_*S*T_*U*V*W*X*Y*Z, **********a), shape_({P, Q, R_, S, T_, U, V, W, X, Y, Z}, buffer_.begin()) {} RA_FORCE_inline rarray(const rarray &a) noexcept : buffer_(a.buffer_), shape_(a.shape_) {} inline auto operator=(const rarray &a) noexcept -> rarray& { buffer_ = a.buffer_; shape_ = a.shape_; return *this; } inline rarray(rarray&& x) noexcept : buffer_(std::move(x.buffer_)), shape_(std::move(x.shape_)) {} inline auto operator=(rarray&& x) noexcept -> rarray& { buffer_ = std::move(x.buffer_); shape_ = std::move(x.shape_); return *this; } inline auto operator=(const T& e) noexcept(RA_noboundscheck && std::is_nothrow_copy_constructible()) -> detail::CommaOp { RA_CHECKORSAY(!empty(), "assignment to unsized array"); RA_CHECKORSAY(size() > 0, "assignment with more elements than in array"); T* first = &(buffer_[0]); if (size() > 0) *first = e; else return detail::CommaOp(nullptr, nullptr); detail::CommaOp co(first+1, first+size()-1); return co; } RA_FORCE_inline ~rarray() = default; template RA_FORCE_inline void fill(const detail::Expr& e); template RA_FORCE_inline void form(const detail::Expr& e); template RA_FORCE_inline auto operator=(const detail::Expr& e) -> rarray&; template RA_FORCE_inline auto operator+=(const detail::Expr& e) -> rarray&; template RA_FORCE_inline auto operator-=(const detail::Expr& e) -> rarray&; template RA_FORCE_inline auto operator*=(const detail::Expr& e) -> rarray&; template RA_FORCE_inline auto operator/=(const detail::Expr& e) -> rarray&; template RA_FORCE_inline auto operator%=(const detail::Expr& e) -> rarray&; template::type> inline void reshape(size_type n0, RESIZE resize_policy = RESIZE::NO) { if ( (size() == n0) || (resize_policy == RESIZE::ALLOWED && size() >= n0) ) shape_ = detail::shared_shape({n0}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1)) shape_ = detail::shared_shape({n0, n1}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2)) shape_ = detail::shared_shape({n0, n1, n2}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3)) shape_ = detail::shared_shape({n0, n1, n2, n3}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4*n5 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4*n5)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4*n5*n6 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4*n5*n6)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4*n5*n6*n7 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4*n5*n6*n7)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4*n5*n6*n7*n8 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4*n5*n6*n7*n8)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7, n8}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4*n5*n6*n7*n8*n9 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4*n5*n6*n7*n8*n9)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } template::type> inline void reshape(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9, size_type n10, RESIZE resize_policy = RESIZE::NO) { if (size() == n0*n1*n2*n3*n4*n5*n6*n7*n8*n9*n10 || (resize_policy == RESIZE::ALLOWED && size() >= n0*n1*n2*n3*n4*n5*n6*n7*n8*n9*n10)) shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10}, buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } inline void reshape(const size_type* newshape, RESIZE resize_policy = RESIZE::NO) { size_type newsize = std::accumulate(newshape, newshape+R, 1, std::multiplies()); if (size() == newsize || (resize_policy == RESIZE::ALLOWED && size() >= newsize)) shape_ = detail::shared_shape((const std::array&)(*newshape), buffer_.begin()); else throw std::out_of_range(std::string("Incompatible dimensions in function ") + std::string(__PRETTY_FUNCTION__)); } inline auto copy() const -> rarray { rarray clone; clone.buffer_ = buffer_.copy(); clone.shape_ = shape_.copy(); clone.shape_.relocate(clone.buffer_.begin()); return clone; } template::value>::type> inline operator const rarray&() const noexcept { return const_ref(); } constexpr auto rank() const noexcept -> int { return R; } inline auto empty() const noexcept -> bool { return buffer_.cbegin() == nullptr; } inline auto extent(int i) const -> size_type { return shape_.extent(i); } RA_FORCE_inline auto shape() const noexcept -> const size_type* { return &(shape_.extent()[0]); } inline auto size() const noexcept -> size_type { return shape_.size(); } inline auto data() noexcept -> T* { return buffer_.begin(); } inline auto data() const noexcept -> const T* { return buffer_.begin(); } inline auto ptr_array() const noexcept -> parray_t { return shape_.ptrs(); } inline auto noconst_ptr_array() const noexcept -> noconst_parray_t { return const_cast(shape_.ptrs()); } inline auto const_ref() const noexcept -> const rarray& { return reinterpret_cast&>(*this); } RA_FORCE_inline void clear() noexcept { shape_ = detail::shared_shape(); buffer_ = detail::shared_buffer(); } inline void fill(const T& value) { buffer_.fill(value); } private: template::type> inline void fill_g(std::initializer_list list, MISSING missing_policy) { auto filler = list.begin(); for (size_type i = 0; i < extent(0); i++) { if (filler != list.end()) { buffer_[i] = *filler++; if (filler == list.end()) { if (missing_policy == MISSING::REPEAT) filler = list.begin(); else if (missing_policy == MISSING::SKIP) break; } } else { buffer_[i] = T{}; } } } template::type> inline void fill_g(std::initializer_list list, MISSING missing_policy) { auto filler = list.begin(); for (size_type i = 0; i < extent(0); i++) { if (filler != list.end()) { at(i).fill_g(*filler++, missing_policy); if (filler == list.end()) { if (missing_policy == MISSING::REPEAT) filler = list.begin(); else if (missing_policy == MISSING::SKIP) break; } } else { at(i).fill(T{}); } } } template inline void form_g(std::initializer_list list, MISSING missing_policy) { std::array newshape = detail::init_shape(list); size_type newsize = std::accumulate(newshape.begin(), newshape.end(), 1, std::multiplies()); buffer_ = detail::shared_buffer(newsize); shape_ = detail::shared_shape(newshape, buffer_.begin()); fill_g(list, missing_policy); } public: template::type> inline void fill(std::initializer_list list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void fill(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { fill_g(list, missing_policy); } template::type> inline void form(std::initializer_list list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list< std::initializer_list>>>>>>>>>> list, MISSING missing_policy = MISSING::DEFAULT) { form_g(list, missing_policy); } template::type> inline void form(size_type n0, const T& value) { buffer_ = detail::shared_buffer(n0); shape_ = detail::shared_shape({n0}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, const T& value) { buffer_ = detail::shared_buffer(n0*n1); shape_ = detail::shared_shape({n0, n1}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2); shape_ = detail::shared_shape({n0, n1, n2}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3); shape_ = detail::shared_shape({n0, n1, n2, n3}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4); shape_ = detail::shared_shape({n0, n1, n2, n3, n4}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4*n5); shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4*n5*n6); shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4*n5*n6*n7); shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4*n5*n6*n7*n8); shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7, n8}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4*n5*n6*n7*n8*n9); shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9}, buffer_.begin()); buffer_.fill(value); } template::type> inline void form(size_type n0, size_type n1, size_type n2, size_type n3, size_type n4, size_type n5, size_type n6, size_type n7, size_type n8, size_type n9, size_type n10, const T& value) { buffer_ = detail::shared_buffer(n0*n1*n2*n3*n4*n5*n6*n7*n8*n9*n10); shape_ = detail::shared_shape({n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10}, buffer_.begin()); buffer_.fill(value); } inline auto begin() noexcept -> iterator { return buffer_.begin(); } inline auto begin() const noexcept -> const_iterator { return buffer_.begin(); } inline auto cbegin() const noexcept -> const_iterator { return buffer_.cbegin(); } inline auto end() noexcept -> iterator { return buffer_.end(); } inline auto end() const noexcept -> const_iterator { return buffer_.end(); } inline auto cend() const noexcept -> const_iterator { return buffer_.cend(); } inline auto index(const T& a, int i) const -> size_type { ptrdiff_t linearindex = &a - &(buffer_[0]); if (linearindex < 0 || linearindex >= size()) throw "element not in array"; const size_type* extent_ = shape(); for (int j = R-1; j > i; j--) linearindex /= extent_[j]; return linearindex % extent_[i]; } inline auto index(const iterator& iter, int i) const -> size_type { return index(*iter, i); } inline auto index(const T& a) const -> std::array { std::array ind; ptrdiff_t linearindex = &a - &(buffer_[0]); RA_CHECKORSAY(linearindex >= 0 && linearindex < size(), "element not in array"); int j = R; const size_type* extent_ = shape(); while (j-->0) { ind[j] = linearindex % extent_[j]; linearindex /= extent_[j]; } return ind; } inline auto index(const iterator& i) const -> std::array { return index(*i); } template> RA_FORCE_inline auto at(size_type i) -> typename std::enable_if::type { if (i < 0 || i >= extent(0)) throw std::out_of_range("rarray::at"); size_type stride = size()/extent(0); return ra::rarray(buffer_.slice(i*stride, (i+1)*stride), shape_.at(i)); } template RA_FORCE_inline auto at(size_type i) -> typename std::enable_if::type { if (i < 0 || i >= extent(0)) throw std::out_of_range("rarray::at"); return shape_.ptrs()[i]; } template> RA_FORCE_inline auto at(size_type i) const -> typename std::enable_if::type { if (i < 0 || i >= extent(0)) throw std::out_of_range("rarray::at"); size_type stride = size()/extent(0); return ra::rarray(buffer_.slice(i*stride, (i+1)*stride), shape_.at(i)); } template RA_FORCE_inline auto at(size_type i) const -> typename std::enable_if::type { if (i < 0 || i >= extent(0)) throw std::out_of_range("rarray::at"); return shape_.ptrs()[i]; } RA_FORCE_inline auto slice(size_type beginindex, size_type endindex) -> rarray { if (beginindex < 0 || beginindex >= shape_.extent(0) || endindex < 0 || endindex > shape_.extent(0)) throw std::out_of_range("rarray::slice"); size_type stride = size()/extent(0); return {buffer_.slice(beginindex*stride, endindex*stride), shape_.slice(beginindex, endindex)}; } template RA_FORCE_inline auto operator[](size_type i) -> typename std::enable_if::type { RA_CHECKORSAY(i >= 0 && i < extent(0), "index out of range of array"); return buffer_.begin()[i]; } template RA_FORCE_inline auto operator[](size_type i) -> typename std::enable_if >::type { return { *this, i, this->shape() }; } template RA_FORCE_inline auto operator[](size_type i) const -> typename std::enable_if::type { RA_CHECKORSAY(i >= 0 && i < extent(0), "index out of range of array"); return buffer_.cbegin()[i]; } template RA_FORCE_inline auto operator[](size_type i) const -> typename std::enable_if>::type { return { *this, i, this->shape() }; } #if __cpp_multidimensional_subscript >= 202110L template RA_FORCE_inline auto operator[](size_type i, Ts... args) -> typename std::enable_if::type { return operator[](i)[args...]; } template RA_FORCE_inline auto operator[](size_type i, Ts... args) const -> typename std::enable_if::type { return operator[](i)[args...]; } #endif RA_FORCE_inline auto leval(size_type i) const -> const T&; private: friend class detail::Bracket>; friend class detail::ConstBracket>; RA_FORCE_inline auto private_at(size_type i) -> typename detail::PointerArray::type { return shape_.ptrs()[i]; } RA_FORCE_inline auto private_at(size_type i) const -> typename detail::PointerArray::type { return shape_.ptrs()[i]; } friend class rarray; inline rarray(detail::shared_buffer&& abuffer, detail::shared_shape&& ashape) : buffer_(std::forward>(abuffer)), shape_(std::forward>(ashape)) {} inline rarray(const detail::shared_buffer&& abuffer, detail::shared_shape&& ashape) : buffer_(std::forward>(const_cast&& >(abuffer))), shape_(std::forward>(ashape)) {} detail::shared_buffer buffer_; detail::shared_shape shape_; }; namespace detail { template class CommaOp { public: RA_FORCE_inline auto operator,(const T& e) -> CommaOp& { RA_CHECKORSAY(ptr_ != nullptr && last_ != nullptr, "invalid comma operator"); RA_CHECKORSAY(ptr_ <= last_, "assignment with more elements than in array"); if (ptr_ && ptr_ <= last_) *ptr_++ = e; return *this; } private: RA_FORCE_inline CommaOp(T* ptr, T* last) noexcept(RA_noboundscheck) : ptr_(ptr), last_(last) { RA_CHECKORSAY(ptr_ != nullptr && last_ != nullptr, "invalid comma operator"); } T *ptr_; T * const last_; template friend class ra::rarray; }; template class Bracket { private: P& parent_; ///< what array/array expression is being accessed size_type index_; const size_type* shape_; public: RA_FORCE_inline auto operator[](size_type nextindex) noexcept(RA_noboundscheck) -> Bracket { return { *this, nextindex, shape_ + 1 }; } RA_FORCE_inline operator decltype(parent_.at(index_)) () { return parent_.at(index_); } Bracket(const Bracket&) = delete; Bracket(const Bracket&&) = delete; auto operator=(const Bracket&) -> Bracket& = delete; auto operator=(const Bracket&&) -> Bracket& = delete; ~Bracket() = default; private: RA_FORCE_inline Bracket(P& parent, size_type i, const size_type* shape) noexcept(RA_noboundscheck) : parent_(parent), index_(i), shape_(shape) { RA_CHECKORSAY(index_ >=0 && index_ < shape_[0], "index out of range of array"); } RA_FORCE_inline auto private_at(size_type nextindex) noexcept(RA_noboundscheck) -> decltype(parent_.private_at(index_)[nextindex]) { return parent_.private_at(index_)[nextindex]; } template friend class ra::rarray; template friend class detail::Bracket; public: RA_FORCE_inline auto at(size_type nextindex) -> decltype(parent_.at(index_).at(nextindex)) { return parent_.at(index_).at(nextindex); } #if __cpp_multidimensional_subscript >= 202110L template RA_FORCE_inline auto operator[](size_type nextindex, Ts... args) -> typename std::enable_if::type { return operator[](nextindex)[args...]; } #endif }; template class Bracket { private: P& parent_; ///< what array/array expression is being accessed size_type index_; const size_type* shape_; public: RA_FORCE_inline auto operator[](size_type nextindex) noexcept(RA_noboundscheck) -> T& { RA_CHECKORSAY(nextindex >= 0 && nextindex < shape_[1], "index out of range of array"); return parent_.private_at(index_)[nextindex]; } RA_FORCE_inline operator decltype(parent_.at(index_)) () { return parent_.at(index_); } Bracket(const Bracket&) = delete; Bracket(const Bracket&&) = delete; auto operator=(const Bracket&) -> Bracket& = delete; auto operator=(const Bracket&&) -> Bracket& = delete; ~Bracket() = default; private: RA_FORCE_inline Bracket(P& parent, size_type i, const size_type* shape) noexcept(RA_noboundscheck) : parent_(parent), index_(i), shape_(shape) { RA_CHECKORSAY(index_ >= 0 && index_ < shape_[0], "index out of range of array"); } template friend class ra::rarray; template friend class detail::Bracket; public: RA_FORCE_inline auto at(size_type nextindex) -> decltype(parent_.at(index_).at(nextindex)) { return parent_.at(index_).at(nextindex); } }; template class ConstBracket { private: const P& parent_; size_type index_; public: RA_FORCE_inline auto operator[](size_type nextindex) const noexcept(RA_noboundscheck) -> ConstBracket { return { *this, nextindex, shape_ + 1 }; } RA_FORCE_inline operator decltype(parent_.at(index_)) () { return parent_.at(index_); } ConstBracket(const ConstBracket&) = delete; ConstBracket(const ConstBracket&&) = delete; auto operator=(const ConstBracket&) -> ConstBracket& = delete; auto operator=(const ConstBracket&&) -> ConstBracket& = delete; ~ConstBracket() = default; private: const size_type* shape_; RA_FORCE_inline ConstBracket(const P& parent, size_type i, const size_type* shape) noexcept(RA_noboundscheck) : parent_(parent), index_(i), shape_(shape) { RA_CHECKORSAY(index_ >= 0 && index_ < shape_[0], "index out of range of array"); } RA_FORCE_inline auto private_at(size_type nextindex) const noexcept(RA_noboundscheck) -> decltype(parent_.private_at(index_)[nextindex]) { return parent_.private_at(index_)[nextindex]; } template friend class ra::rarray; template friend class detail::ConstBracket; public: RA_FORCE_inline auto at(size_type nextindex) const -> decltype(parent_.at(index_).at(nextindex)) { return parent_.at(index_).at(nextindex); } #if __cpp_multidimensional_subscript >= 202110L template RA_FORCE_inline auto operator[](size_type nextindex, Ts... args) -> typename std::enable_if::type { return operator[](nextindex)[args...]; } #endif }; template class ConstBracket { private: const P& parent_; size_type index_; public: RA_FORCE_inline auto operator[](size_type nextindex) const noexcept(RA_noboundscheck) -> const T& { RA_CHECKORSAY(nextindex >=0 && nextindex < shape_[1], "index out of range of array"); return parent_.private_at(index_)[nextindex]; } RA_FORCE_inline operator decltype(parent_.at(index_)) () { return parent_.at(index_); } ConstBracket(const ConstBracket&) = delete; ConstBracket(const ConstBracket&&) = delete; auto operator=(const ConstBracket&) -> ConstBracket& = delete; auto operator=(const ConstBracket&&) -> ConstBracket& = delete; ~ConstBracket() = default; private: const size_type* shape_; RA_FORCE_inline ConstBracket(const P& parent, size_type i, const size_type* shape) noexcept(RA_noboundscheck) : parent_(parent), index_(i), shape_(shape) { RA_CHECKORSAY(index_ >= 0 && index_ < shape_[0], "index out of range of array"); } template friend class ra::rarray; template friend class detail::ConstBracket; public: RA_FORCE_inline auto at(size_type nextindex) const -> decltype(parent_.at(index_).at(nextindex)) { return parent_.at(index_).at(nextindex); } }; } // namespace detail template inline auto linspace(S x1, S x2, size_type n = 0, bool end_incl = true) -> rarray { if (n == 0) { if (x2 > x1) n = static_cast(x2 - x1 + end_incl); else n = static_cast(x1 - x2 + end_incl); } rarray x(n); for (size_type i = 0; i < n; i++) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" x[i] = x1 + static_cast(((x2-x1)*i)/(n-end_incl)); #pragma GCC diagnostic pop if (end_incl) x[n-1] = x2; return x; } template class Xrange { private: struct const_iterator { using difference_type = std::ptrdiff_t; using value_type = T; using pointer = T*; using reference = T&; using iterator_category = std::input_iterator_tag; inline const_iterator(): i_(0), di_(1), b_(0) {} inline const_iterator(T i, T di, T b): i_(i), di_(di), b_(b) {} inline auto operator!=(const const_iterator& other) const -> bool { return i_ != other.i_; } inline auto operator==(const const_iterator& other) const -> bool { return i_ == other.i_ && di_ == other.di_ && b_ == other.b_; } inline auto operator++() -> const_iterator& { i_ += di_; if (di_ > 0 && i_ >= b_) i_ = b_; if (di_ < 0 && i_ <= b_) i_ = b_; return *this; } inline auto operator++(int) -> const_iterator { const const_iterator temp = *this; this->operator++(); return temp; } inline auto operator*() const -> const T& { return i_; } T i_, di_, b_; }; T a_, b_, d_; public: inline Xrange(T a, T b, T d) : a_(a), b_(a + static_cast(static_cast(std::ceil(static_cast(b-a)/static_cast(d)))*d)), d_(d) {} inline auto begin() const -> const_iterator { return const_iterator(a_, d_, b_); } inline auto end() const -> const_iterator { return const_iterator(b_, d_, b_); } inline auto size() const -> size_t { return static_cast((b_-a_)/d_); } }; template inline auto xrange(T end) -> Xrange { return Xrange(static_cast(0), end, static_cast(1)); } template inline auto xrange(S begin, T end) -> Xrange { return Xrange(static_cast(begin), end, static_cast(1)); } template inline auto xrange(S begin, T end, U step) -> Xrange { return Xrange(static_cast(begin), end, static_cast(step)); } template inline auto extent(const A &a, int i) -> size_type { switch (i) { case 0: return std::extent(); case 1: return std::extent(); case 2: return std::extent(); case 3: return std::extent(); case 4: return std::extent(); case 5: return std::extent(); case 6: return std::extent(); case 7: return std::extent(); case 8: return std::extent(); case 9: return std::extent(); case 10: return std::extent(); default: throw std::out_of_range("ra::extent"); return 0; } } template inline auto extent(const rarray &a, int i) -> size_type { return a.extent(i); } } // namespace ra namespace std { template struct remove_all_extents> { using type = T; }; template struct rank> { static const size_t value = R; }; } // namespace std namespace ra { namespace detail { template struct Deref { static inline auto access(typename PointerArray::type p, const size_type* indices) -> T& { return Deref::access(p[indices[0]-1], indices+1); } }; template struct Deref { static inline auto access(typename PointerArray::type p, const size_type* indices) -> T& { return p[indices[0]-1]; } }; template struct StringToValue { static inline void get(const std::string& input, T& output) { std::stringstream str(input); str >> output; } }; template<> struct StringToValue { static inline void get(const std::string& input, std::string& output) { output = input; } }; enum class token { BRACEOPEN, BRACECLOSE, COMMA, DATASTRING, END }; inline auto toch(const token& Token) -> char { switch (Token) { case token::BRACEOPEN: return '{'; case token::BRACECLOSE: return '}'; case token::COMMA: return ','; case token::DATASTRING: return '$'; case token::END: return '.'; default: return '\0'; } } template inline auto text_output(std::ostream &o, const rarray& r) -> std::ostream& { if (!r.empty()) { o << "{\n"; for (size_type i = 0; i < r.extent(0); i++) { if (i > 0) o << ",\n"; o << r.at(i); } o << "\n}"; } else { for (int i = 0; i < R; i++) o << '{'; for (int i = 0; i < R; i++) o << '}'; o << "\n"; } return o; } template inline auto text_output(std::ostream &o, const rarray& r) -> std::ostream& { if (!r.empty()) { o << '{'; for (size_type i = 0; i < r.extent(0); i++) { if (i) o << ','; std::stringstream strstr; std::string result; const T& val = r.at(i); strstr << val; result = strstr.str(); if (result.find_first_of("{,}#") != std::string::npos && ! (result[0] == '(' && result[result.size()-1] == ')' && result.substr(1, result.size()-2).find_first_of(")") == std::string::npos) ) o << '#' << result.size() << ':'; o << result; } o << '}'; } else { o << "{}"; } return o; } static inline auto get_but_eat_newline(std::istream & in) -> char { char ch1 = '\n'; while (ch1 == '\n' && !in.eof()) in >> ch1; return ch1; } static inline auto get_but_eat_whitespace(std::istream & in) -> char { char ch1; in >> ch1; return ch1; } template inline auto parse_shape(std::istream & in) -> std::pair>, size_type[R]> { std::pair>, size_type[R]> wholeresult; size_t init_file_ptr = in.tellg(); try { std::list>& result = wholeresult.first; size_type* shape = wholeresult.second; size_type current_shape[R]; for (rank_type i = 0; i < R; i++) { current_shape[i] = 1; shape[i] = 0; if (get_but_eat_newline(in) != '{') throw std::istream::failure("Format error"); else result.push_back({token::BRACEOPEN, ""}); } int current_depth = R-1; while (current_depth >= 0) { if (current_depth == R-1) { char lastchar; std::string word = ""; do { if (word == "") { lastchar = get_but_eat_newline(in); } else { in.get(lastchar); } if (lastchar != ',' && lastchar != '}') { word += lastchar; } if (word == "#") { word = ""; std::string skipstr; do { in.get(lastchar); skipstr += lastchar; } while (lastchar != ':'); int skip = atoi(skipstr.c_str()); for (int i = 0; i < skip; i++) { char nextchar; in.get(nextchar); word += nextchar; } } else if (word == "(") { const int safeguardcount = 1024*1024; int count = 0; while (lastchar != ')' && count < safeguardcount) { in.get(lastchar); word += lastchar; count++; } in.get(lastchar); } if (lastchar == ',') { result.push_back({token::DATASTRING, word}); result.push_back({token::COMMA, ""}); word = ""; current_shape[current_depth]++; } } while (lastchar != '}'); result.push_back({token::DATASTRING, word}); result.push_back({token::BRACECLOSE, ""}); if (shape) if (shape[current_depth] < current_shape[current_depth]) shape[current_depth] = current_shape[current_depth]; current_depth--; } else { switch (get_but_eat_whitespace(in)) { case ',': result.push_back({token::COMMA, ""}); current_shape[current_depth]++; break; case '{': result.push_back({token::BRACEOPEN, ""}); current_depth++; current_shape[current_depth] = 1; break; case '}': result.push_back({token::BRACECLOSE, ""}); if (shape) if (shape[current_depth] < current_shape[current_depth]) shape[current_depth] = current_shape[current_depth]; current_depth--; break; default: throw std::istream::failure("Format error"); } } } result.push_back({token::END, ""}); } catch (std::istream::failure& e) { in.seekg(init_file_ptr, in.beg); in.setstate(std::ios::failbit); throw; } return wholeresult; } template inline void parse_strings(const std::pair>, size_type[R]> & tokens, typename PointerArray::type p) { size_type index[R]; int current_depth = -1; for (auto& tokenpair : tokens.first) { switch (tokenpair.first) { case token::BRACEOPEN: current_depth++; index[current_depth] = 1; break; case token::BRACECLOSE: current_depth--; break; case token::COMMA: index[current_depth]++; break; case token::DATASTRING: StringToValue::get(tokenpair.second, Deref::access(p, index)); break; case token::END: break; } if (tokenpair.first == token::END) break; } } } // namespace detail template inline auto operator<<(std::ostream &o, const rarray& r) -> std::ostream& { return detail::text_output(o, r); } template inline auto operator>>(std::istream &in, rarray& r) -> std::istream& { auto X = detail::parse_shape(in); size_type* Xextent = X.second; if (std::accumulate(Xextent, Xextent+R, 1, std::multiplies()) <= r.size()) r.reshape(Xextent, RESIZE::ALLOWED); else r = rarray(Xextent); detail::parse_strings(X, r.ptr_array()); return in; } } // namespace ra #undef RA_CHECKORSAY #undef RA_FORCE_inline #undef RA_noboundscheck #define EXTENT(A, I) ra::extent(A, I) #define RARRAY(A) rarray::type, std::rank::value>(A) #define INDEX(A, X, I) RARRAY(A).index(X, I) using ra::rarray; using ra::linspace; using ra::xrange; template using rvector = rarray; template using rmatrix = rarray; template using rtensor = rarray; #else #error "This file requires C++11 or newer support." #endif #endif