#ifndef included_ArraySizeClass #define included_ArraySizeClass #include "common/Utilities.h" #include #include #include #include #include #include #include #include #include #if defined(__CUDA_ARCH__) #include #define HOST_DEVICE __host__ __device__ #else #define HOST_DEVICE #endif #if defined(USING_GCC) || defined(USING_CLANG) #define ARRAY_ATTRIBUTE HOST_DEVICE __attribute__((always_inline)) #else #define ARRAY_ATTRIBUTE HOST_DEVICE #endif #if (defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG) #define CHECK_ARRAY_LENGTH(i, length) \ do { \ if (i >= length) \ throw std::out_of_range("Index exceeds array bounds"); \ } while (0) #else #define CHECK_ARRAY_LENGTH(i, length) \ do { \ } while (0) #endif // Forward declerations class FunctionTable; template > class Array; //! Simple range class template class Range final { public: //! Empty constructor Range() : i(0), j(-1), k(1) {} /*! * Create a range i:k:j (or i:j) * @param i_ Starting value * @param j_ Ending value * @param k_ Increment value */ Range(const TYPE &i_, const TYPE &j_, const TYPE &k_ = 1) : i(i_), j(j_), k(k_) {} //! Get the number of values in the range size_t size() const { if (std::is_integral::value) { return (static_cast(j) - static_cast(i)) / static_cast(k); } else if (std::is_floating_point::value) { double tmp = static_cast((j - i)) / static_cast(k); return static_cast(floor(tmp + 1e-12) + 1); } else if (std::is_same>::value || std::is_same>::value) { double tmp = std::real((j - i) / (k)); return static_cast(floor(tmp + 1e-12) + 1); } else { ERROR("Unsupported type for range"); } } public: TYPE i, j, k; }; //! Simple class to store the array dimensions class ArraySize final { public: //! Empty constructor ArraySize() : d_ndim(1), d_length(0), d_N{0, 1, 1, 1, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension */ ArraySize(size_t N1) : d_ndim(1), d_length(N1), d_N{N1, 1, 1, 1, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension * @param N2 Number of elements in the second dimension */ ArraySize(size_t N1, size_t N2) : d_ndim(2), d_length(N1 * N2), d_N{N1, N2, 1, 1, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension * @param N2 Number of elements in the second dimension * @param N3 Number of elements in the third dimension */ ArraySize(size_t N1, size_t N2, size_t N3) : d_ndim(3), d_length(N1 * N2 * N3), d_N{N1, N2, N3, 1, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension * @param N2 Number of elements in the second dimension * @param N3 Number of elements in the third dimension * @param N4 Number of elements in the fourth dimension */ ArraySize(size_t N1, size_t N2, size_t N3, size_t N4) : d_ndim(4), d_length(N1 * N2 * N3 * N4), d_N{N1, N2, N3, N4, 1} {} /*! * Create the vector size * @param N1 Number of elements in the first dimension * @param N2 Number of elements in the second dimension * @param N3 Number of elements in the third dimension * @param N4 Number of elements in the fourth dimension * @param N5 Number of elements in the fifth dimension */ ArraySize(size_t N1, size_t N2, size_t N3, size_t N4, size_t N5) : d_ndim(5), d_length(N1 * N2 * N3 * N4 * N5), d_N{N1, N2, N3, N4, N5} { } /*! * Create from initializer list * @param N Size of the array * @param ndim Number of dimensions */ ArraySize(std::initializer_list N, int ndim = -1) : d_ndim(N.size()), d_length(0), d_N{0, 1, 1, 1, 1} { if (ndim >= 0) d_ndim = ndim; if (d_ndim > 5) throw std::out_of_range("Maximum number of dimensions exceeded"); auto it = N.begin(); for (size_t i = 0; i < d_ndim; i++, ++it) d_N[i] = *it; d_length = 1; for (unsigned long i : d_N) d_length *= i; if (d_ndim == 0) d_length = 0; } /*! * Create from raw pointer * @param ndim Number of dimensions * @param dims Dimensions */ ArraySize(size_t ndim, const size_t *dims) : d_ndim(ndim), d_length(0), d_N{0, 1, 1, 1, 1} { if (d_ndim > 5) throw std::out_of_range("Maximum number of dimensions exceeded"); for (size_t i = 0; i < ndim; i++) d_N[i] = dims[i]; d_length = 1; for (unsigned long i : d_N) d_length *= i; if (d_ndim == 0) d_length = 0; } /*! * Create from std::array * @param N Size of the array */ template ArraySize(const std::array &N) : ArraySize(NDIM, N.data()) {} /*! * Create from std::vector * @param N Size of the array */ inline ArraySize(const std::vector &N) : ArraySize(N.size(), N.data()) {} // Copy/assignment constructors ArraySize(ArraySize &&rhs) = default; ArraySize(const ArraySize &rhs) = default; ArraySize &operator=(ArraySize &&rhs) = default; ArraySize &operator=(const ArraySize &rhs) = default; /*! * Access the ith dimension * @param i Index to access */ ARRAY_ATTRIBUTE size_t operator[](size_t i) const { return d_N[i]; } //! Return the number of dimensions ARRAY_ATTRIBUTE uint8_t ndim() const { return d_ndim; } //! Return the number of dimensions ARRAY_ATTRIBUTE size_t size() const { return d_ndim; } //! Return the total number of elements in the array ARRAY_ATTRIBUTE size_t length() const { return d_length; } //! Resize the dimension void resize(uint8_t dim, size_t N) { if (dim >= d_ndim) throw std::out_of_range("Invalid dimension"); d_N[dim] = N; d_length = 1; for (unsigned long i : d_N) d_length *= i; } /*! * Reshape the Array so that the number of dimensions is the * max of ndim and the largest dim>1. * @param ndim Desired number of dimensions */ void setNdim(uint8_t ndim) { d_ndim = std::max(ndim, d_ndim); } /*! * Remove singleton dimensions */ void squeeze() { d_ndim = 0; for (uint8_t i = 0; i < maxDim(); i++) { if (d_N[i] != 1) d_N[d_ndim++] = d_N[i]; } } //! Returns an iterator to the beginning const size_t *begin() const { return d_N; } //! Returns an iterator to the end const size_t *end() const { return d_N + d_ndim; } // Check if two array sizes are equal ARRAY_ATTRIBUTE bool operator==(const ArraySize &rhs) const { return d_ndim == rhs.d_ndim && memcmp(d_N, rhs.d_N, sizeof(d_N)) == 0; } // Check if two array sizes are equal (ignoring the dimension) ARRAY_ATTRIBUTE bool approxEqual(const ArraySize &rhs) const { return (length() == 0 && rhs.length() == 0) || memcmp(d_N, rhs.d_N, sizeof(d_N)) == 0; } //! Check if two matrices are not equal ARRAY_ATTRIBUTE bool operator!=(const ArraySize &rhs) const { return d_ndim != rhs.d_ndim || memcmp(d_N, rhs.d_N, sizeof(d_N)) != 0; } //! Maximum supported dimension ARRAY_ATTRIBUTE static uint8_t maxDim() { return 5; } //! Get the index ARRAY_ATTRIBUTE size_t index(size_t i) const { CHECK_ARRAY_LENGTH(i, d_length); return i; } //! Get the index ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2) const { size_t index = i1 + i2 * d_N[0]; CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2, size_t i3) const { size_t index = i1 + d_N[0] * (i2 + d_N[1] * i3); CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2, size_t i3, size_t i4) const { size_t index = i1 + d_N[0] * (i2 + d_N[1] * (i3 + d_N[2] * i4)); CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index ARRAY_ATTRIBUTE size_t index(size_t i1, size_t i2, size_t i3, size_t i4, size_t i5) const { size_t index = i1 + d_N[0] * (i2 + d_N[1] * (i3 + d_N[2] * (i4 + d_N[3] * i5))); CHECK_ARRAY_LENGTH(index, d_length); return index; } //! Get the index size_t index(const std::array &i) const { size_t j = 0; for (size_t m = 0, N = 1; m < 5; m++) { j += i[m] * N; N *= d_N[m]; } return j; } //! Get the index size_t index(std::initializer_list i) const { size_t N = 1; size_t j = 0; size_t m = 0; for (size_t k : i) { j += k * N; N *= d_N[m++]; } return j; } //! Convert the index to ijk values std::array ijk(size_t index) const { CHECK_ARRAY_LENGTH(index, d_length); size_t i0 = index % d_N[0]; index = index / d_N[0]; size_t i1 = index % d_N[1]; index = index / d_N[1]; size_t i2 = index % d_N[2]; index = index / d_N[2]; size_t i3 = index % d_N[3]; index = index / d_N[3]; return {i0, i1, i2, i3, index}; } //! Convert the index to ijk values void ijk(size_t index, size_t *x) const { CHECK_ARRAY_LENGTH(index, d_length); x[0] = index % d_N[0]; index = index / d_N[0]; x[1] = index % d_N[1]; index = index / d_N[1]; x[2] = index % d_N[2]; index = index / d_N[2]; x[3] = index % d_N[3]; index = index / d_N[3]; x[4] = index; } private: uint8_t d_ndim; size_t d_length; size_t d_N[5]; }; // Function to concatenate dimensions of two array sizes inline ArraySize cat(const ArraySize &x, const ArraySize &y) { if (x.ndim() + y.ndim() > 5) throw std::out_of_range("Maximum number of dimensions exceeded"); size_t N[5] = {0}; for (int i = 0; i < x.ndim(); i++) N[i] = x[i]; for (int i = 0; i < y.ndim(); i++) N[i + x.ndim()] = y[i]; return ArraySize(x.ndim() + y.ndim(), N); } // Operator overloads inline ArraySize operator*(size_t v, const ArraySize &x) { size_t N[5] = {v * x[0], v * x[1], v * x[2], v * x[3], v * x[4]}; return ArraySize(x.ndim(), N); } inline ArraySize operator*(const ArraySize &x, size_t v) { size_t N[5] = {v * x[0], v * x[1], v * x[2], v * x[3], v * x[4]}; return ArraySize(x.ndim(), N); } inline ArraySize operator-(const ArraySize &x, size_t v) { size_t N[5] = {x[0] - v, x[1] - v, x[2] - v, x[3] - v, x[4] - v}; return ArraySize(x.ndim(), N); } inline ArraySize operator+(const ArraySize &x, size_t v) { size_t N[5] = {x[0] + v, x[1] + v, x[2] + v, x[3] + v, x[4] + v}; return ArraySize(x.ndim(), N); } inline ArraySize operator+(size_t v, const ArraySize &x) { size_t N[5] = {x[0] + v, x[1] + v, x[2] + v, x[3] + v, x[4] + v}; return ArraySize(x.ndim(), N); } #if defined(USING_ICC) ENABLE_WARNINGS #endif #endif