/* enoki/array_base.h -- Base class of all Enoki arrays Enoki is a C++ template library that enables transparent vectorization of numerical kernels using SIMD instruction sets available on current processor architectures. Copyright (c) 2019 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #include #include #include NAMESPACE_BEGIN(enoki) template struct ArrayBase { // ----------------------------------------------------------------------- //! @{ \name Curiously Recurring Template design pattern // ----------------------------------------------------------------------- /// Alias to the derived type using Derived = Derived_; /// Cast to derived type ENOKI_INLINE Derived &derived() { return (Derived &) *this; } /// Cast to derived type (const version) ENOKI_INLINE const Derived &derived() const { return (Derived &) *this; } //! @} // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- //! @{ \name Basic declarations // ----------------------------------------------------------------------- /// Actual type underlying the derived array using Value = Value_; /// Scalar data type all the way at the lowest level using Scalar = scalar_t; /// Specifies how deeply nested this array is static constexpr size_t Depth = 1 + array_depth_v; /// Is this a mask type? static constexpr bool IsMask = is_mask_v; /// Is this a dynamically allocated array (no by default) static constexpr bool IsDynamic = is_dynamic_v; /// Does this array compute derivatives using automatic differentation? static constexpr bool IsDiff = is_diff_array_v; /// Does this array reside on the GPU? (via CUDA) static constexpr bool IsCUDA = is_cuda_array_v; /// Does this array map operations onto native vector instructions? static constexpr bool IsNative = false; /// Is this an AVX512-style 'k' mask register? static constexpr bool IsKMask = false; /// Is the storage representation of this array implemented recursively? static constexpr bool IsRecursive = false; /// Always prefer broadcasting to the outer dimensions of a N-D array static constexpr bool BroadcastPreferOuter = true; /// Does this array represent a fixed size vector? static constexpr bool IsVector = false; /// Does this array represent a complex number? static constexpr bool IsComplex = false; /// Does this array represent a quaternion? static constexpr bool IsQuaternion = false; /// Does this array represent a matrix? static constexpr bool IsMatrix = false; /// Does this array represent the result of a 'masked(...)' epxpression? static constexpr bool IsMaskedArray = false; //! @} // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- //! @{ \name Iterators // ----------------------------------------------------------------------- ENOKI_INLINE auto begin() const { return derived().data(); } ENOKI_INLINE auto begin() { return derived().data(); } ENOKI_INLINE auto end() const { return derived().data() + derived().size(); } ENOKI_INLINE auto end() { return derived().data() + derived().size(); } //! @} // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- //! @{ \name Element access // ----------------------------------------------------------------------- /// Array indexing operator with bounds checks in debug mode ENOKI_INLINE decltype(auto) operator[](size_t i) { #if !defined(NDEBUG) && !defined(ENOKI_DISABLE_RANGE_CHECK) if (i >= derived().size()) throw std::out_of_range( "ArrayBase: out of range access (tried to access index " + std::to_string(i) + " in an array of size " + std::to_string(derived().size()) + ")"); #endif return derived().coeff(i); } /// Array indexing operator with bounds checks in debug mode, const version ENOKI_INLINE decltype(auto) operator[](size_t i) const { #if !defined(NDEBUG) && !defined(ENOKI_DISABLE_RANGE_CHECK) if (i >= derived().size()) throw std::out_of_range( "ArrayBase: out of range access (tried to access index " + std::to_string(i) + " in an array of size " + std::to_string(derived().size()) + ")"); #endif return derived().coeff(i); } template = 0> ENOKI_INLINE auto operator[](const Mask &m) { return detail::MaskedArray{ derived(), (const mask_t &) m }; } //! @} // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- //! @{ \name Fallback implementations for masked operations // ----------------------------------------------------------------------- #define ENOKI_MASKED_OPERATOR_FALLBACK(name, expr) \ template \ ENOKI_INLINE void m##name##_(const T &e, const Mask &m) { \ derived() = select(m, expr, derived()); \ } ENOKI_MASKED_OPERATOR_FALLBACK(assign, e) ENOKI_MASKED_OPERATOR_FALLBACK(add, derived() + e) ENOKI_MASKED_OPERATOR_FALLBACK(sub, derived() - e) ENOKI_MASKED_OPERATOR_FALLBACK(mul, derived() * e) ENOKI_MASKED_OPERATOR_FALLBACK(div, derived() / e) ENOKI_MASKED_OPERATOR_FALLBACK(or, derived() | e) ENOKI_MASKED_OPERATOR_FALLBACK(and, derived() & e) ENOKI_MASKED_OPERATOR_FALLBACK(xor, derived() ^ e) #undef ENOKI_MASKED_OPERATOR_FALLBACK //! @} // ----------------------------------------------------------------------- /// Dot product fallback implementation ENOKI_INLINE auto dot_(const Derived &a) const { return hsum(derived() * a); } /// Horizontal mean fallback implementation ENOKI_INLINE auto hmean_() const { return hsum(derived()) * (1.f / derived().size()); } template ENOKI_INLINE void scatter_add_(void *mem, const Index &index, const Mask &mask) const { transform( mem, index, [](auto &a, auto &b, auto &) { a += b; }, derived(), mask); } }; namespace detail { template ENOKI_INLINE bool convert_mask(T value) { if constexpr (std::is_same_v) return value; else return memcpy_cast::UInt>(value) != 0; } template void print(Stream &os, const Array &a, bool abbrev, const std::array &size, Indices... indices) { ENOKI_MARK_USED(size); ENOKI_MARK_USED(abbrev); if constexpr (sizeof...(Indices) == N) { os << a.derived().coeff(indices...); } else { constexpr size_t k = N - sizeof...(Indices) - 1; os << "["; for (size_t i = 0; i < size[k]; ++i) { if constexpr (is_dynamic_array_v) { if (size[k] > 20 && i == 5 && abbrev) { if (k > 0) { os << ".. " << size[k] - 10 << " skipped ..,\n"; for (size_t j = 0; j <= sizeof...(Indices); ++j) os << " "; } else { os << ".. " << size[k] - 10 << " skipped .., "; } i = size[k] - 6; continue; } } print(os, a, abbrev, size, i, indices...); if (i + 1 < size[k]) { if constexpr (k == 0) { os << ", "; } else { os << ",\n"; for (size_t j = 0; j <= sizeof...(Indices); ++j) os << " "; } } } os << "]"; } } } template ENOKI_NOINLINE std::ostream &operator<<(std::ostream &os, const ArrayBase &a) { if (ragged(a)) os << "[ragged array]"; else detail::print(os, a, true, shape(a)); return os; } NAMESPACE_END(enoki)