// L============================================================================= // L This software is distributed under the MIT license. // L Copyright 2021 Péter Kardos // L============================================================================= #pragma once #include "../Common/Definitions.hpp" #include "../Common/Traits.hpp" #include "../Vector.hpp" #include namespace mathter { //------------------------------------------------------------------------------ // Matrix base class only allocating the memory //------------------------------------------------------------------------------ template class MatrixData { public: /// Returns the number of columns of the matrix. constexpr int ColumnCount() const { return Columns; } /// Returns the number of rows of the matrix. constexpr int RowCount() const { return Rows; } /// Returns the number of columns of the matrix. constexpr int Width() const { return Columns; } /// Returns the number of rows of the matrix. constexpr int Height() const { return Rows; } // Rows equal height, Columns equal width, row-major has column-sized stripes static constexpr int StripeDim = Layout == eMatrixLayout::ROW_MAJOR ? Columns : Rows; static constexpr int StripeCount = Layout == eMatrixLayout::ROW_MAJOR ? Rows : Columns; using StripeVecT = Vector; std::array stripes; protected: // Get element inline T& GetElement(int row, int col) { assert(row < RowCount()); assert(col < ColumnCount()); if constexpr (Layout == eMatrixLayout::ROW_MAJOR) { return stripes[row][col]; } else { return stripes[col][row]; } } inline T GetElement(int row, int col) const { assert(row < RowCount()); assert(col < ColumnCount()); if constexpr (Layout == eMatrixLayout::ROW_MAJOR) { return stripes[row][col]; } else { return stripes[col][row]; } } }; //------------------------------------------------------------------------------ // Submatrix helper //------------------------------------------------------------------------------ template class SubmatrixHelper { friend MatrixT; using Props = traits::MatrixTraits; template friend class SubmatrixHelper; static constexpr int VecDim = std::max(SRows, SColumns); static constexpr bool VectorAssignable = std::min(SRows, SColumns) == 1; protected: SubmatrixHelper(MatrixT& mat, int row, int col) : mat(mat), row(row), col(col) {} public: SubmatrixHelper(const SubmatrixHelper& rhs) = delete; SubmatrixHelper(SubmatrixHelper&& rhs) : mat(rhs.mat), row(rhs.row), col(rhs.col) {} template operator Matrix() const { Matrix ret; for (int i = 0; i < SRows; ++i) { for (int j = 0; j < SColumns; ++j) { ret(i, j) = (*this)(i, j); } } return ret; } template ::type> operator Vector() const { Vector v; int k = 0; for (int i = 0; i < SRows; ++i) { for (int j = 0; j < SColumns; ++j) { v(k) = (*this)(i, j); ++k; } } return v; } template SubmatrixHelper& operator=(const Matrix& rhs) { static_assert(!std::is_const::value, "Cannot assign to submatrix of const matrix."); // If aliasing happens, the same matrix is copied to itself with no side-effects. for (int i = 0; i < SRows; ++i) { for (int j = 0; j < SColumns; ++j) { mat(row + i, col + j) = rhs(i, j); } } return *this; } // From vector if applicable (for 1*N and N*1 submatrices) template ::type> SubmatrixHelper& operator=(const Vector& v) { static_assert(!std::is_const::value, "Cannot assign to submatrix of const matrix."); int k = 0; for (int i = 0; i < SRows; ++i) { for (int j = 0; j < SColumns; ++j) { mat(row + i, col + j) = v(k); ++k; } } return *this; } template SubmatrixHelper& operator=(const SubmatrixHelper& rhs) { static_assert(!std::is_const::value, "Cannot assign to submatrix of const matrix."); // If *this and rhs reference the same matrix, aliasing must be resolved. if ((void*)&mat == (void*)&rhs.mat) { Matrix::Type, SRows, SColumns, traits::MatrixTraits::Order, traits::MatrixTraits::Layout, traits::MatrixTraits::Packed> tmpmat; tmpmat = rhs; operator=(tmpmat); } else { for (int i = 0; i < SRows; ++i) { for (int j = 0; j < SColumns; ++j) { mat(row + i, col + j) = rhs(i, j); } } } return *this; } SubmatrixHelper& operator=(const SubmatrixHelper& rhs) { static_assert(!std::is_const::value, "Cannot assign to submatrix of const matrix."); return operator=(rhs); } typename Props::Type& operator()(int row, int col) { return mat(this->row + row, this->col + col); } typename Props::Type operator()(int row, int col) const { return mat(this->row + row, this->col + col); } private: MatrixT& mat; int row = -1, col = -1; }; //------------------------------------------------------------------------------ // Matrix class providing the common interface for all matrices //------------------------------------------------------------------------------ template class Matrix : public MatrixData { static_assert(Columns >= 1 && Rows >= 1, "Dimensions must be positive integers."); static constexpr int VecDim = std::max(Rows, Columns); static constexpr bool VectorAssignable = std::min(Rows, Columns) == 1; protected: using MatrixData::GetElement; template friend class Matrix; public: using MatrixData::RowCount; using MatrixData::ColumnCount; using typename MatrixData::StripeVecT; using MatrixData::stripes; using MatrixData::StripeCount; struct FromStripes_ {}; static constexpr FromStripes_ FromStripes = {}; //-------------------------------------------- // Constructors //-------------------------------------------- Matrix() = default; // From same multiplication order template Matrix(const Matrix& rhs) { for (int i = 0; i < RowCount(); ++i) { for (int j = 0; j < ColumnCount(); ++j) { (*this)(i, j) = rhs(i, j); } } } template ::value, int>::type = 0, typename std::enable_if<1 + sizeof...(Args) == Rows * Columns, int>::type = 0> Matrix(H h, Args... args) { Assign<0, 0>(h, args...); } // From vector if applicable (for 1*N and N*1 matrices) template ::type> Matrix(const Vector& v) { for (int i = 0; i < v.Dimension(); ++i) { (*this)(i) = v(i); } } /// Used by internal methods. template Matrix(FromStripes_, Stripes... stripes) : MatrixData{ std::forward(stripes)... } {} //-------------------------------------------- // Accessors //-------------------------------------------- // General matrix indexing inline T& operator()(int row, int col) { return GetElement(row, col); } inline T operator()(int row, int col) const { return GetElement(row, col); } // Column and row vector simple indexing template inline typename std::enable_if<(Columns == 1 && Rows > 1) || (Columns > 1 && Rows == 1), Q>::type& operator()(int idx) { return GetElement(Rows == 1 ? 0 : idx, Columns == 1 ? 0 : idx); } template inline typename std::enable_if<(Columns == 1 && Rows > 1) || (Columns > 1 && Rows == 1), Q>::type operator()(int idx) const { return GetElement(Rows == 1 ? 0 : idx, Columns == 1 ? 0 : idx); } // Submatrices /// DEPRECATED: I plan to replace it with a nicer MatrixView like std::string_view. template mathter::SubmatrixHelper Submatrix(int rowIdx, int colIdx) { assert(Subrows + rowIdx <= Rows); assert(Subcolumns + colIdx <= Columns); return SubmatrixHelper(*this, rowIdx, colIdx); } template /// DEPRECATED: I plan to replace it with a nicer MatrixView like std::string_view. mathter::SubmatrixHelper Submatrix(int rowIdx, int colIdx) const { assert(Subrows + rowIdx <= Rows); assert(Subcolumns + colIdx <= Columns); return SubmatrixHelper(*this, rowIdx, colIdx); } /// Return the submatrix corresponding to the specified column. auto Column(int colIdx) { return Submatrix(0, colIdx); } /// Return the submatrix corresponding to the specified row. auto Row(int rowIdx) { return Submatrix<1, Columns>(rowIdx, 0); } /// Return the submatrix corresponding to the specified column. auto Column(int colIdx) const { return Submatrix(0, colIdx); } /// Return the submatrix corresponding to the specified row. auto Row(int rowIdx) const { return Submatrix<1, Columns>(rowIdx, 0); } // Conversion to vector if applicable template ::type> operator Vector() const { Vector v; int k = 0; for (int i = 0; i < Rows; ++i) { for (int j = 0; j < Columns; ++j) { v(k) = (*this)(i, j); ++k; } } return v; } protected: template void Assign(Head head, Args... args) { (*this)(i, j) = (T)head; Assign<((j != Columns - 1) ? i : (i + 1)), ((j + 1) % Columns)>(args...); } template void Assign() {} }; // namespace mathter } // namespace mathter