diff --git a/librapid/include/librapid/array/array.hpp b/librapid/include/librapid/array/array.hpp index 8784f8e0..f3f7e682 100644 --- a/librapid/include/librapid/array/array.hpp +++ b/librapid/include/librapid/array/array.hpp @@ -1,7 +1,7 @@ #ifndef LIBRAPID_ARRAY #define LIBRAPID_ARRAY -#include "sizetype.hpp" +#include "shape.hpp" #include "strideTools.hpp" #include "storage.hpp" diff --git a/librapid/include/librapid/array/arrayContainer.hpp b/librapid/include/librapid/array/arrayContainer.hpp index 8213061b..922a2631 100644 --- a/librapid/include/librapid/array/arrayContainer.hpp +++ b/librapid/include/librapid/array/arrayContainer.hpp @@ -2,109 +2,108 @@ #define LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP namespace librapid { - namespace detail { - template - struct SubscriptType { - using Scalar = T; - using Direct = const Scalar &; - using Ref = Scalar &; - }; - - template - struct SubscriptType> { - using Scalar = T; - using Direct = const Scalar &; - using Ref = Scalar &; - }; - - template - struct SubscriptType> { - using Scalar = T; - using Direct = const Scalar &; - using Ref = Scalar &; - }; + namespace detail { + template + struct SubscriptType { + using Scalar = T; + using Direct = const Scalar &; + using Ref = Scalar &; + }; + + template + struct SubscriptType> { + using Scalar = T; + using Direct = const Scalar &; + using Ref = Scalar &; + }; + + template + struct SubscriptType> { + using Scalar = T; + using Direct = const Scalar &; + using Ref = Scalar &; + }; #if defined(LIBRAPID_HAS_OPENCL) - template - struct SubscriptType> { - using Scalar = T; - using Direct = const OpenCLRef; - using Ref = OpenCLRef; - }; + template + struct SubscriptType> { + using Scalar = T; + using Direct = const OpenCLRef; + using Ref = OpenCLRef; + }; #endif // LIBRAPID_HAS_OPENCL #if defined(LIBRAPID_HAS_CUDA) - template - struct SubscriptType> { - using Scalar = T; - using Direct = const detail::CudaRef; - using Ref = detail::CudaRef; - }; + template + struct SubscriptType> { + using Scalar = T; + using Direct = const detail::CudaRef; + using Ref = detail::CudaRef; + }; #endif // LIBRAPID_HAS_CUDA - } // namespace detail - - namespace typetraits { - template - struct TypeInfo> { - static constexpr detail::LibRapidType type = detail::LibRapidType::ArrayContainer; - using Scalar = typename TypeInfo::Scalar; - using Packet = std::false_type; - using Backend = typename TypeInfo::Backend; - static constexpr int64_t packetWidth = 1; - static constexpr bool supportsArithmetic = TypeInfo::supportsArithmetic; - static constexpr bool supportsLogical = TypeInfo::supportsLogical; - static constexpr bool supportsBinary = TypeInfo::supportsBinary; - static constexpr bool allowVectorisation = TypeInfo::packetWidth > 1; + } // namespace detail + + namespace typetraits { + template + struct TypeInfo> { + static constexpr detail::LibRapidType type = detail::LibRapidType::ArrayContainer; + using Scalar = typename TypeInfo::Scalar; + using Packet = std::false_type; + using Backend = typename TypeInfo::Backend; + static constexpr int64_t packetWidth = 1; + static constexpr bool supportsArithmetic = TypeInfo::supportsArithmetic; + static constexpr bool supportsLogical = TypeInfo::supportsLogical; + static constexpr bool supportsBinary = TypeInfo::supportsBinary; + static constexpr bool allowVectorisation = TypeInfo::packetWidth > 1; #if defined(LIBRAPID_HAS_CUDA) - static constexpr cudaDataType_t CudaType = TypeInfo::CudaType; - static constexpr int64_t cudaPacketWidth = 1; + static constexpr cudaDataType_t CudaType = TypeInfo::CudaType; + static constexpr int64_t cudaPacketWidth = 1; #endif // LIBRAPID_HAS_CUDA - static constexpr bool canAlign = false; - static constexpr int64_t canMemcpy = false; - }; - - /// Evaluates as true if the input type is an ArrayContainer instance - /// \tparam T Input type - template - struct IsArrayContainer : std::false_type {}; - - template - struct IsArrayContainer, StorageScalar>> - : std::true_type {}; - - LIBRAPID_DEFINE_AS_TYPE( - typename SizeType COMMA size_t dims COMMA typename StorageScalar, - array::ArrayContainer COMMA StorageScalar>); - } // namespace typetraits - - namespace array { - template - class ArrayContainer { - public: - using StorageType = StorageType_; - using ShapeType = ShapeType_; - using StrideType = Stride; - using SizeType = typename ShapeType::SizeType; - using Scalar = typename StorageType::Scalar; - using Packet = typename typetraits::TypeInfo::Packet; - using Backend = typename typetraits::TypeInfo::Backend; - using Iterator = detail::ArrayIterator>; - - using DirectSubscriptType = typename detail::SubscriptType::Direct; - using DirectRefSubscriptType = typename detail::SubscriptType::Ref; - - /// Default constructor - ArrayContainer(); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::initializer_list &data); - - template - explicit LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::vector &data); - - // clang-format off + static constexpr bool canAlign = false; + static constexpr int64_t canMemcpy = false; + }; + + /// Evaluates as true if the input type is an ArrayContainer instance + /// \tparam T Input type + template + struct IsArrayContainer : std::false_type {}; + + template + struct IsArrayContainer, StorageScalar>> + : std::true_type {}; + + LIBRAPID_DEFINE_AS_TYPE(size_t dims COMMA typename StorageScalar, + array::ArrayContainer COMMA StorageScalar>); + } // namespace typetraits + + namespace array { + template + class ArrayContainer { + public: + using StorageType = StorageType_; + using ShapeType = ShapeType_; + using StrideType = Stride<32>; + using SizeType = typename ShapeType::SizeType; + using Scalar = typename StorageType::Scalar; + using Packet = typename typetraits::TypeInfo::Packet; + using Backend = typename typetraits::TypeInfo::Backend; + using Iterator = detail::ArrayIterator>; + + using DirectSubscriptType = typename detail::SubscriptType::Direct; + using DirectRefSubscriptType = typename detail::SubscriptType::Ref; + + /// Default constructor + ArrayContainer(); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::initializer_list &data); + + template + explicit LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::vector &data); + + // clang-format off #define SINIT(SUB_TYPE) std::initializer_list #define SVEC(SUB_TYPE) std::vector @@ -126,730 +125,730 @@ namespace librapid { #undef SINIT #undef SVEC - // clang-format on - - /// Constructs an array container from a shape - /// \param shape The shape of the array container - LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const ShapeType &shape); - - /// Create an array container from a shape and a scalar value. The scalar value - /// represents the value the memory is initialized with. \param shape The shape of the - /// array container \param value The value to initialize the memory with - LIBRAPID_ALWAYS_INLINE ArrayContainer(const ShapeType &shape, const Scalar &value); - - /// Allows for a fixed-size array to be constructed with a fill value - /// \param value The value to fill the array with - LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const Scalar &value); - - /// Construct an array container from a shape, which is moved, not copied. - /// \param shape The shape of the array container - LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(ShapeType &&shape); - - /// \brief Reference an existing array container - /// - /// This constructor does not copy the data, but instead references the data of the - /// input array container. This means that the input array container must outlive the - /// constructed array container. Please use ``ArrayContainer::copy()`` if you want to - /// copy the data. - /// \param other The array container to reference - LIBRAPID_ALWAYS_INLINE ArrayContainer(const ArrayContainer &other) = default; - - /// Construct an array container from a temporary array container. - /// \param other The array container to move. - LIBRAPID_ALWAYS_INLINE ArrayContainer(ArrayContainer &&other) noexcept = default; - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer(const Transpose &trans); - - template - LIBRAPID_ALWAYS_INLINE - ArrayContainer(const linalg::ArrayMultiply &multiply); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer & - assign(const detail::Function &function); - - /// Construct an array container from a function object. This will assign the result of - /// the function to the array container, evaluating it accordingly. - /// \tparam desc The assignment descriptor - /// \tparam Functor_ The function type - /// \tparam Args The argument types of the function - /// \param function The function to assign - template - LIBRAPID_ALWAYS_INLINE ArrayContainer( - const detail::Function &function) LIBRAPID_RELEASE_NOEXCEPT; - - /// \brief Reference an existing array container - /// - /// This assignment operator does not copy the data, but instead references the data of - /// the input array container. This means that the input array container must outlive - /// the constructed array container. Please use ``ArrayContainer::copy()`` if you want - /// to copy the data. - /// - /// \param other The array container to reference - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const ArrayContainer &other) = default; - - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const Scalar &value); - - /// Assign a temporary array container to this array container. - /// \param other The array container to move. - /// \return A reference to this array container. - LIBRAPID_ALWAYS_INLINE ArrayContainer & - operator=(ArrayContainer &&other) noexcept = default; - - /// Assign a function object to this array container. This will assign the result of - /// the function to the array container, evaluating it accordingly. - /// \tparam Functor_ The function type - /// \tparam Args The argument types of the function - /// \param function The function to assign - /// \return A reference to this array container. - template - LIBRAPID_ALWAYS_INLINE ArrayContainer & - operator=(const detail::Function &function); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer & - operator=(const Transpose &transpose); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer & - operator=(const linalg::ArrayMultiply &multiply); - - /// Allow ArrayContainer objects to be initialized with a comma separated list of - /// values. This makes use of the CommaInitializer class - /// \tparam T The type of the values - /// \param value The value to set in the Array object - /// \return The comma initializer object - template - detail::CommaInitializer operator<<(const T &value); - - // template - // LIBRAPID_NODISCARD auto cast() const; - - LIBRAPID_NODISCARD ArrayContainer copy() const; - - /// Access a sub-array of this ArrayContainer instance. The sub-array will reference - /// the same memory as this ArrayContainer instance. - /// \param index The index of the sub-array - /// \return A reference to the sub-array (ArrayView) - /// \see ArrayView - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index) const; - - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index); - - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectSubscriptType - operator()(Indices... indices) const; - - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectRefSubscriptType - operator()(Indices... indices); - - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar get() const; - - /// Return the number of dimensions of the ArrayContainer object - /// \return Number of dimensions of the ArrayContainer - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE typename ShapeType::SizeType - ndim() const noexcept; - - /// Return the shape of the array container. This is an immutable reference. - /// \return The shape of the array container. - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const ShapeType &shape() const noexcept; - - /// Return the StorageType object of the ArrayContainer - /// \return The StorageType object of the ArrayContainer - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const StorageType &storage() const noexcept; - - /// Return the StorageType object of the ArrayContainer - /// \return The StorageType object of the ArrayContainer - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE StorageType &storage() noexcept; - - /// Return a Packet object from the array's storage at a specific index. - /// \param index The index to get the packet from - /// \return A Packet object from the array's storage at a specific index - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Packet packet(size_t index) const; - - /// Return a Scalar from the array's storage at a specific index. - /// \param index The index to get the scalar from - /// \return A Scalar from the array's storage at a specific index - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar scalar(size_t index) const; - - /// Write a Packet object to the array's storage at a specific index - /// \param index The index to write the packet to - /// \param value The value to write to the array's storage - LIBRAPID_ALWAYS_INLINE void writePacket(size_t index, const Packet &value); - - /// Write a Scalar to the array's storage at a specific index - /// \param index The index to write the scalar to - /// \param value The value to write to the array's storage - LIBRAPID_ALWAYS_INLINE void write(size_t index, const Scalar &value); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator+=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator-=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator*=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator/=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator%=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator&=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator|=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator^=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator<<=(const T &other); - - template - LIBRAPID_ALWAYS_INLINE ArrayContainer &operator>>=(const T &other); - - /// \brief Return an iterator to the beginning of the array container - /// \return Iterator - LIBRAPID_INLINE Iterator begin() const noexcept; - - /// \brief Return an iterator to the end of the array container - /// \return Iterator - LIBRAPID_INLINE Iterator end() const noexcept; - - /// \brief Return an iterator to the beginning of the array container - /// \return Iterator - LIBRAPID_INLINE Iterator begin(); - - /// \brief Return an iterator to the end of the array container - /// \return Iterator - LIBRAPID_INLINE Iterator end(); - - template - void str(const fmt::formatter &format, char bracket, char separator, - Ctx &ctx) const; - - private: - ShapeType m_shape; // The shape type of the array - StorageType m_storage; // The storage container of the array - }; - - template - ArrayContainer::ArrayContainer() : - m_shape(StorageType_::template defaultShape()) {} - - template - template - ArrayContainer::ArrayContainer( - const std::initializer_list &data) : - m_shape({data.size()}), - m_storage(StorageType::fromData(data)) {} - - template - template - ArrayContainer::ArrayContainer(const std::vector &data) : - m_shape({data.size()}), m_storage(StorageType::fromData(data)) {} - - template - ArrayContainer::ArrayContainer(const ShapeType &shape) : - m_shape(shape), m_storage(shape.size()) { - static_assert(!typetraits::IsFixedStorage::value, - "For a compile-time-defined shape, " - "the storage type must be " - "a FixedStorage object"); - } - - template - ArrayContainer::ArrayContainer(const ShapeType &shape, - const Scalar &value) : - m_shape(shape), - m_storage(shape.size(), value) { - static_assert(typetraits::IsStorage::value || - typetraits::IsOpenCLStorage::value || - typetraits::IsCudaStorage::value, - "For a runtime-defined shape, " - "the storage type must be " - "either a Storage or a " - "CudaStorage object"); - static_assert(!typetraits::IsFixedStorage::value, - "For a compile-time-defined shape, " - "the storage type must be " - "a FixedStorage object"); - } - - template - ArrayContainer::ArrayContainer(const Scalar &value) : - m_shape(detail::shapeFromFixedStorage(m_storage)), m_storage(value) { - static_assert(typetraits::IsFixedStorage::value, - "For a compile-time-defined shape, " - "the storage type must be " - "a FixedStorage object"); - } - - template - ArrayContainer::ArrayContainer(ShapeType_ &&shape) : - m_shape(std::forward(shape)), m_storage(m_shape.size()) {} - - template - template - ArrayContainer::ArrayContainer( - const array::Transpose &trans) { - *this = trans; - } - - template - template - ArrayContainer::ArrayContainer( - const linalg::ArrayMultiply &multiply) { - *this = multiply; - } - - template - template - auto ArrayContainer::assign( - const detail::Function &function) -> ArrayContainer & { - using FunctionType = detail::Function; - m_storage.resize(function.shape().size(), 0); - if constexpr (std::is_same_v || - std::is_same_v) { - detail::assign(*this, function); - } else { + // clang-format on + + /// Constructs an array container from a shape + /// \param shape The shape of the array container + LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const ShapeType &shape); + + /// Create an array container from a shape and a scalar value. The scalar value + /// represents the value the memory is initialized with. \param shape The shape of the + /// array container \param value The value to initialize the memory with + LIBRAPID_ALWAYS_INLINE ArrayContainer(const ShapeType &shape, const Scalar &value); + + /// Allows for a fixed-size array to be constructed with a fill value + /// \param value The value to fill the array with + LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const Scalar &value); + + /// Construct an array container from a shape, which is moved, not copied. + /// \param shape The shape of the array container + LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(ShapeType &&shape); + + /// \brief Reference an existing array container + /// + /// This constructor does not copy the data, but instead references the data of the + /// input array container. This means that the input array container must outlive the + /// constructed array container. Please use ``ArrayContainer::copy()`` if you want to + /// copy the data. + /// \param other The array container to reference + LIBRAPID_ALWAYS_INLINE ArrayContainer(const ArrayContainer &other) = default; + + /// Construct an array container from a temporary array container. + /// \param other The array container to move. + LIBRAPID_ALWAYS_INLINE ArrayContainer(ArrayContainer &&other) noexcept = default; + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer(const Transpose &trans); + + template + LIBRAPID_ALWAYS_INLINE + ArrayContainer(const linalg::ArrayMultiply &multiply); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer & + assign(const detail::Function &function); + + /// Construct an array container from a function object. This will assign the result of + /// the function to the array container, evaluating it accordingly. + /// \tparam desc The assignment descriptor + /// \tparam Functor_ The function type + /// \tparam Args The argument types of the function + /// \param function The function to assign + template + LIBRAPID_ALWAYS_INLINE ArrayContainer( + const detail::Function &function) LIBRAPID_RELEASE_NOEXCEPT; + + /// \brief Reference an existing array container + /// + /// This assignment operator does not copy the data, but instead references the data of + /// the input array container. This means that the input array container must outlive + /// the constructed array container. Please use ``ArrayContainer::copy()`` if you want + /// to copy the data. + /// + /// \param other The array container to reference + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const ArrayContainer &other) = default; + + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const Scalar &value); + + /// Assign a temporary array container to this array container. + /// \param other The array container to move. + /// \return A reference to this array container. + LIBRAPID_ALWAYS_INLINE ArrayContainer & + operator=(ArrayContainer &&other) noexcept = default; + + /// Assign a function object to this array container. This will assign the result of + /// the function to the array container, evaluating it accordingly. + /// \tparam Functor_ The function type + /// \tparam Args The argument types of the function + /// \param function The function to assign + /// \return A reference to this array container. + template + LIBRAPID_ALWAYS_INLINE ArrayContainer & + operator=(const detail::Function &function); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer & + operator=(const Transpose &transpose); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer & + operator=(const linalg::ArrayMultiply &multiply); + + /// Allow ArrayContainer objects to be initialized with a comma separated list of + /// values. This makes use of the CommaInitializer class + /// \tparam T The type of the values + /// \param value The value to set in the Array object + /// \return The comma initializer object + template + detail::CommaInitializer operator<<(const T &value); + + // template + // LIBRAPID_NODISCARD auto cast() const; + + LIBRAPID_NODISCARD ArrayContainer copy() const; + + /// Access a sub-array of this ArrayContainer instance. The sub-array will reference + /// the same memory as this ArrayContainer instance. + /// \param index The index of the sub-array + /// \return A reference to the sub-array (ArrayView) + /// \see ArrayView + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index) const; + + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index); + + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectSubscriptType + operator()(Indices... indices) const; + + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectRefSubscriptType + operator()(Indices... indices); + + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar get() const; + + /// Return the number of dimensions of the ArrayContainer object + /// \return Number of dimensions of the ArrayContainer + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE typename ShapeType::SizeType + ndim() const noexcept; + + /// Return the shape of the array container. This is an immutable reference. + /// \return The shape of the array container. + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const ShapeType &shape() const noexcept; + + /// Return the StorageType object of the ArrayContainer + /// \return The StorageType object of the ArrayContainer + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const StorageType &storage() const noexcept; + + /// Return the StorageType object of the ArrayContainer + /// \return The StorageType object of the ArrayContainer + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE StorageType &storage() noexcept; + + /// Return a Packet object from the array's storage at a specific index. + /// \param index The index to get the packet from + /// \return A Packet object from the array's storage at a specific index + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Packet packet(size_t index) const; + + /// Return a Scalar from the array's storage at a specific index. + /// \param index The index to get the scalar from + /// \return A Scalar from the array's storage at a specific index + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar scalar(size_t index) const; + + /// Write a Packet object to the array's storage at a specific index + /// \param index The index to write the packet to + /// \param value The value to write to the array's storage + LIBRAPID_ALWAYS_INLINE void writePacket(size_t index, const Packet &value); + + /// Write a Scalar to the array's storage at a specific index + /// \param index The index to write the scalar to + /// \param value The value to write to the array's storage + LIBRAPID_ALWAYS_INLINE void write(size_t index, const Scalar &value); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator+=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator-=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator*=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator/=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator%=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator&=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator|=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator^=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator<<=(const T &other); + + template + LIBRAPID_ALWAYS_INLINE ArrayContainer &operator>>=(const T &other); + + /// \brief Return an iterator to the beginning of the array container + /// \return Iterator + LIBRAPID_INLINE Iterator begin() const noexcept; + + /// \brief Return an iterator to the end of the array container + /// \return Iterator + LIBRAPID_INLINE Iterator end() const noexcept; + + /// \brief Return an iterator to the beginning of the array container + /// \return Iterator + LIBRAPID_INLINE Iterator begin(); + + /// \brief Return an iterator to the end of the array container + /// \return Iterator + LIBRAPID_INLINE Iterator end(); + + template + void str(const fmt::formatter &format, char bracket, char separator, + Ctx &ctx) const; + + private: + ShapeType m_shape; // The shape type of the array + StorageType m_storage; // The storage container of the array + }; + + template + ArrayContainer::ArrayContainer() : + m_shape(StorageType_::template defaultShape()) {} + + template + template + ArrayContainer::ArrayContainer( + const std::initializer_list &data) : + m_shape({data.size()}), + m_storage(StorageType::fromData(data)) {} + + template + template + ArrayContainer::ArrayContainer(const std::vector &data) : + m_shape({data.size()}), m_storage(StorageType::fromData(data)) {} + + template + ArrayContainer::ArrayContainer(const ShapeType &shape) : + m_shape(shape), m_storage(shape.size()) { + static_assert(!typetraits::IsFixedStorage::value, + "For a compile-time-defined shape, " + "the storage type must be " + "a FixedStorage object"); + } + + template + ArrayContainer::ArrayContainer(const ShapeType &shape, + const Scalar &value) : + m_shape(shape), + m_storage(shape.size(), value) { + static_assert(typetraits::IsStorage::value || + typetraits::IsOpenCLStorage::value || + typetraits::IsCudaStorage::value, + "For a runtime-defined shape, " + "the storage type must be " + "either a Storage or a " + "CudaStorage object"); + static_assert(!typetraits::IsFixedStorage::value, + "For a compile-time-defined shape, " + "the storage type must be " + "a FixedStorage object"); + } + + template + ArrayContainer::ArrayContainer(const Scalar &value) : + m_shape(detail::shapeFromFixedStorage(m_storage)), m_storage(value) { + static_assert(typetraits::IsFixedStorage::value, + "For a compile-time-defined shape, " + "the storage type must be " + "a FixedStorage object"); + } + + template + ArrayContainer::ArrayContainer(ShapeType_ &&shape) : + m_shape(std::forward(shape)), m_storage(m_shape.size()) {} + + template + template + ArrayContainer::ArrayContainer( + const array::Transpose &trans) { + *this = trans; + } + + template + template + ArrayContainer::ArrayContainer( + const linalg::ArrayMultiply &multiply) { + *this = multiply; + } + + template + template + auto ArrayContainer::assign( + const detail::Function &function) -> ArrayContainer & { + using FunctionType = detail::Function; + m_storage.resize(function.shape().size(), 0); + if constexpr (std::is_same_v || + std::is_same_v) { + 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 + 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 - template - ArrayContainer::ArrayContainer( - const detail::Function &function) LIBRAPID_RELEASE_NOEXCEPT - : m_shape(function.shape()), - m_storage(m_shape.size()) { - assign(function); - } - - template - template - auto ArrayContainer::operator=( - const detail::Function &function) -> ArrayContainer & { - return assign(function); - } - - template - template - auto ArrayContainer::operator=( - const Transpose &transpose) -> ArrayContainer & { - m_shape = transpose.shape(); - m_storage.resize(m_shape.size(), 0); - transpose.applyTo(*this); - return *this; - } - - template - template - auto ArrayContainer::operator=( - const linalg::ArrayMultiply &arrayMultiply) -> ArrayContainer & { - m_shape = arrayMultiply.shape(); - m_storage.resize(m_shape.size(), 0); - arrayMultiply.applyTo(*this); - return *this; - } - - template - auto ArrayContainer::operator=(const Scalar &value) - -> ArrayContainer & { - LIBRAPID_ASSERT(m_shape.ndim() == 0, "Cannot assign a scalar to an array"); - m_storage[0] = value; - return *this; - } - - template - template - auto ArrayContainer::operator<<(const T &value) - -> detail::CommaInitializer { - return detail::CommaInitializer(*this, static_cast(value)); - } - - template - auto ArrayContainer::copy() const -> ArrayContainer { - ArrayContainer res(m_shape); - res.m_storage = m_storage.copy(); - return res; - } - - template - auto ArrayContainer::operator[](int64_t index) const { - LIBRAPID_ASSERT( - index >= 0 && index < static_cast(m_shape[0]), - "Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}", - index, - m_shape[0]); - - if constexpr (typetraits::IsOpenCLStorage::value) { + detail::assign(*this, function); + } + return *this; + } + + template + template + ArrayContainer::ArrayContainer( + const detail::Function &function) LIBRAPID_RELEASE_NOEXCEPT + : m_shape(function.shape()), + m_storage(m_shape.size()) { + assign(function); + } + + template + template + auto ArrayContainer::operator=( + const detail::Function &function) -> ArrayContainer & { + return assign(function); + } + + template + template + auto ArrayContainer::operator=( + const Transpose &transpose) -> ArrayContainer & { + m_shape = transpose.shape(); + m_storage.resize(m_shape.size(), 0); + transpose.applyTo(*this); + return *this; + } + + template + template + auto ArrayContainer::operator=( + const linalg::ArrayMultiply &arrayMultiply) -> ArrayContainer & { + m_shape = arrayMultiply.shape(); + m_storage.resize(m_shape.size(), 0); + arrayMultiply.applyTo(*this); + return *this; + } + + template + auto ArrayContainer::operator=(const Scalar &value) + -> ArrayContainer & { + LIBRAPID_ASSERT(m_shape.ndim() == 0, "Cannot assign a scalar to an array"); + m_storage[0] = value; + return *this; + } + + template + template + auto ArrayContainer::operator<<(const T &value) + -> detail::CommaInitializer { + return detail::CommaInitializer(*this, static_cast(value)); + } + + template + auto ArrayContainer::copy() const -> ArrayContainer { + ArrayContainer res(m_shape); + res.m_storage = m_storage.copy(); + return res; + } + + template + auto ArrayContainer::operator[](int64_t index) const { + LIBRAPID_ASSERT( + index >= 0 && index < static_cast(m_shape[0]), + "Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}", + index, + m_shape[0]); + + if constexpr (typetraits::IsOpenCLStorage::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; + 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"); + LIBRAPID_ERROR("OpenCL support not enabled"); #endif // LIBRAPID_HAS_OPENCL - } else if constexpr (typetraits::IsCudaStorage::value) { + } else if constexpr (typetraits::IsCudaStorage::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; + 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"); + LIBRAPID_ERROR("CUDA support not enabled"); #endif // LIBRAPID_HAS_CUDA - } else if constexpr (typetraits::IsFixedStorage::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 - auto ArrayContainer::operator[](int64_t index) { - LIBRAPID_ASSERT( - index >= 0 && index < static_cast(m_shape[0]), - "Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}", - index, - m_shape[0]); - - if constexpr (typetraits::IsOpenCLStorage::value) { + } else if constexpr (typetraits::IsFixedStorage::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 + auto ArrayContainer::operator[](int64_t index) { + LIBRAPID_ASSERT( + index >= 0 && index < static_cast(m_shape[0]), + "Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}", + index, + m_shape[0]); + + if constexpr (typetraits::IsOpenCLStorage::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.set( - StorageType_(m_storage.data().createSubBuffer( - StorageType_::bufferFlags, CL_BUFFER_CREATE_TYPE_REGION, ®ion), - subSize, - false)); - return res; + 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.set( + StorageType_(m_storage.data().createSubBuffer( + StorageType_::bufferFlags, CL_BUFFER_CREATE_TYPE_REGION, ®ion), + subSize, + false)); + return res; #else - LIBRAPID_ERROR("OpenCL support not enabled"); + LIBRAPID_ERROR("OpenCL support not enabled"); #endif // LIBRAPID_HAS_OPENCL - } else if constexpr (typetraits::IsCudaStorage::value) { + } else if constexpr (typetraits::IsCudaStorage::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.set(StorageType_(begin, subSize, false)); - return res; + 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.set(StorageType_(begin, subSize, false)); + return res; #else - LIBRAPID_ERROR("CUDA support not enabled"); + LIBRAPID_ERROR("CUDA support not enabled"); #endif // LIBRAPID_HAS_CUDA - } else if constexpr (typetraits::IsFixedStorage::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.set(StorageType_(begin, end, false)); - return res; - } - } - - template - template - auto ArrayContainer::operator()(Indices... indices) const - -> DirectSubscriptType { - LIBRAPID_ASSERT( - m_shape.ndim() == sizeof...(Indices), - "ArrayContainer::operator() called with {} indices, but array has {} dimensions", - sizeof...(Indices), - m_shape.ndim()); - - int64_t index = 0; - for (int64_t i : {indices...}) { - LIBRAPID_ASSERT( - i >= 0 && i < static_cast(m_shape[index]), - "Index {} out of bounds in ArrayContainer::operator() with dimension={}", - i, - m_shape[index]); - index = index * m_shape[index] + i; - } - return m_storage[index]; - } - - template - template - auto ArrayContainer::operator()(Indices... indices) - -> DirectRefSubscriptType { - LIBRAPID_ASSERT( - 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( - i >= 0 && i < static_cast(m_shape[count]), - "Index {} out of bounds in ArrayContainer::operator() with dimension={}", - i, - m_shape[index]); - index = index * m_shape[count++] + i; - } - return m_storage[index]; - } - - template - auto ArrayContainer::get() const -> Scalar { - LIBRAPID_ASSERT(m_shape.ndim() == 0, - "Can only cast a scalar ArrayView to a salar object"); - return scalar(0); - } - - template - auto ArrayContainer::ndim() const noexcept -> - typename ShapeType_::SizeType { - return m_shape.ndim(); - } - - template - auto ArrayContainer::shape() const noexcept -> const ShapeType & { - return m_shape; - } - - template - auto ArrayContainer::storage() const noexcept - -> const StorageType & { - return m_storage; - } - - template - auto ArrayContainer::storage() noexcept -> StorageType & { - return m_storage; - } - - template - auto ArrayContainer::packet(size_t index) const -> Packet { + } else if constexpr (typetraits::IsFixedStorage::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.set(StorageType_(begin, end, false)); + return res; + } + } + + template + template + auto ArrayContainer::operator()(Indices... indices) const + -> DirectSubscriptType { + LIBRAPID_ASSERT( + m_shape.ndim() == sizeof...(Indices), + "ArrayContainer::operator() called with {} indices, but array has {} dimensions", + sizeof...(Indices), + m_shape.ndim()); + + int64_t index = 0; + for (int64_t i : {indices...}) { + LIBRAPID_ASSERT( + i >= 0 && i < static_cast(m_shape[index]), + "Index {} out of bounds in ArrayContainer::operator() with dimension={}", + i, + m_shape[index]); + index = index * m_shape[index] + i; + } + return m_storage[index]; + } + + template + template + auto ArrayContainer::operator()(Indices... indices) + -> DirectRefSubscriptType { + LIBRAPID_ASSERT( + 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( + i >= 0 && i < static_cast(m_shape[count]), + "Index {} out of bounds in ArrayContainer::operator() with dimension={}", + i, + m_shape[index]); + index = index * m_shape[count++] + i; + } + return m_storage[index]; + } + + template + auto ArrayContainer::get() const -> Scalar { + LIBRAPID_ASSERT(m_shape.ndim() == 0, + "Can only cast a scalar ArrayView to a salar object"); + return scalar(0); + } + + template + auto ArrayContainer::ndim() const noexcept -> + typename ShapeType_::SizeType { + return m_shape.ndim(); + } + + template + auto ArrayContainer::shape() const noexcept -> const ShapeType & { + return m_shape; + } + + template + auto ArrayContainer::storage() const noexcept + -> const StorageType & { + return m_storage; + } + + template + auto ArrayContainer::storage() noexcept -> StorageType & { + return m_storage; + } + + template + auto ArrayContainer::packet(size_t index) const -> Packet { #if defined(LIBRAPID_NATIVE_ARCH) && !defined(LIBRAPID_OSX) - // On MacOS (and other platforms??) we cannot use aligned loads in arrays due to one - // annoying edge case. Normally, all SIMD loads will be aligned to a 64-byte boundary. - // Say, however, this array is a sub-array of a larger array. If the outer dimension - // of the larger array does not result in a 64-byte alignment, the data of *this* array - // will not be correctly aligned, hence causing a segfault. - return xsimd::load_aligned(m_storage.begin() + index); + // On MacOS (and other platforms??) we cannot use aligned loads in arrays due to one + // annoying edge case. Normally, all SIMD loads will be aligned to a 64-byte boundary. + // Say, however, this array is a sub-array of a larger array. If the outer dimension + // of the larger array does not result in a 64-byte alignment, the data of *this* array + // will not be correctly aligned, hence causing a segfault. + return xsimd::load_aligned(m_storage.begin() + index); #else - return xsimd::load_unaligned(m_storage.begin() + index); + return xsimd::load_unaligned(m_storage.begin() + index); #endif - } + } - template - auto ArrayContainer::scalar(size_t index) const -> Scalar { - return m_storage[index]; - } + template + auto ArrayContainer::scalar(size_t index) const -> Scalar { + return m_storage[index]; + } - template - void ArrayContainer::writePacket(size_t index, - const Packet &value) { + template + void ArrayContainer::writePacket(size_t index, + const Packet &value) { #if defined(LIBRAPID_NATIVE_ARCH) - value.store_aligned(m_storage.begin() + index); + value.store_aligned(m_storage.begin() + index); #else - value.store_unaligned(m_storage.begin() + index); + value.store_unaligned(m_storage.begin() + index); #endif - } - - template - void ArrayContainer::write(size_t index, const Scalar &value) { - m_storage[index] = value; - } - - template - template - auto ArrayContainer::operator+=(const T &value) - -> ArrayContainer & { - *this = *this + value; - return *this; - } - - template - template - auto ArrayContainer::operator-=(const T &value) - -> ArrayContainer & { - *this = *this - value; - return *this; - } - - template - template - auto ArrayContainer::operator*=(const T &value) - -> ArrayContainer & { - *this = *this * value; - return *this; - } - - template - template - auto ArrayContainer::operator/=(const T &value) - -> ArrayContainer & { - *this = *this / value; - return *this; - } - - template - template - auto ArrayContainer::operator%=(const T &value) - -> ArrayContainer & { - *this = *this % value; - return *this; - } - - template - template - auto ArrayContainer::operator&=(const T &value) - -> ArrayContainer & { - *this = *this & value; - return *this; - } - - template - template - auto ArrayContainer::operator|=(const T &value) - -> ArrayContainer & { - *this = *this | value; - return *this; - } - - template - template - auto ArrayContainer::operator^=(const T &value) - -> ArrayContainer & { - *this = *this ^ value; - return *this; - } - - template - template - auto ArrayContainer::operator<<=(const T &value) - -> ArrayContainer & { - *this = *this << value; - return *this; - } - - template - template - auto ArrayContainer::operator>>=(const T &value) - -> ArrayContainer & { - *this = *this >> value; - return *this; - } - - template - auto ArrayContainer::begin() const noexcept -> Iterator { - return Iterator(GeneralArrayView(*this), 0); - } - - template - auto ArrayContainer::end() const noexcept -> Iterator { - return Iterator(GeneralArrayView(*this), m_shape[0]); - } - - template - auto ArrayContainer::begin() -> Iterator { - return Iterator(GeneralArrayView(*this), 0); - } - - template - auto ArrayContainer::end() -> Iterator { - return Iterator(GeneralArrayView(*this), m_shape[0]); - } - - template - template - void ArrayContainer::str(const fmt::formatter &format, - char bracket, char separator, - Ctx &ctx) const { + } + + template + void ArrayContainer::write(size_t index, const Scalar &value) { + m_storage[index] = value; + } + + template + template + auto ArrayContainer::operator+=(const T &value) + -> ArrayContainer & { + *this = *this + value; + return *this; + } + + template + template + auto ArrayContainer::operator-=(const T &value) + -> ArrayContainer & { + *this = *this - value; + return *this; + } + + template + template + auto ArrayContainer::operator*=(const T &value) + -> ArrayContainer & { + *this = *this * value; + return *this; + } + + template + template + auto ArrayContainer::operator/=(const T &value) + -> ArrayContainer & { + *this = *this / value; + return *this; + } + + template + template + auto ArrayContainer::operator%=(const T &value) + -> ArrayContainer & { + *this = *this % value; + return *this; + } + + template + template + auto ArrayContainer::operator&=(const T &value) + -> ArrayContainer & { + *this = *this & value; + return *this; + } + + template + template + auto ArrayContainer::operator|=(const T &value) + -> ArrayContainer & { + *this = *this | value; + return *this; + } + + template + template + auto ArrayContainer::operator^=(const T &value) + -> ArrayContainer & { + *this = *this ^ value; + return *this; + } + + template + template + auto ArrayContainer::operator<<=(const T &value) + -> ArrayContainer & { + *this = *this << value; + return *this; + } + + template + template + auto ArrayContainer::operator>>=(const T &value) + -> ArrayContainer & { + *this = *this >> value; + return *this; + } + + template + auto ArrayContainer::begin() const noexcept -> Iterator { + return Iterator(GeneralArrayView(*this), 0); + } + + template + auto ArrayContainer::end() const noexcept -> Iterator { + return Iterator(GeneralArrayView(*this), m_shape[0]); + } + + template + auto ArrayContainer::begin() -> Iterator { + return Iterator(GeneralArrayView(*this), 0); + } + + template + auto ArrayContainer::end() -> Iterator { + return Iterator(GeneralArrayView(*this), m_shape[0]); + } + + template + template + void ArrayContainer::str(const fmt::formatter &format, + char bracket, char separator, + Ctx &ctx) const { GeneralArrayView(*this).str(format, bracket, separator, ctx); - } - } // namespace array - - namespace detail { - template - struct IsArrayType { - static constexpr bool val = false; - }; - - template - struct IsArrayType> { - static constexpr bool val = true; - }; - - template - struct IsArrayType> { - static constexpr bool val = true; - }; - - template - struct IsArrayType> { - static constexpr bool val = true; - }; - - template - struct ContainsArrayType { - static constexpr auto evaluator() { - if constexpr (sizeof...(Types) == 0) - return IsArrayType::val; - else - return IsArrayType::val || ContainsArrayType::val; - }; - - static constexpr bool val = evaluator(); - }; - }; // namespace detail + } + } // namespace array + + namespace detail { + template + struct IsArrayType { + static constexpr bool val = false; + }; + + template + struct IsArrayType> { + static constexpr bool val = true; + }; + + template + struct IsArrayType> { + static constexpr bool val = true; + }; + + template + struct IsArrayType> { + static constexpr bool val = true; + }; + + template + struct ContainsArrayType { + static constexpr auto evaluator() { + if constexpr (sizeof...(Types) == 0) + return IsArrayType::val; + else + return IsArrayType::val || ContainsArrayType::val; + }; + + static constexpr bool val = evaluator(); + }; + }; // namespace detail } // namespace librapid // Support FMT printing #ifdef FMT_API ARRAY_TYPE_FMT_IML(typename ShapeType_ COMMA typename StorageType_, - librapid::array::ArrayContainer) + librapid::array::ArrayContainer) LIBRAPID_SIMPLE_IO_NORANGE(typename ShapeType_ COMMA typename StorageType_, - librapid::array::ArrayContainer) + librapid::array::ArrayContainer) #endif // FMT_API #endif // LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP \ No newline at end of file diff --git a/librapid/include/librapid/array/arrayTypeDef.hpp b/librapid/include/librapid/array/arrayTypeDef.hpp index fb173744..147f1fb3 100644 --- a/librapid/include/librapid/array/arrayTypeDef.hpp +++ b/librapid/include/librapid/array/arrayTypeDef.hpp @@ -2,141 +2,142 @@ #define LIBRAPID_ARRAY_TYPE_DEF_HPP namespace librapid { - namespace detail { - template - struct TypeDefStorageEvaluator { - using Type = T; - }; + namespace detail { + template + struct TypeDefStorageEvaluator { + using Type = T; + }; - template - struct TypeDefStorageEvaluator { - using Type = Storage; - }; + template + struct TypeDefStorageEvaluator { + using Type = Storage; + }; - template - struct TypeDefStorageEvaluator { - using Type = OpenCLStorage; - }; + template + struct TypeDefStorageEvaluator { + using Type = OpenCLStorage; + }; - template - struct TypeDefStorageEvaluator { - using Type = CudaStorage; - }; - } // namespace detail + template + struct TypeDefStorageEvaluator { + using Type = CudaStorage; + }; + } // namespace detail - /// An easier to use definition than ArrayContainer. In this case, StorageType can be - /// `backend::CPU`, `backend::CUDA` or any Storage interface - /// \tparam Scalar The scalar type of the array. - /// \tparam StorageType The storage type of the array. - template - using Array = - array::ArrayContainer, - typename detail::TypeDefStorageEvaluator::Type>; + /// An easier to use definition than ArrayContainer. In this case, StorageType can be + /// `backend::CPU`, `backend::CUDA` or any Storage interface + /// \tparam Scalar The scalar type of the array. + /// \tparam StorageType The storage type of the array. + template + using Array = + array::ArrayContainer, + typename detail::TypeDefStorageEvaluator::Type>; - /// A definition for fixed-size array objects. - /// \tparam Scalar The scalar type of the array. - /// \tparam Dimensions The dimensions of the array. - /// \see Array - template - using ArrayF = array::ArrayContainer, FixedStorage>; + /// A definition for fixed-size array objects. + /// \tparam Scalar The scalar type of the array. + /// \tparam Dimensions The dimensions of the array. + /// \see Array + template + using ArrayF = + array::ArrayContainer, FixedStorage>; - /// A reference type for Array objects. Use this to accept Array objects as parameters since - /// the compiler cannot determine the templates tingle for the Array typedef. For more - /// granularity, you can also accept a raw ArrayContainer object. \tparam StorageType The - /// storage type of the array. \see Array \see ArrayF \see Function \see FunctionRef - template - using ArrayRef = array::ArrayContainer, StorageType>; + /// A reference type for Array objects. Use this to accept Array objects as parameters since + /// the compiler cannot determine the templates tingle for the Array typedef. For more + /// granularity, you can also accept a raw ArrayContainer object. \tparam StorageType The + /// storage type of the array. \see Array \see ArrayF \see Function \see FunctionRef + template + using ArrayRef = array::ArrayContainer, StorageType>; - /// A reference type for Array Function objects. Use this to accept Function objects as - /// parameters since the compiler cannot determine the templates for the typedef by default. - /// Additionally, this can be used to store references to Function objects. - /// \tparam Inputs The argument types to the function (template...) - /// \see Array - /// \see ArrayF - /// \see ArrayRef - /// \see Function - template - using FunctionRef = detail::Function; + /// A reference type for Array Function objects. Use this to accept Function objects as + /// parameters since the compiler cannot determine the templates for the typedef by default. + /// Additionally, this can be used to store references to Function objects. + /// \tparam Inputs The argument types to the function (template...) + /// \see Array + /// \see ArrayF + /// \see ArrayRef + /// \see Function + template + using FunctionRef = detail::Function; - namespace array { - /// An intermediate type to represent a slice or view of an array. - /// \tparam T The type of the array. - template - class GeneralArrayView; + namespace array { + /// An intermediate type to represent a slice or view of an array. + /// \tparam T The type of the array. + template + class GeneralArrayView; - template - class Transpose; - } // namespace array + template + class Transpose; + } // namespace array - namespace linalg { - template - class ArrayMultiply; - } + namespace linalg { + template + class ArrayMultiply; + } - template - using IsArrayType = std::integral_constant< - bool, (typetraits::TypeInfo::type == detail::LibRapidType::ArrayContainer) || - (typetraits::TypeInfo::type == detail::LibRapidType::GeneralArrayView) || - (typetraits::TypeInfo::type == detail::LibRapidType::ArrayFunction)>; + template + using IsArrayType = std::integral_constant< + bool, (typetraits::TypeInfo::type == detail::LibRapidType::ArrayContainer) || + (typetraits::TypeInfo::type == detail::LibRapidType::GeneralArrayView) || + (typetraits::TypeInfo::type == detail::LibRapidType::ArrayFunction)>; #define ARRAY_TYPE_FMT_IML(TEMPLATE_, TYPE_) \ - template \ - struct fmt::formatter { \ - using Type = TYPE_; \ - using Scalar = typename librapid::typetraits::TypeInfo::Scalar; \ - using Formatter = fmt::formatter; \ - Formatter m_formatter; \ - char m_bracket = 's'; \ - char m_separator = ' '; \ + template \ + struct fmt::formatter { \ + using Type = TYPE_; \ + using Scalar = typename librapid::typetraits::TypeInfo::Scalar; \ + using Formatter = fmt::formatter; \ + Formatter m_formatter; \ + char m_bracket = 's'; \ + char m_separator = ' '; \ \ - template \ - FMT_CONSTEXPR auto parse(ParseContext &ctx) -> const char * { \ - /* Custom format options: */ \ - /* - "~r" for round brackets */ \ - /* - "~s" for square brackets */ \ - /* - "~c" for curly brackets */ \ - /* - "~a" for angle brackets */ \ - /* - "~p" for pipe brackets */ \ - /* - "-," for comma separator */ \ - /* - "-;" for semicolon separator */ \ - /* - "-:" for colon separator */ \ - /* - "-|" for pipe separator */ \ - /* - "-_" for underscore separator */ \ + template \ + FMT_CONSTEXPR auto parse(ParseContext &ctx) -> const char * { \ + /* Custom format options: */ \ + /* - "~r" for round brackets */ \ + /* - "~s" for square brackets */ \ + /* - "~c" for curly brackets */ \ + /* - "~a" for angle brackets */ \ + /* - "~p" for pipe brackets */ \ + /* - "-," for comma separator */ \ + /* - "-;" for semicolon separator */ \ + /* - "-:" for colon separator */ \ + /* - "-|" for pipe separator */ \ + /* - "-_" for underscore separator */ \ \ - auto it = ctx.begin(), end = ctx.end(); \ - if (it != end && *it == '~') { \ - ++it; \ - if (it != end && \ - (*it == 'r' || *it == 's' || *it == 'c' || *it == 'a' || *it == 'p')) { \ - m_bracket = *it++; \ - } \ - } \ + auto it = ctx.begin(), end = ctx.end(); \ + if (it != end && *it == '~') { \ + ++it; \ + if (it != end && \ + (*it == 'r' || *it == 's' || *it == 'c' || *it == 'a' || *it == 'p')) { \ + m_bracket = *it++; \ + } \ + } \ \ - if (it != end && *it == '-') { \ - ++it; \ - if (it != end) { m_separator = *it++; } \ - } \ + if (it != end && *it == '-') { \ + ++it; \ + if (it != end) { m_separator = *it++; } \ + } \ \ - ctx.advance_to(it); \ + ctx.advance_to(it); \ \ - return m_formatter.parse(ctx); \ - } \ + return m_formatter.parse(ctx); \ + } \ \ - template \ - FMT_CONSTEXPR auto format(const Type &val, FormatContext &ctx) const \ - -> decltype(ctx.out()) { \ - val.str(m_formatter, m_bracket, m_separator, ctx); \ - return ctx.out(); \ - } \ - }; \ + template \ + FMT_CONSTEXPR auto format(const Type &val, FormatContext &ctx) const \ + -> decltype(ctx.out()) { \ + val.str(m_formatter, m_bracket, m_separator, ctx); \ + return ctx.out(); \ + } \ + }; \ \ - template \ - auto operator<<(std::ostream &os, const TYPE_ &object) -> std::ostream & { \ - os << fmt::format("{}", object); \ - return os; \ - } + template \ + auto operator<<(std::ostream &os, const TYPE_ &object) -> std::ostream & { \ + os << fmt::format("{}", object); \ + return os; \ + } } // namespace librapid #endif // LIBRAPID_ARRAY_TYPE_DEF_HPP \ No newline at end of file diff --git a/librapid/include/librapid/array/function.hpp b/librapid/include/librapid/array/function.hpp index 02a6e62f..2bfc3928 100644 --- a/librapid/include/librapid/array/function.hpp +++ b/librapid/include/librapid/array/function.hpp @@ -111,7 +111,7 @@ namespace librapid { public: using Type = Function; using Functor = Functor_; - using ShapeType = Shape; + using ShapeType = Shape; using StrideType = ShapeType; using Scalar = typename typetraits::TypeInfo::Scalar; using Backend = typename typetraits::TypeInfo::Backend; diff --git a/librapid/include/librapid/array/generalArrayViewToString.hpp b/librapid/include/librapid/array/generalArrayViewToString.hpp index 45000781..2d4167dc 100644 --- a/librapid/include/librapid/array/generalArrayViewToString.hpp +++ b/librapid/include/librapid/array/generalArrayViewToString.hpp @@ -2,85 +2,85 @@ #define LIBRAPID_ARRAY_ARRAY_VIEW_STRING_HPP namespace librapid { - namespace detail { - template - void generalArrayViewToString(const array::GeneralArrayView &view, - const fmt::formatter &formatter, char bracket, - char separator, int64_t indent, Ctx &ctx) { - char bracketCharOpen, bracketCharClose; + namespace detail { + template + void generalArrayViewToString(const array::GeneralArrayView &view, + const fmt::formatter &formatter, char bracket, + char separator, int64_t indent, Ctx &ctx) { + char bracketCharOpen, bracketCharClose; - switch (bracket) { - case 'r': - bracketCharOpen = '('; - bracketCharClose = ')'; - break; - case 's': - bracketCharOpen = '['; - bracketCharClose = ']'; - break; - case 'c': - bracketCharOpen = '{'; - bracketCharClose = '}'; - break; - case 'a': - bracketCharOpen = '<'; - bracketCharClose = '>'; - break; - case 'p': - bracketCharOpen = '|'; - bracketCharClose = '|'; - break; - default: - bracketCharOpen = '['; - bracketCharClose = ']'; - break; - } + switch (bracket) { + case 'r': + bracketCharOpen = '('; + bracketCharClose = ')'; + break; + case 's': + bracketCharOpen = '['; + bracketCharClose = ']'; + break; + case 'c': + bracketCharOpen = '{'; + bracketCharClose = '}'; + break; + case 'a': + bracketCharOpen = '<'; + bracketCharClose = '>'; + break; + case 'p': + bracketCharOpen = '|'; + bracketCharClose = '|'; + break; + default: + bracketCharOpen = '['; + bracketCharClose = ']'; + break; + } - // Separator char is already the correct character + // Separator char is already the correct character - if (view.ndim() == 0) { - formatter.format(view.scalar(0), ctx); - } else if (view.ndim() == 1) { - fmt::format_to(ctx.out(), "{}", bracketCharOpen); - for (int64_t i = 0; i < static_cast(view.shape()[0]); i++) { - formatter.format(view.scalar(i), ctx); - if (i != view.shape()[0] - 1) { - if (separator == ' ') { - fmt::format_to(ctx.out(), " "); - } else { - fmt::format_to(ctx.out(), "{} ", separator); - } - } - } - fmt::format_to(ctx.out(), "{}", bracketCharClose); - } else { - fmt::format_to(ctx.out(), "{}", bracketCharOpen); - for (int64_t i = 0; i < static_cast(view.shape()[0]); i++) { - if (i > 0) fmt::format_to(ctx.out(), "{}", std::string(indent + 1, ' ')); + if (view.ndim() == 0) { + formatter.format(view.scalar(0), ctx); + } else if (view.ndim() == 1) { + fmt::format_to(ctx.out(), "{}", bracketCharOpen); + for (int64_t i = 0; i < static_cast(view.shape()[0]); i++) { + formatter.format(view.scalar(i), ctx); + if (i != view.shape()[0] - 1) { + if (separator == ' ') { + fmt::format_to(ctx.out(), " "); + } else { + fmt::format_to(ctx.out(), "{} ", separator); + } + } + } + fmt::format_to(ctx.out(), "{}", bracketCharClose); + } else { + fmt::format_to(ctx.out(), "{}", bracketCharOpen); + for (int64_t i = 0; i < static_cast(view.shape()[0]); i++) { + if (i > 0) fmt::format_to(ctx.out(), "{}", std::string(indent + 1, ' ')); generalArrayViewToString( view[i], formatter, bracket, separator, indent + 1, ctx); - if (i != view.shape()[0] - 1) { - if (separator == ' ') { - fmt::format_to(ctx.out(), "\n"); - } else { - fmt::format_to(ctx.out(), "{}\n", separator); - } - if (view.ndim() > 2) { fmt::format_to(ctx.out(), "\n"); } - } - } - fmt::format_to(ctx.out(), "{}", bracketCharClose); - } - } - } // namespace detail + if (i != view.shape()[0] - 1) { + if (separator == ' ') { + fmt::format_to(ctx.out(), "\n"); + } else { + fmt::format_to(ctx.out(), "{}\n", separator); + } + if (view.ndim() > 2) { fmt::format_to(ctx.out(), "\n"); } + } + } + fmt::format_to(ctx.out(), "{}", bracketCharClose); + } + } + } // namespace detail - namespace array { - template - template - void GeneralArrayView::str(const fmt::formatter &format, char bracket, - char separator, Ctx &ctx) const { + namespace array { + template + template + void GeneralArrayView::str(const fmt::formatter &format, + char bracket, char separator, Ctx &ctx) const { detail::generalArrayViewToString(*this, format, bracket, separator, 0, ctx); - } - } // namespace array + } + } // namespace array } // namespace librapid #endif // LIBRAPID_ARRAY_ARRAY_VIEW_STRING_HPP \ No newline at end of file diff --git a/librapid/include/librapid/array/pseudoConstructors.hpp b/librapid/include/librapid/array/pseudoConstructors.hpp index 58499e4b..91c6427c 100644 --- a/librapid/include/librapid/array/pseudoConstructors.hpp +++ b/librapid/include/librapid/array/pseudoConstructors.hpp @@ -2,227 +2,227 @@ #define LIBRAPID_ARRAY_PSEUDO_CONSTRUCTORS_HPP namespace librapid { - /// \brief Force the input to be evaluated to an Array - /// - /// When given a scalar or Array type, this function will return the input unchanged. When given - /// a Function, it will evaluate the function and return the result. This is useful for - /// functions which require an Array instance as input and cannot function with function types. - /// - /// Note that the input is not copied or moved, so the returned Array will be a reference to the - /// input. - /// - /// \tparam T Input type - /// \param other Input - /// \return Evaluated input - template - auto evaluated(const T &other) { - return other; - } - - template - auto evaluated(const array::ArrayContainer &other) { - return other; - } - - template - auto evaluated(const detail::Function &other) { - return other.eval(); - } - - /// \brief Create a new array with the same type and shape as the input array, but without - /// initializing any of the data - /// \tparam T Input array type - /// \param other Input array - /// \return New array - template - auto emptyLike(const T &other) { - using Scalar = typename typetraits::TypeInfo::Scalar; - using Backend = typename typetraits::TypeInfo::Backend; - return Array(other.shape()); - } - - /// \brief Create an Array filled with zeros - /// - /// Create an array with a specified shape, scalar type and Backend, and fill it with zeros. - /// - /// \tparam Scalar Scalar type of the Array - /// \tparam Backend Backend type of the Array - /// \tparam T Scalar type of the Shape - /// \tparam N Maximum number of dimensions of the Shape - /// \param shape Shape of the Array - /// \return Array filled with zeros - template - Array zeros(const Shape &shape) { - return Array(shape, Scalar(0)); - } - - /// \brief Create an Array filled with zeros, with the same type and shape as the input array - /// - /// \tparam T Input array type - /// \param other Input array - /// \return New array - template - auto zerosLike(const T &other) { - using Scalar = typename typetraits::TypeInfo::Scalar; - using Backend = typename typetraits::TypeInfo::Backend; - return zeros(other.shape()); - } - - /// \brief Create an Array filled with ones - /// - /// Create an array with a specified shape, scalar type and Backend, and fill it with ones. - /// - /// \tparam Scalar Scalar type of the Array - /// \tparam Backend Backend type of the Array - /// \tparam T Scalar type of the Shape - /// \tparam N Maximum number of dimensions of the Shape - /// \param shape Shape of the Array - /// \return Array filled with ones - template - Array ones(const Shape &shape) { - return Array(shape, Scalar(1)); - } - - /// \brief Create an Array filled with ones, with the same type and shape as the input array - /// - /// \tparam T Input array type - /// \param other Input array - /// \return New array - template - auto onesLike(const T &other) { - using Scalar = typename typetraits::TypeInfo::Scalar; - using Backend = typename typetraits::TypeInfo::Backend; - return ones(other.shape()); - } - - /// \brief Create an Array filled, in order, with the numbers 0 to N-1 - /// - /// Create a new Array object with a given shape, where each value is filled with a number from - /// 0 to N-1, where N is the total number of elements in the array. The values are filled in - /// the same order as the array is stored in memory. - /// - /// \tparam Scalar Scalar type of the Array - /// \tparam Backend Backend type of the Array - /// \tparam T Scalar type of the Shape - /// \tparam N Maximum number of dimensions of the Shape - /// \param shape Shape of the Array - /// \return Array filled with numbers from 0 to N-1 - template - Array ordered(const Shape &shape) { - Array result(shape); - for (size_t i = 0; i < shape.size(); i++) { result.storage()[i] = Scalar(i); } - return result; - } - - /// \brief Create a 1-dimensional Array from a range of numbers and a step size - /// - /// Provided with a start value and a stop value, create a 1-dimensional Array with - /// \f$\lfloor \frac{stop - start}{step} \rfloor \f$ elements, where each element is - /// \f$start + i \times step\f$, for \f$i \in [0, \lfloor \frac{stop - start}{step} \rfloor)\f$. - /// - /// \tparam Scalar Scalar type of the Array - /// \tparam Backend Backend for the Array - /// \tparam Start Scalar type of the start value - /// \tparam Stop Scalar type of the stop value - /// \tparam Step Scalar type of the step size - /// \param start First value in the range - /// \param stop Second value in the range - /// \param step Step size between values in the range - /// \return Array - template - Array arange(Start start, Stop stop, Step step) { - LIBRAPID_ASSERT(step != 0, "Step size cannot be zero"); - LIBRAPID_ASSERT((stop - start) / step > 0, "Step size is invalid for the specified range"); - - Shape shape = {(int64_t)::librapid::abs((stop - start) / step)}; - Array result(shape); - for (size_t i = 0; i < shape.size(); i++) { - result.storage()[i] = Scalar(start + i * step); - } - return result; - } - - template - Array arange(T start, T stop) { - LIBRAPID_ASSERT((stop - start) > 0, "Step size is invalid for the specified range"); - - Shape shape = {(int64_t)::librapid::abs(stop - start)}; - Array result(shape); - for (size_t i = 0; i < shape.size(); i++) { result.storage()[i] = Scalar(start + i); } - return result; - } - - template - Array arange(T stop) { - Shape shape = {(int64_t)::librapid::abs(stop)}; - Array result(shape); - for (size_t i = 0; i < shape.size(); i++) { result.storage()[i] = Scalar(i); } - return result; - } - - /// \brief Create a 1-dimensional Array with a specified number of elements, evenly spaced - /// between two values - /// - /// Create a 1-dimensional Array with a specified number of elements, evenly spaced between - /// two values. If \p includeEnd is true, the last element of the Array will be equal to - /// \p stop, otherwise it will be equal to \p stop - \f$\frac{stop - start}{num}\f$. - /// - /// \tparam Scalar Scalar type of the Array - /// \tparam Backend Backend for the Array - /// \tparam Start Scalar type of the start value - /// \tparam Stop Scalar type of the stop value - /// \param start First value in the range - /// \param stop Second value in the range - /// \param num Number of elements in the Array - /// \param includeEnd Whether or not to include the end value in the Array - /// \return Linearly spaced Array - template - Array linspace(Start start, Stop stop, int64_t num, bool includeEnd = true) { - LIBRAPID_ASSERT(num > 0, "Number of samples must be greater than zero"); - - auto startCast = static_cast(start); - auto stopCast = static_cast(stop); - auto den = static_cast(num - includeEnd); - Shape shape = {num}; - Array result(shape); - for (size_t i = 0; i < shape.size(); i++) { - result.storage()[i] = startCast + (stopCast - startCast) * static_cast(i) / den; - } - return result; - } - - template - Array logspace(Start start, Stop stop, int64_t num, bool includeEnd = true) { - LIBRAPID_ASSERT(num > 0, "Number of samples must be greater than zero"); - - auto logLower = ::librapid::log(static_cast(start)); - auto logUpper = ::librapid::log(static_cast(stop)); - - Shape shape = {num}; - Array result(shape); - - for (size_t i = 0; i < shape.size(); i++) { - result.storage()[i] = - ::librapid::exp(logLower + (logUpper - logLower) * static_cast(i) / - static_cast(num - includeEnd)); - } - - return result; - } - - template - Array random(const ShapeType &shape, Lower lower = 0, Upper upper = 1) { - Array result(shape); - fillRandom(result, lower, upper); - return result; - } + /// \brief Force the input to be evaluated to an Array + /// + /// When given a scalar or Array type, this function will return the input unchanged. When given + /// a Function, it will evaluate the function and return the result. This is useful for + /// functions which require an Array instance as input and cannot function with function types. + /// + /// Note that the input is not copied or moved, so the returned Array will be a reference to the + /// input. + /// + /// \tparam T Input type + /// \param other Input + /// \return Evaluated input + template + auto evaluated(const T &other) { + return other; + } + + template + auto evaluated(const array::ArrayContainer &other) { + return other; + } + + template + auto evaluated(const detail::Function &other) { + return other.eval(); + } + + /// \brief Create a new array with the same type and shape as the input array, but without + /// initializing any of the data + /// \tparam T Input array type + /// \param other Input array + /// \return New array + template + auto emptyLike(const T &other) { + using Scalar = typename typetraits::TypeInfo::Scalar; + using Backend = typename typetraits::TypeInfo::Backend; + return Array(other.shape()); + } + + /// \brief Create an Array filled with zeros + /// + /// Create an array with a specified shape, scalar type and Backend, and fill it with zeros. + /// + /// \tparam Scalar Scalar type of the Array + /// \tparam Backend Backend type of the Array + /// \tparam T Scalar type of the Shape + /// \tparam N Maximum number of dimensions of the Shape + /// \param shape Shape of the Array + /// \return Array filled with zeros + template + Array zeros(const Shape &shape) { + return Array(shape, Scalar(0)); + } + + /// \brief Create an Array filled with zeros, with the same type and shape as the input array + /// + /// \tparam T Input array type + /// \param other Input array + /// \return New array + template + auto zerosLike(const T &other) { + using Scalar = typename typetraits::TypeInfo::Scalar; + using Backend = typename typetraits::TypeInfo::Backend; + return zeros(other.shape()); + } + + /// \brief Create an Array filled with ones + /// + /// Create an array with a specified shape, scalar type and Backend, and fill it with ones. + /// + /// \tparam Scalar Scalar type of the Array + /// \tparam Backend Backend type of the Array + /// \tparam T Scalar type of the Shape + /// \tparam N Maximum number of dimensions of the Shape + /// \param shape Shape of the Array + /// \return Array filled with ones + template + Array ones(const Shape &shape) { + return Array(shape, Scalar(1)); + } + + /// \brief Create an Array filled with ones, with the same type and shape as the input array + /// + /// \tparam T Input array type + /// \param other Input array + /// \return New array + template + auto onesLike(const T &other) { + using Scalar = typename typetraits::TypeInfo::Scalar; + using Backend = typename typetraits::TypeInfo::Backend; + return ones(other.shape()); + } + + /// \brief Create an Array filled, in order, with the numbers 0 to N-1 + /// + /// Create a new Array object with a given shape, where each value is filled with a number from + /// 0 to N-1, where N is the total number of elements in the array. The values are filled in + /// the same order as the array is stored in memory. + /// + /// \tparam Scalar Scalar type of the Array + /// \tparam Backend Backend type of the Array + /// \tparam T Scalar type of the Shape + /// \tparam N Maximum number of dimensions of the Shape + /// \param shape Shape of the Array + /// \return Array filled with numbers from 0 to N-1 + template + Array ordered(const Shape &shape) { + Array result(shape); + for (size_t i = 0; i < shape.size(); i++) { result.storage()[i] = Scalar(i); } + return result; + } + + /// \brief Create a 1-dimensional Array from a range of numbers and a step size + /// + /// Provided with a start value and a stop value, create a 1-dimensional Array with + /// \f$\lfloor \frac{stop - start}{step} \rfloor \f$ elements, where each element is + /// \f$start + i \times step\f$, for \f$i \in [0, \lfloor \frac{stop - start}{step} \rfloor)\f$. + /// + /// \tparam Scalar Scalar type of the Array + /// \tparam Backend Backend for the Array + /// \tparam Start Scalar type of the start value + /// \tparam Stop Scalar type of the stop value + /// \tparam Step Scalar type of the step size + /// \param start First value in the range + /// \param stop Second value in the range + /// \param step Step size between values in the range + /// \return Array + template + Array arange(Start start, Stop stop, Step step) { + LIBRAPID_ASSERT(step != 0, "Step size cannot be zero"); + LIBRAPID_ASSERT((stop - start) / step > 0, "Step size is invalid for the specified range"); + + Shape shape = {(int64_t)::librapid::abs((stop - start) / step)}; + Array result(shape); + for (size_t i = 0; i < shape.size(); i++) { + result.storage()[i] = Scalar(start + i * step); + } + return result; + } + + template + Array arange(T start, T stop) { + LIBRAPID_ASSERT((stop - start) > 0, "Step size is invalid for the specified range"); + + Shape shape = {(int64_t)::librapid::abs(stop - start)}; + Array result(shape); + for (size_t i = 0; i < shape.size(); i++) { result.storage()[i] = Scalar(start + i); } + return result; + } + + template + Array arange(T stop) { + Shape shape = {(int64_t)::librapid::abs(stop)}; + Array result(shape); + for (size_t i = 0; i < shape.size(); i++) { result.storage()[i] = Scalar(i); } + return result; + } + + /// \brief Create a 1-dimensional Array with a specified number of elements, evenly spaced + /// between two values + /// + /// Create a 1-dimensional Array with a specified number of elements, evenly spaced between + /// two values. If \p includeEnd is true, the last element of the Array will be equal to + /// \p stop, otherwise it will be equal to \p stop - \f$\frac{stop - start}{num}\f$. + /// + /// \tparam Scalar Scalar type of the Array + /// \tparam Backend Backend for the Array + /// \tparam Start Scalar type of the start value + /// \tparam Stop Scalar type of the stop value + /// \param start First value in the range + /// \param stop Second value in the range + /// \param num Number of elements in the Array + /// \param includeEnd Whether or not to include the end value in the Array + /// \return Linearly spaced Array + template + Array linspace(Start start, Stop stop, int64_t num, bool includeEnd = true) { + LIBRAPID_ASSERT(num > 0, "Number of samples must be greater than zero"); + + auto startCast = static_cast(start); + auto stopCast = static_cast(stop); + auto den = static_cast(num - includeEnd); + Shape shape = {num}; + Array result(shape); + for (size_t i = 0; i < shape.size(); i++) { + result.storage()[i] = startCast + (stopCast - startCast) * static_cast(i) / den; + } + return result; + } + + template + Array logspace(Start start, Stop stop, int64_t num, bool includeEnd = true) { + LIBRAPID_ASSERT(num > 0, "Number of samples must be greater than zero"); + + auto logLower = ::librapid::log(static_cast(start)); + auto logUpper = ::librapid::log(static_cast(stop)); + + Shape shape = {num}; + Array result(shape); + + for (size_t i = 0; i < shape.size(); i++) { + result.storage()[i] = + ::librapid::exp(logLower + (logUpper - logLower) * static_cast(i) / + static_cast(num - includeEnd)); + } + + return result; + } + + template + Array random(const ShapeType &shape, Lower lower = 0, Upper upper = 1) { + Array result(shape); + fillRandom(result, lower, upper); + return result; + } } // namespace librapid #endif // LIBRAPID_ARRAY_PSEUDO_CONSTRUCTORS_HPP \ No newline at end of file diff --git a/librapid/include/librapid/array/shape.hpp b/librapid/include/librapid/array/shape.hpp new file mode 100644 index 00000000..932d0a55 --- /dev/null +++ b/librapid/include/librapid/array/shape.hpp @@ -0,0 +1,376 @@ +#ifndef LIBRAPID_ARRAY_SIZETYPE_HPP +#define LIBRAPID_ARRAY_SIZETYPE_HPP + +/* + * This file defines the Shape class and some helper functions, + * including stride operations. + */ + +namespace librapid { + namespace typetraits { + LIBRAPID_DEFINE_AS_TYPE(size_t N, Shape); + } + + template + class Shape { + public: + using SizeType = uint32_t; + static constexpr size_t MaxDimensions = N; + + /// Default constructor + Shape() = default; + + /// Create a shape object from the dimensions of a FixedStorage object. This is used + // mainly internally, but may serve some purpose I haven't yet thought of. + /// \tparam Scalar Scalar type of the FixedStorage object + /// \tparam Dimensions Dimensions of the FixedStorage object + /// \param fixed The FixedStorage object + template + explicit Shape(const FixedStorage &fixed); + + /// Create a Shape object from a list of values + /// \tparam V Scalar type of the values + /// \param vals The dimensions for the object + template + Shape(const std::initializer_list &vals); + + /// Create a Shape object from a vector of values + /// \tparam V Scalar type of the values + /// \param vals The dimensions for the object + template + explicit Shape(const std::vector &vals); + + /// Create a copy of a Shape object + /// \param other Shape object to copy + Shape(const Shape &other) = default; + + /// Create a Shape from an RValue + /// \param other Temporary Shape object to copy + Shape(Shape &&other) noexcept = default; + + /// Create a Shape object from one with a different type and number of dimensions. + /// \tparam V Scalar type of the values + /// \tparam Dim Number of dimensions + /// \param other Shape object to copy + template + Shape(const Shape &other); + + /// Create a Shape object from one with a different type and number of dimensions, moving it + /// instead of copying it. + /// \tparam V Scalar type of the values + /// \tparam Dim Number of dimensions + /// \param other Temporary Shape object to move + template + Shape(Shape &&other) noexcept; + + /// Assign a Shape object to this object + /// \tparam V Scalar type of the Shape + /// \param vals Dimensions of the Shape + /// \return *this + template + auto operator=(const std::initializer_list &vals) -> Shape &; + + /// Assign a Shape object to this object + /// \tparam V Scalar type of the Shape + /// \param vals Dimensions of the Shape + /// \return *this + template + auto operator=(const std::vector &vals) -> Shape &; + + /// Assign an RValue Shape to this object + /// \param other RValue to move + /// \return + auto operator=(Shape &&other) noexcept -> Shape & = default; + + /// Assign a Shape to this object + /// \param other Shape to copy + /// \return + auto operator=(const Shape &other) -> Shape & = default; + + /// Return a Shape object with \p dims dimensions, all initialized to zero. + /// \param dims Number of dimensions + /// \return New Shape object + static auto zeros(size_t dims) -> Shape; + + /// Return a Shape object with \p dims dimensions, all initialized to one. + /// \param dims Number of dimensions + /// \return New Shape object + static auto ones(size_t dims) -> Shape; + + /// Access an element of the Shape object + /// \tparam Index Typename of the index + /// \param index Index to access + /// \return The value at the index + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](Index index) const + -> const SizeType &; + + /// Access an element of the Shape object + /// \tparam Index Typename of the index + /// \param index Index to access + /// \return A reference to the value at the index + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](Index index) -> SizeType &; + + /// Compare two Shape objects, returning true if and only if they are identical + /// \param other Shape object to compare + /// \return true if the objects are identical + LIBRAPID_ALWAYS_INLINE auto operator==(const Shape &other) const -> bool; + + /// Compare two Shape objects, returning true if and only if they are not identical + /// \param other Shape object to compare + /// \return true if the objects are not identical + LIBRAPID_ALWAYS_INLINE auto operator!=(const Shape &other) const -> bool; + + /// Return the number of dimensions in the Shape object + /// \return Number of dimensions + LIBRAPID_NODISCARD auto ndim() const -> int; + + /// Return a subshape of the Shape object + /// \param start Starting index + /// \param end Ending index + /// \return Subshape + LIBRAPID_NODISCARD auto subshape(size_t start, size_t end) const -> Shape; + + /// Return the number of elements the Shape object represents + /// \return Number of elements + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto size() const -> size_t; + + template + void str(const fmt::formatter &format, Ctx &ctx) const; + + protected: + int m_dims; + std::array m_data; + }; + + namespace detail { + template + Shape shapeFromFixedStorage(const FixedStorage &) { + return Shape({Dims...}); + } + } // namespace detail + + template + template + Shape::Shape(const FixedStorage &) : m_data({Dimensions...}) {} + + template + template + Shape::Shape(const std::initializer_list &vals) : m_dims(vals.size()) { + LIBRAPID_ASSERT(vals.size() <= N, + "Shape object is limited to {} dimensions. Cannot initialize with {}", + N, + vals.size()); + for (size_t i = 0; i < vals.size(); ++i) { m_data[i] = *(vals.begin() + i); } + } + + template + template + Shape::Shape(const std::vector &vals) : m_dims(vals.size()) { + LIBRAPID_ASSERT(vals.size() <= N, + "Shape object is limited to {} dimensions. Cannot initialize with {}", + N, + vals.size()); + for (size_t i = 0; i < vals.size(); ++i) { m_data[i] = vals[i]; } + } + + template + template + Shape::Shape(const Shape &other) : m_dims(other.ndim()) { + LIBRAPID_ASSERT(other.ndim() <= N, + "Shape object is limited to {} dimensions. Cannot initialize with {}", + N, + other.ndim()); + for (size_t i = 0; i < m_dims; ++i) { m_data[i] = other[i]; } + } + + template + template + Shape::Shape(Shape &&other) noexcept : m_dims(other.ndim()) { + LIBRAPID_ASSERT(other.ndim() <= N, + "Shape object is limited to {} dimensions. Cannot initialize with {}", + N, + other.ndim()); + for (size_t i = 0; i < m_dims; ++i) { m_data[i] = other[i]; } + } + + template + template + auto Shape::operator=(const std::initializer_list &vals) -> Shape & { + LIBRAPID_ASSERT(vals.size() <= N, + "Shape object is limited to {} dimensions. Cannot initialize with {}", + N, + vals.size()); + m_dims = vals.size(); + for (int64_t i = 0; i < vals.size(); ++i) { m_data[i] = *(vals.begin() + i); } + return *this; + } + + template + template + auto Shape::operator=(const std::vector &vals) -> Shape & { + LIBRAPID_ASSERT(vals.size() <= N, + "Shape object is limited to {} dimensions. Cannot initialize with {}", + N, + vals.size()); + m_dims = vals.size(); + for (int64_t i = 0; i < vals.size(); ++i) { m_data[i] = vals[i]; } + return *this; + } + + template + auto Shape::zeros(size_t dims) -> Shape { + Shape res; + res.m_dims = dims; + for (size_t i = 0; i < dims; ++i) res.m_data[i] = 0; + return res; + } + + template + auto Shape::ones(size_t dims) -> Shape { + Shape res; + res.m_dims = dims; + for (size_t i = 0; i < dims; ++i) res.m_data[i] = 1; + return res; + } + + template + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto Shape::operator[](Index index) const + -> const SizeType & { + LIBRAPID_ASSERT(static_cast(index) < m_dims, "Index out of bounds"); + LIBRAPID_ASSERT(index >= 0, "Index out of bounds"); + return m_data[index]; + } + + template + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto Shape::operator[](Index index) -> SizeType & { + LIBRAPID_ASSERT(static_cast(index) < m_dims, "Index out of bounds"); + LIBRAPID_ASSERT(index >= 0, "Index out of bounds"); + return m_data[index]; + } + + template + LIBRAPID_ALWAYS_INLINE auto Shape::operator==(const Shape &other) const -> bool { + if (m_dims != other.m_dims) return false; + for (size_t i = 0; i < m_dims; ++i) { + if (m_data[i] != other.m_data[i]) return false; + } + return true; + } + + template + LIBRAPID_ALWAYS_INLINE auto Shape::operator!=(const Shape &other) const -> bool { + return !(*this == other); + } + + template + LIBRAPID_NODISCARD auto Shape::ndim() const -> int { + return m_dims; + } + + template + LIBRAPID_NODISCARD auto Shape::subshape(size_t start, size_t end) const -> Shape { + LIBRAPID_ASSERT(start <= end, "Start index must be less than end index"); + LIBRAPID_ASSERT(end <= m_dims, + "End index must be less than or equal to the number of dimensions"); + LIBRAPID_ASSERT(start >= 0, "Start index must be greater than or equal to 0"); + + Shape res; + res.m_dims = end - start; + for (size_t i = 0; i < res.m_dims; ++i) res.m_data[i] = m_data[i + start]; + return res; + } + + template + LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto Shape::size() const -> size_t { + size_t res = 1; + for (size_t i = 0; i < m_dims; ++i) res *= m_data[i]; + return res; + } + + template + template + void Shape::str(const fmt::formatter &format, Ctx &ctx) const { + fmt::format_to(ctx.out(), "Shape("); + for (size_t i = 0; i < m_dims; ++i) { + format.format(m_data[i], ctx); + if (i != m_dims - 1) fmt::format_to(ctx.out(), ", "); + } + fmt::format_to(ctx.out(), ")"); + } + + /// Returns true if all inputs have the same shape + /// \tparam T1 Type of the first input + /// \tparam N1 Number of dimensions of the first input + /// \tparam T2 Type of the second input + /// \tparam N2 Number of dimensions of the second input + /// \tparam Tn Type of the remaining (optional) inputs + /// \tparam Nn Number of dimensions of the remaining (optional) inputs + /// \param first First input + /// \param second Second input + /// \param shapes Remaining (optional) inputs + /// \return True if all inputs have the same shape, false otherwise + template + LIBRAPID_NODISCARD LIBRAPID_INLINE bool + shapesMatch(const Shape &first, const Shape &second, const Shape &...shapes) { + if constexpr (sizeof...(Nn) == 0) { + return first == second; + } else { + return first == second && shapesMatch(first, shapes...); + } + } + + /// \sa shapesMatch + template + LIBRAPID_NODISCARD LIBRAPID_INLINE bool + shapesMatch(const std::tuple, Shape, Shape...> &shapes) { + if constexpr (sizeof...(Nn) == 0) { + return std::get<0>(shapes) == std::get<1>(shapes); + } else { + return std::get<0>(shapes) == std::get<1>(shapes) && + shapesMatch(std::apply( + [](auto, auto, auto... rest) { return std::make_tuple(rest...); }, shapes)); + } + } + + namespace typetraits { + template + struct IsSizeType { + using value = std::false_type; + }; + + template + struct IsSizeType> { + using value = std::true_type; + }; + } // namespace typetraits +} // namespace librapid + +// Support FMT printing +#ifdef FMT_API +template +struct fmt::formatter> { +private: + using Type = librapid::Shape; + using SizeType = librapid::Shape::SizeType; + using Base = fmt::formatter; + Base m_base; + +public: + template + FMT_CONSTEXPR auto parse(ParseContext &ctx) -> const char * { + return m_base.parse(ctx); + } + + template + FMT_CONSTEXPR auto format(const Type &val, FormatContext &ctx) const -> decltype(ctx.out()) { + val.str(m_base, ctx); + return ctx.out(); + } +}; +#endif // FMT_API + +#endif // LIBRAPID_ARRAY_SIZETYPE_HPP \ No newline at end of file diff --git a/librapid/include/librapid/array/sizetype.hpp b/librapid/include/librapid/array/sizetype.hpp deleted file mode 100644 index e78e3a63..00000000 --- a/librapid/include/librapid/array/sizetype.hpp +++ /dev/null @@ -1,374 +0,0 @@ -#ifndef LIBRAPID_ARRAY_SIZETYPE_HPP -#define LIBRAPID_ARRAY_SIZETYPE_HPP - -/* - * This file defines the Shape class and some helper functions, - * including stride operations. - */ - -namespace librapid { - namespace typetraits { - LIBRAPID_DEFINE_AS_TYPE(typename T COMMA size_t N, Shape); - } - - template - class Shape { - public: - using SizeType = T; - static constexpr size_t MaxDimensions = N; - - /// Default constructor - Shape() = default; - - /// Create a shape object from the dimensions of a FixedStorage object. This is used - // mainly internally, but may serve some purpose I haven't yet thought of. - /// \tparam Scalar Scalar type of the FixedStorage object - /// \tparam Dimensions Dimensions of the FixedStorage object - /// \param fixed The FixedStorage object - template - explicit Shape(const FixedStorage &fixed); - - /// Create a Shape object from a list of values - /// \tparam V Scalar type of the values - /// \param vals The dimensions for the object - template::value> = 0> - Shape(const std::initializer_list &vals); - - /// Create a Shape object from a vector of values - /// \tparam V Scalar type of the values - /// \param vals The dimensions for the object - template::value> = 0> - explicit Shape(const std::vector &vals); - - /// Create a copy of a Shape object - /// \param other Shape object to copy - Shape(const Shape &other) = default; - - /// Create a Shape from an RValue - /// \param other Temporary Shape object to copy - Shape(Shape &&other) noexcept = default; - - /// Create a Shape object from one with a different type and number of dimensions. - /// \tparam V Scalar type of the values - /// \tparam Dim Number of dimensions - /// \param other Shape object to copy - template - Shape(const Shape &other); - - /// Create a Shape object from one with a different type and number of dimensions, moving it - /// instead of copying it. - /// \tparam V Scalar type of the values - /// \tparam Dim Number of dimensions - /// \param other Temporary Shape object to move - template - Shape(Shape &&other) noexcept; - - /// Assign a Shape object to this object - /// \tparam V Scalar type of the Shape - /// \param vals Dimensions of the Shape - /// \return *this - template::value> = 0> - Shape &operator=(const std::initializer_list &vals); - - /// Assign a Shape object to this object - /// \tparam V Scalar type of the Shape - /// \param vals Dimensions of the Shape - /// \return *this - template::value> = 0> - Shape &operator=(const std::vector &vals); - - /// Assign an RValue Shape to this object - /// \param other RValue to move - /// \return - Shape &operator=(Shape &&other) noexcept = default; - - /// Assign a Shape to this object - /// \param other Shape to copy - /// \return - Shape &operator=(const Shape &other) = default; - - /// Return a Shape object with \p dims dimensions, all initialized to zero. - /// \param dims Number of dimensions - /// \return New Shape object - static Shape zeros(size_t dims); - - /// Return a Shape object with \p dims dimensions, all initialized to one. - /// \param dims Number of dimensions - /// \return New Shape object - static Shape ones(size_t dims); - - /// Access an element of the Shape object - /// \tparam Index Typename of the index - /// \param index Index to access - /// \return The value at the index - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const T &operator[](Index index) const; - - /// Access an element of the Shape object - /// \tparam Index Typename of the index - /// \param index Index to access - /// \return A reference to the value at the index - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T &operator[](Index index); - - /// Compare two Shape objects, returning true if and only if they are identical - /// \param other Shape object to compare - /// \return true if the objects are identical - LIBRAPID_ALWAYS_INLINE bool operator==(const Shape &other) const; - - /// Compare two Shape objects, returning true if and only if they are not identical - /// \param other Shape object to compare - /// \return true if the objects are not identical - LIBRAPID_ALWAYS_INLINE bool operator!=(const Shape &other) const; - - /// Return the number of dimensions in the Shape object - /// \return Number of dimensions - LIBRAPID_NODISCARD T ndim() const; - - /// Return a subshape of the Shape object - /// \param start Starting index - /// \param end Ending index - /// \return Subshape - LIBRAPID_NODISCARD Shape subshape(size_t start, size_t end) const; - - /// Return the number of elements the Shape object represents - /// \return Number of elements - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T size() const; - - template - void str(const fmt::formatter &format, Ctx &ctx) const; - - protected: - T m_dims; - std::array m_data; - }; - - namespace detail { - template - Shape shapeFromFixedStorage(const FixedStorage &) { - return Shape({Dims...}); - } - } // namespace detail - - template - template - Shape::Shape(const FixedStorage &) : m_data({Dimensions...}) {} - - template - template::value>> - Shape::Shape(const std::initializer_list &vals) : m_dims(vals.size()) { - LIBRAPID_ASSERT(vals.size() <= N, - "Shape object is limited to {} dimensions. Cannot initialize with {}", - N, - vals.size()); - for (size_t i = 0; i < vals.size(); ++i) { m_data[i] = *(vals.begin() + i); } - } - - template - template::value>> - Shape::Shape(const std::vector &vals) : m_dims(vals.size()) { - LIBRAPID_ASSERT(vals.size() <= N, - "Shape object is limited to {} dimensions. Cannot initialize with {}", - N, - vals.size()); - for (size_t i = 0; i < vals.size(); ++i) { m_data[i] = vals[i]; } - } - - template - template - Shape::Shape(const Shape &other) : m_dims(other.ndim()) { - LIBRAPID_ASSERT(other.ndim() <= N, - "Shape object is limited to {} dimensions. Cannot initialize with {}", - N, - other.ndim()); - for (size_t i = 0; i < m_dims; ++i) { m_data[i] = other[i]; } - } - - template - template - Shape::Shape(Shape &&other) noexcept : m_dims(other.ndim()) { - LIBRAPID_ASSERT(other.ndim() <= N, - "Shape object is limited to {} dimensions. Cannot initialize with {}", - N, - other.ndim()); - for (size_t i = 0; i < m_dims; ++i) { m_data[i] = other[i]; } - } - - template - template::value>> - Shape &Shape::operator=(const std::initializer_list &vals) { - LIBRAPID_ASSERT(vals.size() <= N, - "Shape object is limited to {} dimensions. Cannot initialize with {}", - N, - vals.size()); - m_dims = vals.size(); - for (int64_t i = 0; i < vals.size(); ++i) { m_data[i] = *(vals.begin() + i); } - return *this; - } - - template - template::value>> - Shape &Shape::operator=(const std::vector &vals) { - LIBRAPID_ASSERT(vals.size() <= N, - "Shape object is limited to {} dimensions. Cannot initialize with {}", - N, - vals.size()); - m_dims = vals.size(); - for (int64_t i = 0; i < vals.size(); ++i) { m_data[i] = vals[i]; } - return *this; - } - - template - Shape Shape::zeros(size_t dims) { - Shape res; - res.m_dims = dims; - for (size_t i = 0; i < dims; ++i) res.m_data[i] = 0; - return res; - } - - template - Shape Shape::ones(size_t dims) { - Shape res; - res.m_dims = dims; - for (size_t i = 0; i < dims; ++i) res.m_data[i] = 1; - return res; - } - - template - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const T &Shape::operator[](Index index) const { - LIBRAPID_ASSERT(static_cast(index) < m_dims, "Index out of bounds"); - LIBRAPID_ASSERT(index >= 0, "Index out of bounds"); - return m_data[index]; - } - - template - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T &Shape::operator[](Index index) { - LIBRAPID_ASSERT(static_cast(index) < m_dims, "Index out of bounds"); - LIBRAPID_ASSERT(index >= 0, "Index out of bounds"); - return m_data[index]; - } - - template - LIBRAPID_ALWAYS_INLINE bool Shape::operator==(const Shape &other) const { - if (m_dims != other.m_dims) return false; - for (size_t i = 0; i < m_dims; ++i) { - if (m_data[i] != other.m_data[i]) return false; - } - return true; - } - - template - LIBRAPID_ALWAYS_INLINE bool Shape::operator!=(const Shape &other) const { - return !(*this == other); - } - - template - LIBRAPID_NODISCARD T Shape::ndim() const { - return m_dims; - } - - template - LIBRAPID_NODISCARD auto Shape::subshape(size_t start, size_t end) const -> Shape { - LIBRAPID_ASSERT(start <= end, "Start index must be less than end index"); - LIBRAPID_ASSERT(end <= m_dims, - "End index must be less than or equal to the number of dimensions"); - LIBRAPID_ASSERT(start >= 0, "Start index must be greater than or equal to 0"); - - Shape res; - res.m_dims = end - start; - for (size_t i = 0; i < res.m_dims; ++i) res.m_data[i] = m_data[i + start]; - return res; - } - - template - LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T Shape::size() const { - T res = 1; - for (size_t i = 0; i < m_dims; ++i) res *= m_data[i]; - return res; - } - - template - template - void Shape::str(const fmt::formatter &format, Ctx &ctx) const { - fmt::format_to(ctx.out(), "Shape("); - for (size_t i = 0; i < m_dims; ++i) { - format.format(m_data[i], ctx); - if (i != m_dims - 1) fmt::format_to(ctx.out(), ", "); - } - fmt::format_to(ctx.out(), ")"); - } - - /// Returns true if all inputs have the same shape - /// \tparam T1 Type of the first input - /// \tparam N1 Number of dimensions of the first input - /// \tparam T2 Type of the second input - /// \tparam N2 Number of dimensions of the second input - /// \tparam Tn Type of the remaining (optional) inputs - /// \tparam Nn Number of dimensions of the remaining (optional) inputs - /// \param first First input - /// \param second Second input - /// \param shapes Remaining (optional) inputs - /// \return True if all inputs have the same shape, false otherwise - template - LIBRAPID_NODISCARD LIBRAPID_INLINE bool shapesMatch(const Shape &first, - const Shape &second, - const Shape &...shapes) { - if constexpr (sizeof...(Tn) == 0) { - return first == second; - } else { - return first == second && shapesMatch(first, shapes...); - } - } - - /// \sa shapesMatch - template - LIBRAPID_NODISCARD LIBRAPID_INLINE bool - shapesMatch(const std::tuple, Shape, Shape...> &shapes) { - if constexpr (sizeof...(Tn) == 0) { - return std::get<0>(shapes) == std::get<1>(shapes); - } else { - return std::get<0>(shapes) == std::get<1>(shapes) && - shapesMatch(std::apply( - [](auto, auto, auto... rest) { return std::make_tuple(rest...); }, shapes)); - } - } - - namespace typetraits { - template - struct IsSizeType { - using value = std::false_type; - }; - - template - struct IsSizeType> { - using value = std::true_type; - }; - } // namespace typetraits -} // namespace librapid - -// Support FMT printing -#ifdef FMT_API -template -struct fmt::formatter> { -private: - using Base = fmt::formatter; - Base m_base; - -public: - template - FMT_CONSTEXPR auto parse(ParseContext &ctx) -> const char * { - return m_base.parse(ctx); - } - - template - FMT_CONSTEXPR auto format(const librapid::Shape &val, FormatContext &ctx) const - -> decltype(ctx.out()) { - val.str(m_base, ctx); - return ctx.out(); - } -}; -#endif // FMT_API - -#endif // LIBRAPID_ARRAY_SIZETYPE_HPP \ No newline at end of file diff --git a/librapid/include/librapid/array/strideTools.hpp b/librapid/include/librapid/array/strideTools.hpp index 283e67ed..6d4af178 100644 --- a/librapid/include/librapid/array/strideTools.hpp +++ b/librapid/include/librapid/array/strideTools.hpp @@ -2,64 +2,64 @@ #define LIBRAPID_ARRAY_STRIDE_TOOLS_HPP namespace librapid { - namespace typetraits { - LIBRAPID_DEFINE_AS_TYPE(typename T COMMA size_t N, Stride); - } + namespace typetraits { + LIBRAPID_DEFINE_AS_TYPE(size_t N, Stride); + } - /// A Stride is a vector of integers that describes the distance between elements in each - /// dimension of an ArrayContainer object. This can be used to access elements in a non-trivial - /// order, or to access a sub-array of an ArrayContainer object. The Stride class inherits from - /// the Shape class. - /// \tparam T The type of the Stride. Must be an integer type. - /// \tparam N The number of dimensions in the Stride. - /// \see Shape - template - class Stride : public Shape { - public: - /// Default Constructor - Stride() = default; + /// A Stride is a vector of integers that describes the distance between elements in each + /// dimension of an ArrayContainer object. This can be used to access elements in a non-trivial + /// order, or to access a sub-array of an ArrayContainer object. The Stride class inherits from + /// the Shape class. + /// \tparam T The type of the Stride. Must be an integer type. + /// \tparam N The number of dimensions in the Stride. + /// \see Shape + template + class Stride : public Shape { + public: + /// Default Constructor + Stride() = default; - /// Construct a Stride from a Shape object. This will assume that the data represented by - /// the Shape object is a contiguous block of memory, and will calculate the corresponding - /// strides based on this. - /// \param shape - Stride(const Shape &shape); + /// Construct a Stride from a Shape object. This will assume that the data represented by + /// the Shape object is a contiguous block of memory, and will calculate the corresponding + /// strides based on this. + /// \param shape + Stride(const Shape &shape); - /// Copy a Stride object - /// \param other The Stride object to copy. - Stride(const Stride &other) = default; + /// Copy a Stride object + /// \param other The Stride object to copy. + Stride(const Stride &other) = default; - /// Move a Stride object - /// \param other The Stride object to move. - Stride(Stride &&other) noexcept = default; + /// Move a Stride object + /// \param other The Stride object to move. + Stride(Stride &&other) noexcept = default; - /// Assign a Stride object to this Stride object. - /// \param other The Stride object to assign. - Stride &operator=(const Stride &other) = default; + /// Assign a Stride object to this Stride object. + /// \param other The Stride object to assign. + Stride &operator=(const Stride &other) = default; - /// Move a Stride object to this Stride object. - /// \param other The Stride object to move. - Stride &operator=(Stride &&other) noexcept = default; - }; + /// Move a Stride object to this Stride object. + /// \param other The Stride object to move. + Stride &operator=(Stride &&other) noexcept = default; + }; - template - Stride::Stride(const Shape &shape) : Shape(shape) { - if (this->m_dims == 0) { - // Edge case for a zero-dimensional array - this->m_data[0] = 1; - return; - } + template + Stride::Stride(const Shape &shape) : Shape(shape) { + if (this->m_dims == 0) { + // Edge case for a zero-dimensional array + this->m_data[0] = 1; + return; + } - T tmp[N] {0}; - tmp[this->m_dims - 1] = 1; - for (size_t i = this->m_dims - 1; i > 0; --i) tmp[i - 1] = tmp[i] * this->m_data[i]; - for (size_t i = 0; i < this->m_dims; ++i) this->m_data[i] = tmp[i]; - } + typename Shape::SizeType tmp[N] {0}; + tmp[this->m_dims - 1] = 1; + for (size_t i = this->m_dims - 1; i > 0; --i) tmp[i - 1] = tmp[i] * this->m_data[i]; + for (size_t i = 0; i < this->m_dims; ++i) this->m_data[i] = tmp[i]; + } } // namespace librapid // Support FMT printing #ifdef FMT_API -LIBRAPID_SIMPLE_IO_IMPL(typename T COMMA size_t N, librapid::Stride) +LIBRAPID_SIMPLE_IO_IMPL(size_t N, librapid::Stride) #endif // FMT_API #endif // LIBRAPID_ARRAY_STRIDE_TOOLS_HPP \ No newline at end of file diff --git a/librapid/include/librapid/core/forward.hpp b/librapid/include/librapid/core/forward.hpp index 5f945ce4..f3c450dc 100644 --- a/librapid/include/librapid/core/forward.hpp +++ b/librapid/include/librapid/core/forward.hpp @@ -4,10 +4,10 @@ #ifndef LIBRAPID_DOXYGEN namespace librapid { - template + template class Shape; - template + template class Stride; template