#ifndef TINY_VECTOR_HPP #define TINY_VECTOR_HPP #include <utils/InvalidData.hpp> #include <utils/NaNHelper.hpp> #include <utils/PugsAssert.hpp> #include <utils/PugsMacros.hpp> #include <utils/Types.hpp> #include <cmath> #include <iostream> template <size_t N, typename T = double> class [[nodiscard]] TinyVector { public: inline static constexpr size_t Dimension = N; using data_type = T; private: T m_values[N]; static_assert((N > 0), "TinyVector size must be strictly positive"); template <typename... Args> PUGS_FORCEINLINE constexpr void _unpackVariadicInput(const T& t, Args&&... args) noexcept { m_values[N - 1 - sizeof...(args)] = t; if constexpr (sizeof...(args) > 0) { this->_unpackVariadicInput(std::forward<Args>(args)...); } } public: PUGS_INLINE constexpr TinyVector operator-() const { TinyVector opposite; for (size_t i = 0; i < N; ++i) { opposite.m_values[i] = -m_values[i]; } return opposite; } PUGS_INLINE constexpr size_t dimension() const { return N; } PUGS_INLINE constexpr bool operator==(const TinyVector& v) const { for (size_t i = 0; i < N; ++i) { if (m_values[i] != v.m_values[i]) return false; } return true; } PUGS_INLINE constexpr bool operator!=(const TinyVector& v) const { return not this->operator==(v); } PUGS_INLINE constexpr friend T dot(const TinyVector& u, const TinyVector& v) { T t = u.m_values[0] * v.m_values[0]; for (size_t i = 1; i < N; ++i) { t += u.m_values[i] * v.m_values[i]; } return t; } PUGS_INLINE constexpr TinyVector& operator*=(const T& t) { for (size_t i = 0; i < N; ++i) { m_values[i] *= t; } return *this; } PUGS_INLINE constexpr friend TinyVector operator*(const T& t, const TinyVector& v) { TinyVector w = v; return w *= t; } PUGS_INLINE constexpr friend TinyVector operator*(const T& t, TinyVector&& v) { v *= t; return std::move(v); } PUGS_INLINE constexpr friend std::ostream& operator<<(std::ostream& os, const TinyVector& v) { os << '(' << NaNHelper(v.m_values[0]); for (size_t i = 1; i < N; ++i) { os << ',' << NaNHelper(v.m_values[i]); } os << ')'; return os; } PUGS_INLINE constexpr TinyVector operator+(const TinyVector& v) const { TinyVector sum; for (size_t i = 0; i < N; ++i) { sum.m_values[i] = m_values[i] + v.m_values[i]; } return sum; } PUGS_INLINE constexpr TinyVector operator+(TinyVector&& v) const { for (size_t i = 0; i < N; ++i) { v.m_values[i] += m_values[i]; } return std::move(v); } PUGS_INLINE constexpr TinyVector operator-(const TinyVector& v) const { TinyVector difference; for (size_t i = 0; i < N; ++i) { difference.m_values[i] = m_values[i] - v.m_values[i]; } return difference; } PUGS_INLINE constexpr TinyVector operator-(TinyVector&& v) const { for (size_t i = 0; i < N; ++i) { v.m_values[i] = m_values[i] - v.m_values[i]; } return std::move(v); } PUGS_INLINE constexpr TinyVector& operator+=(const TinyVector& v) { for (size_t i = 0; i < N; ++i) { m_values[i] += v.m_values[i]; } return *this; } PUGS_INLINE constexpr void operator+=(const volatile TinyVector& v) volatile { for (size_t i = 0; i < N; ++i) { m_values[i] += v.m_values[i]; } } PUGS_INLINE constexpr TinyVector& operator-=(const TinyVector& v) { for (size_t i = 0; i < N; ++i) { m_values[i] -= v.m_values[i]; } return *this; } PUGS_INLINE constexpr T& operator[](size_t i) noexcept(NO_ASSERT) { Assert(i < N); return m_values[i]; } PUGS_INLINE constexpr const T& operator[](size_t i) const noexcept(NO_ASSERT) { Assert(i < N); return m_values[i]; } PUGS_INLINE constexpr TinyVector& operator=(ZeroType) noexcept { static_assert(std::is_arithmetic<T>(), "Cannot assign 'zero' value for non-arithmetic types"); for (size_t i = 0; i < N; ++i) { m_values[i] = 0; } return *this; } PUGS_INLINE TinyVector& operator=(const TinyVector&) noexcept = default; PUGS_INLINE constexpr TinyVector& operator=(TinyVector&& v) noexcept = default; template <typename... Args> PUGS_INLINE constexpr TinyVector(const T& t, Args&&... args) noexcept { static_assert(sizeof...(args) == N - 1, "wrong number of parameters"); this->_unpackVariadicInput(t, std::forward<Args>(args)...); } // One does not use the '=default' constructor to avoid // (zero-initialization) performances issues PUGS_INLINE constexpr TinyVector() noexcept { #ifndef NDEBUG for (size_t i = 0; i < N; ++i) { m_values[i] = invalid_data_v<T>; } #endif // NDEBUG } PUGS_INLINE constexpr TinyVector(ZeroType) noexcept { static_assert(std::is_arithmetic<T>(), "Cannot construct from 'zero' value " "for non-arithmetic types"); for (size_t i = 0; i < N; ++i) { m_values[i] = 0; } } PUGS_INLINE constexpr TinyVector(const TinyVector&) noexcept = default; PUGS_INLINE constexpr TinyVector(TinyVector && v) noexcept = default; PUGS_INLINE ~TinyVector() noexcept = default; }; template <size_t N, typename T> PUGS_INLINE constexpr T l2Norm(const TinyVector<N, T>& x) { static_assert(std::is_arithmetic<T>(), "Cannot compute L2 norm for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "L2 norm is defined for floating point types only"); return std::sqrt(dot(x, x)); } // Cross product is only defined for K^3 vectors template <typename T> PUGS_INLINE constexpr TinyVector<3, T> crossProduct(const TinyVector<3, T>& u, const TinyVector<3, T>& v) { TinyVector<3, T> cross_product(u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]); return cross_product; } #endif // TINYVECTOR_HPP