Program Listing for File arrayContainer.hpp#
↰ Return to documentation for file (librapid/include/librapid/array/arrayContainer.hpp)
#ifndef LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP
#define LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP
namespace librapid {
namespace detail {
template<typename T>
struct SubscriptType {
using Scalar = T;
using Direct = const Scalar &;
using Ref = Scalar &;
};
template<typename T>
struct SubscriptType<Storage<T>> {
using Scalar = T;
using Direct = const Scalar &;
using Ref = Scalar &;
};
template<typename T, size_t... Dims>
struct SubscriptType<FixedStorage<T, Dims...>> {
using Scalar = T;
using Direct = const Scalar &;
using Ref = Scalar &;
};
#if defined(LIBRAPID_HAS_OPENCL)
template<typename T>
struct SubscriptType<OpenCLStorage<T>> {
using Scalar = T;
using Direct = const OpenCLRef<Scalar>;
using Ref = OpenCLRef<Scalar>;
};
#endif // LIBRAPID_HAS_OPENCL
#if defined(LIBRAPID_HAS_CUDA)
template<typename T>
struct SubscriptType<CudaStorage<T>> {
using Scalar = T;
using Direct = const detail::CudaRef<Scalar>;
using Ref = detail::CudaRef<Scalar>;
};
#endif // LIBRAPID_HAS_CUDA
} // namespace detail
namespace typetraits {
template<typename ShapeType_, typename StorageType_>
struct TypeInfo<array::ArrayContainer<ShapeType_, StorageType_>> {
static constexpr detail::LibRapidType type = detail::LibRapidType::ArrayContainer;
using Scalar = typename TypeInfo<StorageType_>::Scalar;
using Packet = std::false_type;
using Backend = typename TypeInfo<StorageType_>::Backend;
using ShapeType = ShapeType_;
using StorageType = StorageType_;
static constexpr int64_t packetWidth = 1;
static constexpr bool supportsArithmetic = TypeInfo<Scalar>::supportsArithmetic;
static constexpr bool supportsLogical = TypeInfo<Scalar>::supportsLogical;
static constexpr bool supportsBinary = TypeInfo<Scalar>::supportsBinary;
static constexpr bool allowVectorisation = []() {
if constexpr (typetraits::HasAllowVectorisation<TypeInfo<Scalar>>::value) {
return TypeInfo<Scalar>::allowVectorisation;
} else {
return TypeInfo<Scalar>::packetWidth > 1;
}
}();
#if defined(LIBRAPID_HAS_CUDA)
static constexpr cudaDataType_t CudaType = TypeInfo<Scalar>::CudaType;
static constexpr int64_t cudaPacketWidth = 1;
#endif // LIBRAPID_HAS_CUDA
static constexpr bool canAlign = false;
static constexpr int64_t canMemcpy = false;
};
LIBRAPID_DEFINE_AS_TYPE(typename StorageScalar,
array::ArrayContainer<Shape COMMA StorageScalar>);
LIBRAPID_DEFINE_AS_TYPE(typename StorageScalar,
array::ArrayContainer<MatrixShape COMMA StorageScalar>);
} // namespace typetraits
namespace array {
template<typename ShapeType_, typename StorageType_>
class ArrayContainer {
public:
using StorageType = StorageType_;
using ShapeType = ShapeType_;
using StrideType = Stride<ShapeType>;
using SizeType = typename ShapeType::SizeType;
using Scalar = typename StorageType::Scalar;
using Packet = typename typetraits::TypeInfo<Scalar>::Packet;
using Backend = typename typetraits::TypeInfo<ArrayContainer>::Backend;
using DirectSubscriptType = typename detail::SubscriptType<StorageType>::Direct;
using DirectRefSubscriptType = typename detail::SubscriptType<StorageType>::Ref;
ArrayContainer();
// template<typename T>
// LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::initializer_list<T> &data);
// template<typename T>
// explicit LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::vector<T> &data);
// clang-format off
#define SINIT(SUB_TYPE) std::initializer_list<SUB_TYPE>
#define SVEC(SUB_TYPE) std::vector<SUB_TYPE>
#define ARRAY_FROM_DATA_DEF(TYPE_INIT, TYPE_VEC) \
LIBRAPID_ALWAYS_INLINE ArrayContainer(const TYPE_INIT & data); \
explicit LIBRAPID_ALWAYS_INLINE ArrayContainer(const TYPE_VEC &data) ;
ARRAY_FROM_DATA_DEF(SINIT(Scalar), SVEC(Scalar));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(Scalar)), SVEC(SVEC(Scalar)));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(SINIT(Scalar))), SVEC(SVEC(SVEC(Scalar))));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(SINIT(SINIT(Scalar)))), SVEC(SVEC(SVEC(SVEC(Scalar)))));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(SINIT(SINIT(SINIT(Scalar))))), SVEC(SVEC(SVEC(SVEC(SVEC(Scalar))))));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(SINIT(SINIT(SINIT(SINIT(Scalar)))))), SVEC(SVEC(SVEC(SVEC(SVEC(SVEC(Scalar)))))));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(SINIT(SINIT(SINIT(SINIT(SINIT(Scalar))))))), SVEC(SVEC(SVEC(SVEC(SVEC(SVEC(SVEC(Scalar))))))));
ARRAY_FROM_DATA_DEF(SINIT(SINIT(SINIT(SINIT(SINIT(SINIT(SINIT(SINIT(Scalar)))))))), SVEC(SVEC(SVEC(SVEC(SVEC(SVEC(SVEC(SVEC(Scalar)))))))));
#undef SINIT
#undef SVEC
// clang-format on
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const Shape &shape);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const MatrixShape &shape);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const VectorShape &shape);
LIBRAPID_ALWAYS_INLINE ArrayContainer(const Shape &shape, const Scalar &value);
LIBRAPID_ALWAYS_INLINE ArrayContainer(const MatrixShape &shape, const Scalar &value);
LIBRAPID_ALWAYS_INLINE ArrayContainer(const VectorShape &shape, const Scalar &value);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const Scalar &value);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(ShapeType &&shape);
LIBRAPID_ALWAYS_INLINE ArrayContainer(const ArrayContainer &other) = default;
LIBRAPID_ALWAYS_INLINE ArrayContainer(ArrayContainer &&other) noexcept = default;
template<typename TransposeType>
LIBRAPID_ALWAYS_INLINE ArrayContainer(const Transpose<TransposeType> &trans);
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
LIBRAPID_ALWAYS_INLINE
ArrayContainer(const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB,
StorageTypeB, Alpha, Beta> &multiply);
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
assign(const detail::Function<desc, Functor_, Args...> &function);
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE
ArrayContainer(const detail::Function<desc, Functor_, Args...> &function);
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const ArrayContainer &other) = default;
template<typename ArrayViewType, typename ArrayViewScalar>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const array::GeneralArrayView<ArrayViewType, ArrayViewScalar> &view);
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const Scalar &value);
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(ArrayContainer &&other) noexcept = default;
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const detail::Function<desc, Functor_, Args...> &function);
template<typename TransposeType>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const Transpose<TransposeType> &transpose);
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB,
StorageTypeB, Alpha, Beta> &multiply);
template<typename T>
LIBRAPID_ALWAYS_INLINE detail::CommaInitializer<ArrayContainer>
operator<<(const T &value);
// template<typename ScalarTo = Scalar, typename BackendTo = Backend>
// LIBRAPID_NODISCARD auto cast() const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE ArrayContainer copy() const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index) const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index);
template<typename... Indices>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectSubscriptType
operator()(Indices... indices) const;
template<typename... Indices>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectRefSubscriptType
operator()(Indices... indices);
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar get() const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE typename ShapeType::SizeType
ndim() const noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto size() const noexcept -> size_t;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const ShapeType &shape() const noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const StorageType &storage() const noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE StorageType &storage() noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Packet packet(size_t index) const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar scalar(size_t index) const;
LIBRAPID_ALWAYS_INLINE void writePacket(size_t index, const Packet &value);
LIBRAPID_ALWAYS_INLINE void write(size_t index, const Scalar &value);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator+=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator-=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator*=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator/=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator%=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator&=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator|=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator^=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator<<=(const T &other);
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator>>=(const T &other);
LIBRAPID_ALWAYS_INLINE auto begin() const noexcept;
LIBRAPID_ALWAYS_INLINE auto end() const noexcept;
LIBRAPID_ALWAYS_INLINE auto begin();
LIBRAPID_ALWAYS_INLINE auto end();
template<typename T, typename Char, size_t N, typename Ctx>
void str(const fmt::formatter<T, Char> &format, char bracket, char separator,
const char (&formatString)[N], Ctx &ctx) const;
private:
ShapeType m_shape; // The shape type of the array
size_t m_size; // The size of the array
StorageType m_storage; // The storage container of the array
};
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE ArrayContainer<ShapeType_, StorageType_>::ArrayContainer() :
m_shape(StorageType_::template defaultShape<ShapeType_>()), m_size(0) {}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const Shape &shape) :
m_shape(shape),
m_size(shape.size()), m_storage(m_size) {
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const MatrixShape &shape) :
m_shape(shape),
m_size(shape.size()), m_storage(m_size) {
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const VectorShape &shape) :
m_shape(shape),
m_size(shape.size()), m_storage(m_size) {
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const Shape &shape,
const Scalar &value) :
m_shape(shape),
m_size(shape.size()), m_storage(m_size, value) {
static_assert(typetraits::IsStorage<StorageType_>::value ||
typetraits::IsOpenCLStorage<StorageType_>::value ||
typetraits::IsCudaStorage<StorageType_>::value,
"For a runtime-defined shape, "
"the storage type must be "
"either a Storage or a "
"CudaStorage object");
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const MatrixShape &shape,
const Scalar &value) :
m_shape(shape),
m_size(shape.size()), m_storage(m_size, value) {
static_assert(typetraits::IsStorage<StorageType_>::value ||
typetraits::IsOpenCLStorage<StorageType_>::value ||
typetraits::IsCudaStorage<StorageType_>::value,
"For a runtime-defined shape, "
"the storage type must be "
"either a Storage or a "
"CudaStorage object");
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const VectorShape &shape,
const Scalar &value) :
m_shape(shape),
m_size(shape.size()), m_storage(m_size, value) {
static_assert(typetraits::IsStorage<StorageType_>::value ||
typetraits::IsOpenCLStorage<StorageType_>::value ||
typetraits::IsCudaStorage<StorageType_>::value,
"For a runtime-defined shape, "
"the storage type must be "
"either a Storage or a "
"CudaStorage object");
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const Scalar &value) :
m_shape(detail::shapeFromFixedStorage(m_storage)),
m_size(m_shape.size()), m_storage(value) {
static_assert(typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(ShapeType_ &&shape) :
m_shape(std::forward<ShapeType_>(shape)),
m_size(m_shape.size()), m_storage(m_size) {}
template<typename ShapeType_, typename StorageType_>
template<typename TransposeType>
LIBRAPID_ALWAYS_INLINE ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const array::Transpose<TransposeType> &trans) {
*this = trans;
}
template<typename ShapeType_, typename StorageType_>
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
LIBRAPID_ALWAYS_INLINE ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB, StorageTypeB, Alpha,
Beta> &multiply) {
*this = multiply;
}
template<typename ShapeType_, typename StorageType_>
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::assign(
const detail::Function<desc, Functor_, Args...> &function) -> ArrayContainer & {
using FunctionType = detail::Function<desc, Functor_, Args...>;
m_storage.resize(function.size(), 0);
if constexpr (std::is_same_v<typename FunctionType::Backend, backend::OpenCL> ||
std::is_same_v<typename FunctionType::Backend, backend::CUDA>) {
detail::assign(*this, function);
} else {
#if !defined(LIBRAPID_OPTIMISE_SMALL_ARRAYS)
if (m_storage.size() > global::multithreadThreshold && global::numThreads > 1)
detail::assignParallel(*this, function);
else
#endif // LIBRAPID_OPTIMISE_SMALL_ARRAYS
detail::assign(*this, function);
}
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const detail::Function<desc, Functor_, Args...> &function) :
m_shape(function.shape()),
m_size(function.size()), m_storage(m_shape.size()) {
assign(function);
}
template<typename ShapeType_, typename StorageType_>
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const detail::Function<desc, Functor_, Args...> &function) -> ArrayContainer & {
return assign(function);
}
template<typename ShapeType_, typename StorageType_>
template<typename TransposeType>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const Transpose<TransposeType> &transpose) -> ArrayContainer & {
m_shape = transpose.shape();
m_size = transpose.size();
m_storage.resize(m_shape.size(), 0);
transpose.applyTo(*this);
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB, StorageTypeB, Alpha,
Beta> &arrayMultiply) -> ArrayContainer & {
m_shape = arrayMultiply.shape();
m_size = arrayMultiply.size();
m_storage.resize(m_shape.size(), 0);
arrayMultiply.applyTo(*this);
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename ArrayViewType, typename ArrayViewScalar>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const array::GeneralArrayView<ArrayViewType, ArrayViewScalar> &view) -> ArrayContainer & {
m_shape = view.shape();
m_size = view.size();
m_storage.resize(m_shape.size(), 0);
for (int64_t i = 0; i < m_size; ++i) { m_storage[i] = view.scalar(i); }
return *this;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator=(const Scalar &value)
-> ArrayContainer & {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::invalid_argument, m_shape.ndim() == 0, "Cannot assign a scalar to an array with {} dimensions", m_shape.ndim());
m_storage[0] = value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator<<(const T &value)
-> detail::CommaInitializer<ArrayContainer> {
return detail::CommaInitializer<ArrayContainer>(*this, static_cast<Scalar>(value));
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::copy() const
-> ArrayContainer {
ArrayContainer res(m_shape);
res.m_storage = m_storage.copy();
return res;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator[](int64_t index) const {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::out_of_range,
index >= 0 && index < static_cast<int64_t>(m_shape[0]),
"Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}",
index,
m_shape[0]);
return createGeneralArrayView(*this)[index];
// if constexpr (typetraits::IsOpenCLStorage<StorageType_>::value) {
// #if defined(LIBRAPID_HAS_OPENCL)
// ArrayContainer res;
// res.m_shape = m_shape.subshape(1, ndim());
// auto subSize = res.shape().size();
// int64_t storageSize = sizeof(typename StorageType_::Scalar);
// cl_buffer_region region {index * subSize * storageSize, subSize *
// storageSize}; res.m_storage =
// StorageType_(m_storage.data().createSubBuffer(
// StorageType_::bufferFlags,
// CL_BUFFER_CREATE_TYPE_REGION, ®ion), subSize,
// false); return res; #else LIBRAPID_ERROR("OpenCL support
// not enabled"); #endif // LIBRAPID_HAS_OPENCL } else if constexpr
//(typetraits::IsCudaStorage<StorageType_>::value) { #if defined(LIBRAPID_HAS_CUDA)
// ArrayContainer res;
// res.m_shape = m_shape.subshape(1, ndim());
// auto subSize = res.shape().size();
// Scalar *begin = m_storage.begin().get() + index * subSize;
// res.m_storage = StorageType_(begin, subSize, false);
// return res;
// #else
// LIBRAPID_ERROR("CUDA support not enabled");
// #endif // LIBRAPID_HAS_CUDA
// } else if constexpr (typetraits::IsFixedStorage<StorageType_>::value) {
// return GeneralArrayView(*this)[index];
// } else {
// ArrayContainer res;
// res.m_shape = m_shape.subshape(1, ndim());
// auto subSize = res.shape().size();
// Scalar *begin = m_storage.begin() + index * subSize;
// Scalar *end = begin + subSize;
// res.m_storage = StorageType_(begin, end, false);
// return res;
// }
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator[](int64_t index) {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::out_of_range,
index >= 0 && index < static_cast<int64_t>(m_shape[0]),
"Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}",
index,
m_shape[0]);
return createGeneralArrayView(*this)[index];
// if constexpr (typetraits::IsOpenCLStorage<StorageType_>::value) {
// #if defined(LIBRAPID_HAS_OPENCL)
// ArrayContainer res;
// res.m_shape = m_shape.subshape(1, ndim());
// auto subSize = res.shape().size();
// int64_t storageSize = sizeof(typename StorageType_::Scalar);
// cl_buffer_region region {index * subSize * storageSize, subSize *
// storageSize}; res.m_storage =
// StorageType_(m_storage.data().createSubBuffer(
// StorageType_::bufferFlags,
// CL_BUFFER_CREATE_TYPE_REGION, ®ion), subSize,
// false); return res; #else LIBRAPID_ERROR("OpenCL support
// not enabled"); #endif // LIBRAPID_HAS_OPENCL } else if constexpr
//(typetraits::IsCudaStorage<StorageType_>::value) { #if defined(LIBRAPID_HAS_CUDA)
// ArrayContainer res;
// res.m_shape = m_shape.subshape(1, ndim());
// auto subSize = res.shape().size();
// Scalar *begin = m_storage.begin().get() + index * subSize;
// res.m_storage = StorageType_(begin, subSize, false);
// return res;
// #else
// LIBRAPID_ERROR("CUDA support not enabled");
// #endif // LIBRAPID_HAS_CUDA
// } else if constexpr (typetraits::IsFixedStorage<StorageType_>::value) {
// return GeneralArrayView(*this)[index];
// } else {
// ArrayContainer res;
// res.m_shape = m_shape.subshape(1, ndim());
// auto subSize = res.shape().size();
// Scalar *begin = m_storage.begin() + index * subSize;
// Scalar *end = begin + subSize;
// res.m_storage = StorageType_(begin, end, false);
// return res;
// }
}
template<typename ShapeType_, typename StorageType_>
template<typename... Indices>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator()(Indices... indices) const
-> DirectSubscriptType {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::invalid_argument,
m_shape.ndim() == sizeof...(Indices),
"ArrayContainer::operator() called with {} indices, but array has {} dimensions",
sizeof...(Indices),
m_shape.ndim());
int dim = 0;
int64_t index = 0;
for (int64_t i : {indices...}) {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::out_of_range,
i >= 0 && i < static_cast<int64_t>(m_shape[dim]),
"Index {} out of bounds in ArrayContainer::operator() with dimension={}",
i,
m_shape[dim]);
index = index * m_shape[dim++] + i;
}
return m_storage[index];
}
template<typename ShapeType_, typename StorageType_>
template<typename... Indices>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator()(Indices... indices)
-> DirectRefSubscriptType {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::invalid_argument,
m_shape.ndim() == sizeof...(Indices),
"ArrayContainer::operator() called with {} indices, but array has {} dimensions",
sizeof...(Indices),
m_shape.ndim());
int64_t index = 0;
int64_t count = 0;
for (int64_t i : {indices...}) {
LIBRAPID_ASSERT_WITH_EXCEPTION(
std::out_of_range,
i >= 0 && i < static_cast<int64_t>(m_shape[count]),
"Index {} out of bounds in ArrayContainer::operator() with dimension={}",
i,
m_shape[count]);
index = index * m_shape[count++] + i;
}
return m_storage[index];
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::get() const
-> Scalar {
LIBRAPID_ASSERT_WITH_EXCEPTION(std::invalid_argument,
m_shape.ndim() == 0,
"Can only cast a scalar ArrayView to a salar object. Array has {}", m_shape);
return scalar(0);
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::ndim() const noexcept
-> typename ShapeType_::SizeType {
return m_shape.ndim();
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::size() const noexcept
-> size_t {
return m_size;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::shape() const noexcept
-> const ShapeType & {
return m_shape;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::storage() const noexcept -> const StorageType & {
return m_storage;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::storage() noexcept
-> StorageType & {
return m_storage;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::packet(size_t index) const -> Packet {
auto ptr = LIBRAPID_ASSUME_ALIGNED(m_storage.begin());
#if defined(LIBRAPID_NATIVE_ARCH)
return xsimd::load_aligned(ptr + index);
#else
return xsimd::load_unaligned(ptr + index);
#endif
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::scalar(size_t index) const -> Scalar {
return m_storage[index];
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE void
ArrayContainer<ShapeType_, StorageType_>::writePacket(size_t index, const Packet &value) {
auto ptr = LIBRAPID_ASSUME_ALIGNED(m_storage.begin());
#if defined(LIBRAPID_NATIVE_ARCH)
value.store_aligned(ptr + index);
#else
value.store_unaligned(ptr + index);
#endif
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE void
ArrayContainer<ShapeType_, StorageType_>::write(size_t index, const Scalar &value) {
m_storage[index] = value;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator+=(const T &value) -> ArrayContainer & {
*this = *this + value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator-=(const T &value) -> ArrayContainer & {
*this = *this - value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator*=(const T &value) -> ArrayContainer & {
*this = *this * value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator/=(const T &value) -> ArrayContainer & {
*this = *this / value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator%=(const T &value) -> ArrayContainer & {
*this = *this % value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator&=(const T &value) -> ArrayContainer & {
*this = *this & value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator|=(const T &value) -> ArrayContainer & {
*this = *this | value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator^=(const T &value) -> ArrayContainer & {
*this = *this ^ value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator<<=(const T &value) -> ArrayContainer & {
*this = *this << value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
LIBRAPID_ALWAYS_INLINE auto
ArrayContainer<ShapeType_, StorageType_>::operator>>=(const T &value) -> ArrayContainer & {
*this = *this >> value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::begin() const noexcept
-> auto {
return detail::ArrayIterator(createGeneralArrayView(*this), 0);
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::end() const noexcept
-> auto {
return detail::ArrayIterator(createGeneralArrayView(*this), m_shape[0]);
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::begin() -> auto {
return detail::ArrayIterator(createGeneralArrayView(*this), 0);
}
template<typename ShapeType_, typename StorageType_>
LIBRAPID_ALWAYS_INLINE auto ArrayContainer<ShapeType_, StorageType_>::end() -> auto {
return detail::ArrayIterator(createGeneralArrayView(*this), m_shape[0]);
}
template<typename ShapeType_, typename StorageType_>
template<typename T, typename Char, size_t N, typename Ctx>
LIBRAPID_ALWAYS_INLINE void ArrayContainer<ShapeType_, StorageType_>::str(
const fmt::formatter<T, Char> &format, char bracket, char separator,
const char (&formatString)[N], Ctx &ctx) const {
createGeneralArrayView(*this).str(format, bracket, separator, formatString, ctx);
}
} // namespace array
namespace detail {
template<typename T>
struct IsArrayType {
static constexpr bool val = false;
};
template<typename T, typename V>
struct IsArrayType<ArrayRef<T, V>> {
static constexpr bool val = true;
};
template<typename... T>
struct IsArrayType<FunctionRef<T...>> {
static constexpr bool val = true;
};
template<typename T, typename S>
struct IsArrayType<array::GeneralArrayView<T, S>> {
static constexpr bool val = true;
};
template<typename First, typename... Types>
struct ContainsArrayType {
static constexpr auto evaluator() {
if constexpr (sizeof...(Types) == 0)
return IsArrayType<First>::val;
else
return IsArrayType<First>::val || ContainsArrayType<Types...>::val;
};
static constexpr bool val = evaluator();
};
}; // namespace detail
} // namespace librapid
// Support FMT printing
ARRAY_TYPE_FMT_IML(typename ShapeType_ COMMA typename StorageType_,
librapid::array::ArrayContainer<ShapeType_ COMMA StorageType_>)
LIBRAPID_SIMPLE_IO_NORANGE(typename ShapeType_ COMMA typename StorageType_,
librapid::array::ArrayContainer<ShapeType_ COMMA StorageType_>)
#endif // LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP