// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "tdm/Types.h" #include "tdm/Vec.h" namespace tdm { namespace impl { template struct is_all_scalar : std::false_type {}; template<> struct is_all_scalar<> : std::false_type {}; template struct is_all_scalar : std::is_scalar {}; template struct is_all_scalar : std::is_scalar {}; template struct is_all_scalar : is_all_scalar {}; } // namespace impl template inline mat transpose(const mat& m); template inline mat inverse(const mat& m); template struct mat { using value_type = T; using row_type = vec; using column_type = vec; static constexpr dim_t rows() { return column_type::dimensions(); } static constexpr dim_t columns() { return row_type::dimensions(); } private: row_type values[R]; public: mat() : values{} { } ~mat() = default; mat(const mat&) = default; mat& operator=(const mat&) = default; mat(mat&&) = default; mat& operator=(mat&&) = default; template::value>::type* = nullptr> explicit mat(U scalar) { apply([scalar](row_type& row, dim_t /*unused*/) { row = row_type{scalar}; }); } template::value>::type* = nullptr> mat(Us... scalars) { T tmp[sizeof...(Us)] = {static_cast(scalars)...}; apply([&tmp](row_type& row, dim_t ri) { row.apply([&tmp, ri](value_type& value, dim_t ci) { value = tmp[ri * columns() + ci]; }); }); } template mat(const mat& rhs) { apply([&rhs](row_type& row, dim_t ri) { row = rhs[ri]; }); } template mat& operator=(const mat& rhs) { return operator=(mat{rhs}); } template::type* = nullptr> mat(const vec& ... vs) : values{row_type{vs} ...} { } template::type * = nullptr> static mat fromRows(const vec& ... vs) { return mat{vs ...}; } template::type* = nullptr> static mat fromColumns(const vec& ... vs) { mat tmp{vs ...}; return tdm::transpose(tmp); } template static typename std::enable_if::type diagonal(U scalar) { mat ret; ret.apply([scalar](row_type& row, dim_t ri) { row[ri] = scalar; }); return ret; } template static typename std::enable_if::type diagonal(const vec& scalars) { mat ret; ret.apply([&scalars](row_type& row, dim_t ri) { row[ri] = scalars[ri]; }); return ret; } template static typename std::enable_if<(H == W) && (sizeof...(Us) == H), mat>::type diagonal(Us... scalars) { return diagonal(vec{scalars ...}); } template static typename std::enable_if::type identity() { return diagonal(static_cast(1)); } row_type& operator[](dim_t index) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay) assert(index < rows()); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) return values[index]; } const row_type& operator[](dim_t index) const { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay) assert(index < rows()); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) return values[index]; } T& operator()(dim_t rowIndex, dim_t colIndex) { return operator[](rowIndex)[colIndex]; } const T& operator()(dim_t rowIndex, dim_t colIndex) const { return operator[](rowIndex)[colIndex]; } template mat& apply(F func) { for (dim_t ri{}; ri != rows(); ++ri) { func(values[ri], ri); } return *this; } template const mat& apply(F func) const { for (dim_t ri{}; ri != rows(); ++ri) { func(values[ri], ri); } return *this; } mat& operator++() { return apply([](row_type& row, dim_t /*unused*/) { ++row; }); } mat& operator--() { return apply([](row_type& row, dim_t /*unused*/) { --row; }); } template mat& operator+=(U rhs) { return apply([rhs](row_type& row, dim_t /*unused*/) { row += rhs; }); } template mat& operator+=(const mat& rhs) { return apply([&rhs](row_type& row, dim_t ri) { row += rhs[ri]; }); } mat& operator+=(const mat& rhs) { return operator+=(rhs); } template mat& operator-=(U rhs) { return apply([rhs](row_type& row, dim_t /*unused*/) { row -= rhs; }); } template mat& operator-=(const mat& rhs) { return apply([&rhs](row_type& row, dim_t ri) { row -= rhs[ri]; }); } mat& operator-=(const mat& rhs) { return operator-=(rhs); } template mat& operator*=(U rhs) { return apply([rhs](row_type& row, dim_t /*unused*/) { row *= rhs; }); } template mat& operator*=(const mat& rhs) { return (*this = *this * rhs); } mat& operator*=(const mat& rhs) { return operator*=(rhs); } template mat& operator/=(U rhs) { return apply([rhs](row_type& row, dim_t /*unused*/) { row /= rhs; }); } template mat& operator/=(const mat& rhs) { return operator*=(inverse(rhs)); } mat& operator/=(const mat& rhs) { return operator/=(rhs); } template typename std::enable_if::type transpose() { return (*this = tdm::transpose(*this)); } mat& negate() { apply([](row_type& row, dim_t /*unused*/) { row.negate(); }); return *this; } row_type row(dim_t index) const { return operator[](index); } column_type column(dim_t index) const { column_type col; apply([&col, index](const row_type& row, dim_t ri) { col[ri] = row[index]; }); return col; } template typename std::enable_if<(H > 1 && W > 1 && H <= R && W <= C), mat >::type submat(dim_t y, dim_t x) const { assert(H + y <= rows()); assert(W + x <= columns()); mat ret; ret.apply([this, y, x](typename mat::row_type& row, dim_t ri) { row.apply([this, y, x, ri](value_type& value, dim_t ci) { value = values[y + ri][x + ci]; }); }); return ret; } }; template inline bool operator==(const mat& lhs, const mat& rhs) { using row_type = typename mat::row_type; bool retval = true; lhs.apply([&rhs, &retval](const row_type& row, dim_t ri) { retval = retval && (row == rhs[ri]); }); return retval; } template inline bool operator!=(const mat& lhs, const mat& rhs) { return !(lhs == rhs); } template inline mat operator+(const mat& m) { return m; } template inline mat operator-(const mat& m) { mat ret{m}; ret.negate(); return ret; } template inline mat operator+(const mat& lhs, T rhs) { return mat(lhs) += rhs; } template inline mat operator+(T lhs, const mat& rhs) { return mat(lhs) += rhs; } template inline mat operator+(const mat& lhs, const mat& rhs) { return mat(lhs) += rhs; } template inline mat operator-(const mat& lhs, T rhs) { return mat(lhs) -= rhs; } template inline mat operator-(T lhs, const mat& rhs) { return mat(lhs) -= rhs; } template inline mat operator-(const mat& lhs, const mat& rhs) { return mat(lhs) -= rhs; } template inline mat operator*(const mat& lhs, T rhs) { return mat(lhs) *= rhs; } template inline mat operator*(T lhs, const mat& rhs) { return mat(rhs) *= lhs; } template inline typename mat::row_type operator*(const typename mat::column_type& lhs, const mat& rhs) { using row_type = typename mat::row_type; row_type ret; rhs.apply([&ret, &lhs](const row_type& row, dim_t ri) { ret += (row * row_type{lhs[ri]}); }); return ret; } template inline typename mat::column_type operator*(const mat& lhs, const typename mat::row_type& rhs) { using column_type = typename mat::column_type; using value_type = typename column_type::value_type; column_type ret; rhs.apply([&ret, &lhs](value_type value, dim_t ci) { ret += (lhs.column(ci) * column_type{value}); }); return ret; } template inline mat operator*(const mat& lhs, const mat& rhs) { using row_type = typename mat::row_type; mat ret; ret.apply([&lhs, &rhs](row_type& row, dim_t ri) { row = (lhs[ri] * rhs); }); return ret; } template inline mat operator/(const mat& lhs, T rhs) { return mat(lhs) /= rhs; } template inline mat operator/(T lhs, const mat& rhs) { using row_type = typename mat::row_type; mat tmp{rhs}; tmp.apply([lhs](row_type& row, dim_t /*unused*/) { row = lhs / row; }); return tmp; } template inline typename mat::row_type operator/(const typename mat::column_type& lhs, const mat& rhs) { return (lhs * inverse(rhs)); } template inline typename mat::column_type operator/(const mat& lhs, const typename mat::row_type& rhs) { return (inverse(lhs) * rhs); } template inline mat operator/(const mat& lhs, const mat& rhs) { return mat(lhs) /= rhs; } template inline mat applied(const mat& lhs, F func) { mat m{lhs}; m.apply(func); return m; } } // namespace tdm