#pragma once #include #include #include #include #include #include inline size_t mod(int num, size_t module) { while (num < 0) { num += module; }; return static_cast(num % module); } enum Norm { Frob, Max }; template class Matrix { public: // Default Constructor Matrix() = default; // Creation Constructor (0 Matrix) Matrix(size_t nrow, size_t ncol) : _nrow{nrow}, _ncol{ncol}, _data(nrow * ncol) { }; // Creation Constructor with default Element (for example all elements 1) Matrix(size_t nrow, size_t ncol, const T& elem) : _nrow{nrow}, _ncol{ncol}, _data(nrow * ncol, elem) { }; // Copy Constructor Matrix(const Matrix& A) : _nrow{A._nrow}, _ncol{A._ncol}, _data(A._data) { }; size_t nrow() const { return _nrow; }; size_t ncol() const { return _ncol; }; size_t nx() const { return _ncol; }; size_t ny() const { return _nrow; }; size_t size() const { return _nrow * _ncol; }; T* data() { return _data.data(); }; const T* data() const { return _data.data(); }; T& operator()(int i) { return _data[i]; }; const T& operator()(int i) const { return _data[i]; }; T& operator()(int i, int j) { return _data[i + j * _nrow]; } const T& operator()(int i, int j) const { return _data[i + j * _nrow]; } T& get(int i) { return _data[index(i)]; }; const T& get(int i) const { return _data[index(i)]; }; T& get(int i, int j) { return _data[index(i, j)]; } const T& get(int i, int j) const { return _data[index(i, j)]; } template double norm() const { T accum = static_cast(0); /*< result accumulator */ // Enforce valid norm types (at compile time) static_assert(N == Norm::Frob || N == Norm::Max); if constexpr (N == Norm::Frob) { // Sum of Squares for (const T& elem : _data) { accum += elem * elem; } // The Frobenius norm is the square root of the sum of squares return std::sqrt(static_cast(accum)); } else { // Maximum Norm // Maximum absolute value for (const T& elem : _data) { if (accum < std::abs(elem)) { accum = std::abs(elem); } } return static_cast(accum); } } /** * Distance of this Matrix to given matrix A as || this - A ||_N * * Note: It there is a dimension mismatch, only the number of elements * of the smaller matrix are considered. * * @param A matrix to compute the "distance" to * @param mar_b bottom margins * @param mar_l left margins * @param mar_t top margins * @param mar_r right margins */ template double dist(const Matrix& A, size_t mar_b = 0, size_t mar_l = 0, size_t mar_t = 0, size_t mar_r = 0 ) const { // Enforce valid norm types (at compile time) static_assert(N == Norm::Frob || N == Norm::Max); assert(this->_nrow == A._nrow); assert(this->_ncol == A._ncol); T accum = static_cast(0); /*< result accumulator */ for (size_t j = mar_l; j + mar_r < _ncol; ++j) { for (size_t i = mar_t; i + mar_b < _nrow; ++i) { if constexpr (N == Norm::Frob) { T diff = (*this)(i, j) - A(i, j); accum += diff * diff; } else { accum = std::max(std::abs((*this)(i, j) - A(i, j)), accum); } } } if constexpr (N == Norm::Frob) { return std::sqrt(static_cast(accum)); } else { return static_cast(accum); } } /** overloaded standard swap for matrices (of same type) * * @example * Matrix A(1, 2); * Matrix B(3, 4); * std::swap(A, B); */ friend void swap(Matrix& A, Matrix& B) { std::swap(A._nrow, B._nrow); std::swap(A._ncol, B._ncol); std::swap(A._data, B._data); } private: size_t _nrow; /*< Number of Rows */ size_t _ncol; /*< Number of Columns */ std::vector _data; /*< Matrix elements / Data */ size_t index(int i) const { const int nelem = static_cast(_nrow * _ncol); while (i < 0) { i += nelem; } while (i >= nelem) { i -= nelem; } return i; }; size_t index(int i, int j) const { return static_cast(mod(i, _nrow) + mod(j, _ncol) * _nrow); } }; template std::ostream& operator<<(std::ostream& out, const Matrix& mat) { for (size_t i = 0; i < mat.nrow(); ++i) { for (size_t j = 0; j < mat.ncol(); ++j) { out << mat(i, j) << ' '; } out << '\n'; } return out; } template class MatrixView { public: MatrixView(Matrix& matrix, size_t index, size_t stride, size_t nelem) : _matrix(matrix), _index{index}, _stride{stride}, _nelem{nelem} { }; const size_t size() const { return _nelem; }; const size_t stride() const { return _stride; }; T* data() { return _matrix.data() + _index; }; const T* data() const { return _matrix.data() + _index; }; T& operator()(int i) { return _matrix(_index + i * _stride); }; const T& operator()(int i) const { return _matrix(_index + i * _stride); }; T& get(int i) { return _matrix.get(_index + i * _stride); }; const T& get(int i) const { return _matrix.get(_index + i * _stride); }; protected: Matrix& _matrix; size_t _index; size_t _stride; size_t _nelem; }; template std::ostream& operator<<(std::ostream& out, const MatrixView& view) { for (size_t i = 0; i < view.size(); ++i) { out << view(i) << ' '; } return out; } template class Row : public MatrixView { public: Row(Matrix& matrix, int index) : MatrixView(matrix, mod(index, matrix.nrow()), matrix.nrow(), matrix.ncol()) { }; }; template class Col : public MatrixView { public: Col(Matrix& matrix, int index) : MatrixView(matrix, mod(index, matrix.ncol()) * matrix.nrow(), 1, matrix.nrow()) { }; }; template class Diag : public MatrixView { public: Diag(Matrix& matrix) : MatrixView(matrix, 0, matrix.nrow() + 1, matrix.nrow() < matrix.ncol() ? matrix.nrow() : matrix.ncol()) { }; };