Program Listing for File genericVector.hpp#

Return to documentation for file (librapid/include/librapid/math/genericVector.hpp)

#ifndef LIBRAPID_MATH_GENERIC_VECTOR_HPP
#define LIBRAPID_MATH_GENERIC_VECTOR_HPP

namespace librapid {
    template<typename Scalar, int64_t Dims = 3>
    class GenericVector {
    public:
        using StorageType = Scalar[Dims];

        GenericVector() = default;

        explicit GenericVector(const StorageType &arr);

        template<typename S, int64_t D>
        GenericVector(const GenericVector<S, D> &other);

        template<typename... Args, std::enable_if_t<sizeof...(Args) == Dims, int> = 0>
        GenericVector(Args... args);

        template<typename... Args, int64_t size = sizeof...(Args),
                 typename std::enable_if_t<size != Dims, int> = 0>
        GenericVector(Args... args);

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        GenericVector(const std::initializer_list<T> &list);

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        GenericVector(const std::vector<T> &list);

        GenericVector(const GenericVector &other) = default;

        GenericVector(GenericVector &&other) noexcept = default;

        GenericVector &operator=(const GenericVector &other) = default;

        GenericVector &operator=(GenericVector &&other) noexcept = default;

        // Implement conversion to and from GLM data types
#ifdef GLM_VERSION

        template<glm::qualifier p = glm::defaultp>
        GenericVector(const glm::vec<Dims, Scalar, p> &vec);

        template<glm::qualifier p = glm::defaultp>
        operator glm::vec<Dims, Scalar, p>() const;

#endif // GLM_VERSION

        LIBRAPID_NODISCARD const Scalar &operator[](int64_t index) const;

        LIBRAPID_NODISCARD Scalar &operator[](int64_t index);

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator+=(const GenericVector<T, d> &other);

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator-=(const GenericVector<T, d> &other);

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator*=(const GenericVector<T, d> &other);

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator/=(const GenericVector<T, d> &other);

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator+=(const T &value);

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator-=(const T &value);

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator*=(const T &value);

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector &operator/=(const T &value);

        LIBRAPID_ALWAYS_INLINE GenericVector operator-() const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector cmp(const GenericVector<T, d> &other,
                                                 const char *mode) const;

        template<typename T>
        LIBRAPID_ALWAYS_INLINE GenericVector cmp(const T &value, const char *mode) const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector operator<(const GenericVector<T, d> &other) const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector operator<=(const GenericVector<T, d> &other) const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector operator>(const GenericVector<T, d> &other) const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector operator>=(const GenericVector<T, d> &other) const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector operator==(const GenericVector<T, d> &other) const;

        template<typename T, int64_t d>
        LIBRAPID_ALWAYS_INLINE GenericVector operator!=(const GenericVector<T, d> &other) const;

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector operator<(const T &other) const;

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector operator<=(const T &other) const;

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector operator>(const T &other) const;

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector operator>=(const T &other) const;

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector operator==(const T &other) const;

        template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int> = 0>
        LIBRAPID_ALWAYS_INLINE GenericVector operator!=(const T &other) const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar mag2() const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar mag() const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar invMag() const { return 1.0 / mag(); }

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector norm() const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar dot(const GenericVector &other) const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector
        cross(const GenericVector &other) const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector
        proj(const GenericVector &other) const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE explicit operator bool() const;

        LIBRAPID_ALWAYS_INLINE Scalar x() const;

        LIBRAPID_ALWAYS_INLINE Scalar y() const;

        LIBRAPID_ALWAYS_INLINE Scalar z() const;

        LIBRAPID_ALWAYS_INLINE Scalar w() const;

        template<size_t... Indices>
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, sizeof...(Indices)>
        swizzle() const;

        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 2> xy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 2> yx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 2> xz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 2> zx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 2> yz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 2> zy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> xyz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> xzy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> yxz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> yzx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> zxy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> zyx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> xyw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> xwy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> yxw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> ywx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> wxy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> wyx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> xzw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> xwz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> zxw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> zwx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> wxz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> wzx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> yzw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> ywz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> zyw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> zwy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> wyz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 3> wzy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> xyzw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> xywz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> xzyw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> xzwy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> xwyz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> xwzy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> yxzw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> yxwz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> yzxw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> yzwx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> ywxz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> ywzx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> zxyw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> zxwy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> zyxw() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> zywx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> zwxy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> zwyx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> wxyz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> wxzy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> wyxz() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> wyzx() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> wzxy() const;
        LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, 4> wzyx() const;

        LIBRAPID_ALWAYS_INLINE void x(Scalar val);

        LIBRAPID_ALWAYS_INLINE void y(Scalar val);

        LIBRAPID_ALWAYS_INLINE void z(Scalar val);

        LIBRAPID_ALWAYS_INLINE void w(Scalar val);

        LIBRAPID_ALWAYS_INLINE void xy(const GenericVector<Scalar, 2> &v);
        LIBRAPID_ALWAYS_INLINE void yx(const GenericVector<Scalar, 2> &v);
        LIBRAPID_ALWAYS_INLINE void xz(const GenericVector<Scalar, 2> &v);
        LIBRAPID_ALWAYS_INLINE void zx(const GenericVector<Scalar, 2> &v);
        LIBRAPID_ALWAYS_INLINE void yz(const GenericVector<Scalar, 2> &v);
        LIBRAPID_ALWAYS_INLINE void zy(const GenericVector<Scalar, 2> &v);
        LIBRAPID_ALWAYS_INLINE void xyz(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void xzy(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void yxz(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void yzx(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void zxy(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void zyx(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void xyw(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void xwy(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void yxw(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void ywx(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void wxy(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void wyx(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void xzw(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void xwz(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void zxw(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void zwx(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void wxz(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void wzx(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void yzw(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void ywz(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void zyw(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void zwy(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void wyz(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void wzy(const GenericVector<Scalar, 3> &v);
        LIBRAPID_ALWAYS_INLINE void xyzw(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void xywz(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void xzyw(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void xzwy(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void xwyz(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void xwzy(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void yxzw(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void yxwz(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void yzxw(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void yzwx(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void ywxz(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void ywzx(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void zxyw(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void zxwy(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void zyxw(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void zywx(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void zwxy(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void zwyx(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void wxyz(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void wxzy(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void wyxz(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void wyzx(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void wzxy(const GenericVector<Scalar, 4> &v);
        LIBRAPID_ALWAYS_INLINE void wzyx(const GenericVector<Scalar, 4> &v);

        LIBRAPID_ALWAYS_INLINE const StorageType &data() const;

        LIBRAPID_ALWAYS_INLINE StorageType &data();

        LIBRAPID_NODISCARD std::string str(const std::string &formatString = "{}") const;

    protected:
        StorageType m_data {};
    };

    template<typename Scalar, int64_t Dims>
    GenericVector<Scalar, Dims>::GenericVector(const StorageType &arr) : m_data {arr} {}

    template<typename Scalar, int64_t Dims>
    template<typename S, int64_t D>
    GenericVector<Scalar, Dims>::GenericVector(const GenericVector<S, D> &other) {
        for (int64_t i = 0; i < min(Dims, D); ++i) { m_data[i] = static_cast<Scalar>(other[i]); }
    }

    template<typename Scalar, int64_t Dims>
    template<typename... Args, std::enable_if_t<sizeof...(Args) == Dims, int>>
    GenericVector<Scalar, Dims>::GenericVector(Args... args) :
            m_data {static_cast<Scalar>(args)...} {
        static_assert(sizeof...(Args) <= Dims, "Invalid number of arguments");
    }

    template<typename Scalar, int64_t Dims>
    template<typename... Args, int64_t size, std::enable_if_t<size != Dims, int>>
    GenericVector<Scalar, Dims>::GenericVector(Args... args) {
        static_assert(sizeof...(Args) <= Dims, "Invalid number of arguments");
        const Scalar expanded[] = {static_cast<Scalar>(args)...};
        for (int64_t i = 0; i < size; i++) { m_data[i] = expanded[i]; }
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    GenericVector<Scalar, Dims>::GenericVector(const std::initializer_list<T> &list) {
        LIBRAPID_ASSERT(list.size() <= Dims,
                        "Cannot initialize {}-dimensional vector with {} elements",
                        Dims,
                        list.size());
        int64_t i = 0;
        for (const Scalar &val : list) { m_data[i++] = val; }
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    GenericVector<Scalar, Dims>::GenericVector(const std::vector<T> &list) {
        LIBRAPID_ASSERT(list.size() <= Dims,
                        "Cannot initialize {} dimensional vector with {} elements",
                        Dims,
                        list.size());
        int64_t i = 0;
        for (const Scalar &val : list) { m_data[i++] = val; }
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator&(const GenericVector<Scalar, Dims> &vec, const GenericVector<Scalar, Dims> &mask) {
        GenericVector<Scalar, Dims> res(vec);
        for (size_t i = 0; i < Dims; ++i)
            if (!mask[i]) res[i] = 0;
        return res;
    }

#ifdef GLM_VERSION

    template<typename Scalar, int64_t Dims>
    template<glm::qualifier p>
    GenericVector<Scalar, Dims>::GenericVector(const glm::vec<Dims, Scalar, p> &vec) {
        for (size_t i = 0; i < Dims; ++i) { m_data[i] = vec[i]; }
    }

    template<typename Scalar, int64_t Dims>
    template<glm::qualifier p>
    GenericVector<Scalar, Dims>::operator glm::vec<Dims, Scalar, p>() const {
        glm::vec<Dims, Scalar, p> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = m_data[i]; }
        return res;
    }

#endif // GLM_VERSION

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::operator[](int64_t index) const -> const Scalar & {
        LIBRAPID_ASSERT(0 <= index && index < Dims,
                        "Index {} out of range for vector with {} dimensions",
                        index,
                        Dims);
        return m_data[index];
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::operator[](int64_t index) -> Scalar & {
        LIBRAPID_ASSERT(0 <= index && index < Dims,
                        "Index {} out of range for vector with {} dimensions",
                        index,
                        Dims);
        return m_data[index];
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator+=(const GenericVector<T, d> &other)
      -> GenericVector & {
        static_assert(d <= Dims, "Invalid number of dimensions");
        for (int64_t i = 0; i < d; ++i) { m_data[i] += other[i]; }
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator-=(const GenericVector<T, d> &other)
      -> GenericVector & {
        static_assert(d <= Dims, "Invalid number of dimensions");
        for (int64_t i = 0; i < d; ++i) { m_data[i] -= other[i]; }
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator*=(const GenericVector<T, d> &other)
      -> GenericVector & {
        static_assert(d <= Dims, "Invalid number of dimensions");
        for (int64_t i = 0; i < d; ++i) { m_data[i] *= other[i]; }
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator/=(const GenericVector<T, d> &other)
      -> GenericVector & {
        static_assert(d <= Dims, "Invalid number of dimensions");
        for (int64_t i = 0; i < d; ++i) { m_data[i] /= other[i]; }
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator+=(const T &value) -> GenericVector & {
        for (int64_t i = 0; i < Dims; ++i) m_data[i] += value;
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator-=(const T &value) -> GenericVector & {
        for (int64_t i = 0; i < Dims; ++i) m_data[i] -= value;
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator*=(const T &value) -> GenericVector & {
        for (int64_t i = 0; i < Dims; ++i) m_data[i] *= value;
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator/=(const T &value) -> GenericVector & {
        for (int64_t i = 0; i < Dims; ++i) m_data[i] /= value;
        return *this;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::operator-() const -> GenericVector {
        GenericVector res(*this);
        res *= -1;
        return res;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::cmp(const GenericVector<T, d> &other, const char *mode) const
      -> GenericVector {
        GenericVector res(*this);
        int16_t modeInt = *(int16_t *)mode;
        for (size_t i = 0; i < Dims; ++i) {
            switch (modeInt) {
                case 'e' | ('q' << 8):
                    if (res[i] == other[i])
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'n' | ('e' << 8):
                    if (res[i] != other[i])
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'l' | ('t' << 8):
                    if (res[i] < other[i])
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'l' | ('e' << 8):
                    if (res[i] <= other[i])
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'g' | ('t' << 8):
                    if (res[i] > other[i])
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'g' | ('e' << 8):
                    if (res[i] >= other[i])
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                default: LIBRAPID_ASSERT(false, "Invalid mode {}", mode);
            }
        }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T>
    auto GenericVector<Scalar, Dims>::cmp(const T &value, const char *mode) const -> GenericVector {
        // Mode:
        // 0: ==    1: !=
        // 2: <     3: <=
        // 4: >     5: >=

        GenericVector res(*this);
        int16_t modeInt = *(int16_t *)mode;
        for (size_t i = 0; i < Dims; ++i) {
            switch (modeInt) {
                case 'e' | ('q' << 8):
                    if (res[i] == Scalar(value))
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'n' | ('e' << 8):
                    if (res[i] != Scalar(value))
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'l' | ('t' << 8):
                    if (res[i] < Scalar(value))
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'l' | ('e' << 8):
                    if (res[i] <= Scalar(value))
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'g' | ('t' << 8):
                    if (res[i] > Scalar(value))
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                case 'g' | ('e' << 8):
                    if (res[i] >= Scalar(value))
                        res[i] = Scalar(1);
                    else
                        res[i] = Scalar(0);
                    break;
                default: LIBRAPID_ASSERT(false, "Invalid mode {}", mode);
            }
        }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator<(const GenericVector<T, d> &other) const
      -> GenericVector {
        return cmp(other, "lt");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator<=(const GenericVector<T, d> &other) const
      -> GenericVector {
        return cmp(other, "le");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator>(const GenericVector<T, d> &other) const
      -> GenericVector {
        return cmp(other, "gt");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator>=(const GenericVector<T, d> &other) const
      -> GenericVector {
        return cmp(other, "ge");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator==(const GenericVector<T, d> &other) const
      -> GenericVector {
        return cmp(other, "eq");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, int64_t d>
    auto GenericVector<Scalar, Dims>::operator!=(const GenericVector<T, d> &other) const
      -> GenericVector {
        return cmp(other, "ne");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator<(const T &other) const -> GenericVector {
        return cmp(other, "lt");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator<=(const T &other) const -> GenericVector {
        return cmp(other, "le");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator>(const T &other) const -> GenericVector {
        return cmp(other, "gt");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator>=(const T &other) const -> GenericVector {
        return cmp(other, "ge");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator==(const T &other) const -> GenericVector {
        return cmp(other, "eq");
    }

    template<typename Scalar, int64_t Dims>
    template<typename T, std::enable_if_t<std::is_convertible_v<T, Scalar>, int>>
    auto GenericVector<Scalar, Dims>::operator!=(const T &other) const -> GenericVector {
        return cmp(other, "ne");
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::mag2() const -> Scalar {
        Scalar res = 0;
        for (int64_t i = 0; i < Dims; ++i) { res += m_data[i] * m_data[i]; }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::mag() const -> Scalar {
        return sqrt(mag2());
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::norm() const -> GenericVector {
        GenericVector res(*this);
        res /= mag();
        return res;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::dot(const GenericVector &other) const -> Scalar {
        Scalar res = 0;
        for (int64_t i = 0; i < Dims; ++i) { res += m_data[i] * other.m_data[i]; }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::cross(const GenericVector &other) const -> GenericVector {
        static_assert(Dims == 3, "Cross product is only defined for 3D Vectors");
        return GenericVector(y() * other.z() - z() * other.y(),
                             z() * other.x() - x() * other.z(),
                             x() * other.y() - y() * other.x());
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::proj(const GenericVector &other) const -> GenericVector {
        return other * (other.dot(*this) / other.mag2());
    }

    template<typename Scalar, int64_t Dims>
    GenericVector<Scalar, Dims>::operator bool() const {
        for (size_t i = 0; i < Dims; ++i)
            if (m_data[i] != 0) return true;
        return false;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator+(const GenericVector<Scalar, Dims> &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res += rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator-(const GenericVector<Scalar, Dims> &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res -= rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator*(const GenericVector<Scalar, Dims> &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res *= rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator/(const GenericVector<Scalar, Dims> &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res /= rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator+(const GenericVector<Scalar, Dims> &lhs, const S &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res += rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator-(const GenericVector<Scalar, Dims> &lhs, const S &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res -= rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator*(const GenericVector<Scalar, Dims> &lhs, const S &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res *= rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator/(const GenericVector<Scalar, Dims> &lhs, const S &rhs) {
        GenericVector<Scalar, Dims> res(lhs);
        res /= rhs;
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator+(const S &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res;
        for (int64_t i = 0; i < Dims; ++i) res[i] = lhs + rhs[i];
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator-(const S &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res;
        for (int64_t i = 0; i < Dims; ++i) res[i] = lhs - rhs[i];
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator*(const S &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res;
        for (int64_t i = 0; i < Dims; ++i) res[i] = lhs * rhs[i];
        return res;
    }

    template<typename Scalar, int64_t Dims, typename S>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    operator/(const S &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res;
        for (int64_t i = 0; i < Dims; ++i) res[i] = lhs / rhs[i];
        return res;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::x() const -> Scalar {
        if constexpr (Dims < 1)
            return 0;
        else
            return m_data[0];
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::y() const -> Scalar {
        if constexpr (Dims < 2)
            return 0;
        else
            return m_data[1];
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::z() const -> Scalar {
        if constexpr (Dims < 3)
            return 0;
        else
            return m_data[2];
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::w() const -> Scalar {
        if constexpr (Dims < 4)
            return 0;
        else
            return m_data[3];
    }

    template<typename Scalar, int64_t Dims>
    template<size_t... Indices>
    auto GenericVector<Scalar, Dims>::swizzle() const -> GenericVector<Scalar, sizeof...(Indices)> {
        static_assert(sizeof...(Indices) <= Dims, "Too many indices for swizzle");
        return {m_data[Indices]...};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xy() const -> GenericVector<Scalar, 2> {
        return {x(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yx() const -> GenericVector<Scalar, 2> {
        return {y(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xz() const -> GenericVector<Scalar, 2> {
        return {x(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zx() const -> GenericVector<Scalar, 2> {
        return {z(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yz() const -> GenericVector<Scalar, 2> {
        return {y(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zy() const -> GenericVector<Scalar, 2> {
        return {z(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xyz() const -> GenericVector<Scalar, 3> {
        return {x(), y(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xzy() const -> GenericVector<Scalar, 3> {
        return {x(), z(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yxz() const -> GenericVector<Scalar, 3> {
        return {y(), x(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yzx() const -> GenericVector<Scalar, 3> {
        return {y(), z(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zxy() const -> GenericVector<Scalar, 3> {
        return {z(), x(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zyx() const -> GenericVector<Scalar, 3> {
        return {z(), y(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xyw() const -> GenericVector<Scalar, 3> {
        return {x(), y(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xwy() const -> GenericVector<Scalar, 3> {
        return {x(), w(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yxw() const -> GenericVector<Scalar, 3> {
        return {y(), x(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::ywx() const -> GenericVector<Scalar, 3> {
        return {y(), w(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wxy() const -> GenericVector<Scalar, 3> {
        return {w(), x(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wyx() const -> GenericVector<Scalar, 3> {
        return {w(), y(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xzw() const -> GenericVector<Scalar, 3> {
        return {x(), z(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xwz() const -> GenericVector<Scalar, 3> {
        return {x(), w(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zxw() const -> GenericVector<Scalar, 3> {
        return {z(), x(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zwx() const -> GenericVector<Scalar, 3> {
        return {z(), w(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wxz() const -> GenericVector<Scalar, 3> {
        return {w(), x(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wzx() const -> GenericVector<Scalar, 3> {
        return {w(), z(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yzw() const -> GenericVector<Scalar, 3> {
        return {y(), z(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::ywz() const -> GenericVector<Scalar, 3> {
        return {y(), w(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zyw() const -> GenericVector<Scalar, 3> {
        return {z(), y(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zwy() const -> GenericVector<Scalar, 3> {
        return {z(), w(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wyz() const -> GenericVector<Scalar, 3> {
        return {w(), y(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wzy() const -> GenericVector<Scalar, 3> {
        return {w(), z(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xyzw() const -> GenericVector<Scalar, 4> {
        return {x(), y(), z(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xywz() const -> GenericVector<Scalar, 4> {
        return {x(), y(), w(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xzyw() const -> GenericVector<Scalar, 4> {
        return {x(), z(), y(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xzwy() const -> GenericVector<Scalar, 4> {
        return {x(), z(), w(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xwyz() const -> GenericVector<Scalar, 4> {
        return {x(), w(), y(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::xwzy() const -> GenericVector<Scalar, 4> {
        return {x(), w(), z(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yxzw() const -> GenericVector<Scalar, 4> {
        return {y(), x(), z(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yxwz() const -> GenericVector<Scalar, 4> {
        return {y(), x(), w(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yzxw() const -> GenericVector<Scalar, 4> {
        return {y(), z(), x(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::yzwx() const -> GenericVector<Scalar, 4> {
        return {y(), z(), w(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::ywxz() const -> GenericVector<Scalar, 4> {
        return {y(), w(), x(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::ywzx() const -> GenericVector<Scalar, 4> {
        return {y(), w(), z(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zxyw() const -> GenericVector<Scalar, 4> {
        return {z(), x(), y(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zxwy() const -> GenericVector<Scalar, 4> {
        return {z(), x(), w(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zyxw() const -> GenericVector<Scalar, 4> {
        return {z(), y(), x(), w()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zywx() const -> GenericVector<Scalar, 4> {
        return {z(), y(), w(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zwxy() const -> GenericVector<Scalar, 4> {
        return {z(), w(), x(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::zwyx() const -> GenericVector<Scalar, 4> {
        return {z(), w(), y(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wxyz() const -> GenericVector<Scalar, 4> {
        return {w(), x(), y(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wxzy() const -> GenericVector<Scalar, 4> {
        return {w(), x(), z(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wyxz() const -> GenericVector<Scalar, 4> {
        return {w(), y(), x(), z()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wyzx() const -> GenericVector<Scalar, 4> {
        return {w(), y(), z(), x()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wzxy() const -> GenericVector<Scalar, 4> {
        return {w(), z(), x(), y()};
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::wzyx() const -> GenericVector<Scalar, 4> {
        return {w(), z(), y(), x()};
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::x(Scalar val) {
        if constexpr (Dims >= 1) m_data[0] = val;
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::y(Scalar val) {
        if constexpr (Dims >= 2) m_data[1] = val;
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::z(Scalar val) {
        if constexpr (Dims >= 3) m_data[2] = val;
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::w(Scalar val) {
        if constexpr (Dims >= 4) m_data[3] = val;
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xy(const GenericVector<Scalar, 2> &v) {
        x(v.x());
        y(v.y());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yx(const GenericVector<Scalar, 2> &v) {
        y(v.x());
        x(v.y());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xz(const GenericVector<Scalar, 2> &v) {
        x(v.x());
        z(v.y());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zx(const GenericVector<Scalar, 2> &v) {
        z(v.x());
        x(v.y());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yz(const GenericVector<Scalar, 2> &v) {
        y(v.x());
        z(v.y());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zy(const GenericVector<Scalar, 2> &v) {
        z(v.x());
        y(v.y());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xyz(const GenericVector<Scalar, 3> &v) {
        x(v.x());
        y(v.y());
        z(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xzy(const GenericVector<Scalar, 3> &v) {
        x(v.x());
        z(v.y());
        y(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yxz(const GenericVector<Scalar, 3> &v) {
        y(v.x());
        x(v.y());
        z(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yzx(const GenericVector<Scalar, 3> &v) {
        y(v.x());
        z(v.y());
        x(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zxy(const GenericVector<Scalar, 3> &v) {
        z(v.x());
        x(v.y());
        y(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zyx(const GenericVector<Scalar, 3> &v) {
        z(v.x());
        y(v.y());
        x(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xyw(const GenericVector<Scalar, 3> &v) {
        x(v.x());
        y(v.y());
        w(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xwy(const GenericVector<Scalar, 3> &v) {
        x(v.x());
        w(v.y());
        y(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yxw(const GenericVector<Scalar, 3> &v) {
        y(v.x());
        x(v.y());
        w(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::ywx(const GenericVector<Scalar, 3> &v) {
        y(v.x());
        w(v.y());
        x(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wxy(const GenericVector<Scalar, 3> &v) {
        w(v.x());
        x(v.y());
        y(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wyx(const GenericVector<Scalar, 3> &v) {
        w(v.x());
        y(v.y());
        x(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xzw(const GenericVector<Scalar, 3> &v) {
        x(v.x());
        z(v.y());
        w(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xwz(const GenericVector<Scalar, 3> &v) {
        x(v.x());
        w(v.y());
        z(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zxw(const GenericVector<Scalar, 3> &v) {
        z(v.x());
        x(v.y());
        w(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zwx(const GenericVector<Scalar, 3> &v) {
        z(v.x());
        w(v.y());
        x(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wxz(const GenericVector<Scalar, 3> &v) {
        w(v.x());
        x(v.y());
        z(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wzx(const GenericVector<Scalar, 3> &v) {
        w(v.x());
        z(v.y());
        x(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yzw(const GenericVector<Scalar, 3> &v) {
        y(v.x());
        z(v.y());
        w(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::ywz(const GenericVector<Scalar, 3> &v) {
        y(v.x());
        w(v.y());
        z(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zyw(const GenericVector<Scalar, 3> &v) {
        z(v.x());
        y(v.y());
        w(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zwy(const GenericVector<Scalar, 3> &v) {
        z(v.x());
        w(v.y());
        y(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wyz(const GenericVector<Scalar, 3> &v) {
        w(v.x());
        y(v.y());
        z(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wzy(const GenericVector<Scalar, 3> &v) {
        w(v.x());
        z(v.y());
        y(v.z());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xyzw(const GenericVector<Scalar, 4> &v) {
        x(v.x());
        y(v.y());
        z(v.z());
        w(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xywz(const GenericVector<Scalar, 4> &v) {
        x(v.x());
        y(v.y());
        w(v.z());
        z(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xzyw(const GenericVector<Scalar, 4> &v) {
        x(v.x());
        z(v.y());
        y(v.z());
        w(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xzwy(const GenericVector<Scalar, 4> &v) {
        x(v.x());
        z(v.y());
        w(v.z());
        y(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xwyz(const GenericVector<Scalar, 4> &v) {
        x(v.x());
        w(v.y());
        y(v.z());
        z(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::xwzy(const GenericVector<Scalar, 4> &v) {
        x(v.x());
        w(v.y());
        z(v.z());
        y(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yxzw(const GenericVector<Scalar, 4> &v) {
        y(v.x());
        x(v.y());
        z(v.z());
        w(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yxwz(const GenericVector<Scalar, 4> &v) {
        y(v.x());
        x(v.y());
        w(v.z());
        z(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yzxw(const GenericVector<Scalar, 4> &v) {
        y(v.x());
        z(v.y());
        x(v.z());
        w(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::yzwx(const GenericVector<Scalar, 4> &v) {
        y(v.x());
        z(v.y());
        w(v.z());
        x(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::ywxz(const GenericVector<Scalar, 4> &v) {
        y(v.x());
        w(v.y());
        x(v.z());
        z(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::ywzx(const GenericVector<Scalar, 4> &v) {
        y(v.x());
        w(v.y());
        z(v.z());
        x(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zxyw(const GenericVector<Scalar, 4> &v) {
        z(v.x());
        x(v.y());
        y(v.z());
        w(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zxwy(const GenericVector<Scalar, 4> &v) {
        z(v.x());
        x(v.y());
        w(v.z());
        y(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zyxw(const GenericVector<Scalar, 4> &v) {
        z(v.x());
        y(v.y());
        x(v.z());
        w(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zywx(const GenericVector<Scalar, 4> &v) {
        z(v.x());
        y(v.y());
        w(v.z());
        x(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zwxy(const GenericVector<Scalar, 4> &v) {
        z(v.x());
        w(v.y());
        x(v.z());
        y(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::zwyx(const GenericVector<Scalar, 4> &v) {
        z(v.x());
        w(v.y());
        y(v.z());
        x(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wxyz(const GenericVector<Scalar, 4> &v) {
        w(v.x());
        x(v.y());
        y(v.z());
        z(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wxzy(const GenericVector<Scalar, 4> &v) {
        w(v.x());
        x(v.y());
        z(v.z());
        y(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wyxz(const GenericVector<Scalar, 4> &v) {
        w(v.x());
        y(v.y());
        x(v.z());
        z(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wyzx(const GenericVector<Scalar, 4> &v) {
        w(v.x());
        y(v.y());
        z(v.z());
        x(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wzxy(const GenericVector<Scalar, 4> &v) {
        w(v.x());
        z(v.y());
        x(v.z());
        y(v.w());
    }

    template<typename Scalar, int64_t Dims>
    void GenericVector<Scalar, Dims>::wzyx(const GenericVector<Scalar, 4> &v) {
        w(v.x());
        z(v.y());
        y(v.z());
        x(v.w());
    }

#pragma endregion SwizzleImpl

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::data() const -> const StorageType & {
        return m_data;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::data() -> StorageType & {
        return m_data;
    }

    template<typename Scalar, int64_t Dims>
    auto GenericVector<Scalar, Dims>::str(const std::string &formatString) const -> std::string {
        std::string res = "(";
        for (size_t i = 0; i < Dims; ++i) {
            res += fmt::format(formatString, m_data[i]);
            if (i < Dims - 1) res += ", ";
        }
        return res + ")";
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE Scalar dist2(const GenericVector<Scalar, Dims> &lhs,
                                        const GenericVector<Scalar, Dims> &rhs) {
        return (lhs - rhs).mag2();
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE Scalar dist(const GenericVector<Scalar, Dims> &lhs,
                                       const GenericVector<Scalar, Dims> &rhs) {
        return (lhs - rhs).mag();
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> sin(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::sin(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> cos(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::cos(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> tan(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::tan(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    asin(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::asin(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    acos(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::acos(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    atan(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::atan(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    atan2(const GenericVector<Scalar, Dims> &lhs, const GenericVector<Scalar, Dims> &rhs) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::atan2(lhs[i], rhs[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    sinh(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::sinh(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    cosh(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::cosh(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    tanh(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::tanh(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    asinh(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::asinh(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    acosh(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::acosh(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    atanh(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::atanh(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> exp(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::exp(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> log(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::log(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    log10(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::log10(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    log2(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::log2(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    sqrt(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::sqrt(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> pow(const GenericVector<Scalar, Dims> &vec,
                                                           const GenericVector<Scalar, Dims> &exp) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::pow(vec[i], exp[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims, typename T>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> pow(const GenericVector<Scalar, Dims> &vec,
                                                           T exp) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) {
            res[i] = static_cast<Scalar>(pow(vec[i], static_cast<Scalar>(exp)));
        }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> pow(Scalar vec,
                                                           const GenericVector<Scalar, Dims> &exp) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::pow(vec, exp[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    cbrt(const GenericVector<Scalar, Dims> &vec) {
        GenericVector<Scalar, Dims> res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::cbrt(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims> abs(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::abs(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    floor(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::floor(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_ALWAYS_INLINE GenericVector<Scalar, Dims>
    ceil(const GenericVector<Scalar, Dims> &vec) {
        using Type = GenericVector<Scalar, Dims>;
        Type res;
        for (size_t i = 0; i < Dims; ++i) { res[i] = ::librapid::ceil(vec[i]); }
        return res;
    }

    template<typename Scalar, int64_t Dims>
    LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE bool isClose(const GenericVector<Scalar, Dims> &a,
                                                           const GenericVector<Scalar, Dims> &b,
                                                           double tolerance = -1) {
        if (tolerance < 0) {
            if constexpr (std::is_same_v<Scalar, double>) {
                tolerance = 1e-12;
            } else if constexpr (std::is_same_v<Scalar, float>) {
                tolerance = 1e-6f;
            } else if constexpr (std::is_floating_point_v<Scalar>) {
                tolerance = 1e-4;
            } else {
                tolerance = 0;
            }
        };

        return (a - b).mag2() <= tolerance;
    }

    template<typename Scalar, int64_t Dims>
    std::ostream &operator<<(std::ostream &os, const GenericVector<Scalar, Dims> &vec) {
        os << vec.str();
        return os;
    }
} // namespace librapid

#ifdef FMT_API
template<typename Scalar, int64_t D>
struct fmt::formatter<librapid::GenericVector<Scalar, D>> {
    template<typename ParseContext>
    constexpr auto parse(ParseContext &ctx) {
        return ctx.begin();
    }

    template<typename FormatContext>
    auto format(const librapid::GenericVector<Scalar, D> &arr, FormatContext &ctx) {
        return fmt::format_to(ctx.out(), arr.str());
    }
};
#endif // FMT_API

#endif // LIBRAPID_MATH_VECTOR_HPP