NSSC/Exercise_01/src/Matrix.h

228 lines
6.7 KiB
C++

#pragma once
#include <stddef.h>
#include <ostream>
#include <utility>
#include <vector>
#include <cmath>
#include <assert.h>
inline size_t mod(int num, size_t module) {
while (num < 0) { num += module; };
return static_cast<size_t>(num % module);
}
enum Norm { Frob, Max };
template <typename T>
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<T>& 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 <enum Norm N = Norm::Frob>
double norm() const {
T accum = static_cast<T>(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<double>(accum));
} else { // Maximum Norm
// Maximum absolute value
for (const T& elem : _data) {
if (accum < std::abs(elem)) {
accum = std::abs(elem);
}
}
return static_cast<double>(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 <enum Norm N = Norm::Frob>
double dist(const Matrix<T>& 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<T>(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<double>(accum));
} else {
return static_cast<double>(accum);
}
}
/** overloaded standard swap for matrices (of same type)
*
* @example
* Matrix<int> A(1, 2);
* Matrix<int> B(3, 4);
* std::swap(A, B);
*/
friend void swap(Matrix<T>& A, Matrix<T>& 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<T> _data; /*< Matrix elements / Data */
size_t index(int i) const {
const int nelem = static_cast<int>(_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<size_t>(mod(i, _nrow) + mod(j, _ncol) * _nrow);
}
};
template <typename T>
std::ostream& operator<<(std::ostream& out, const Matrix<T>& 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 <typename T>
class MatrixView {
public:
MatrixView(Matrix<T>& 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<T>& _matrix;
size_t _index;
size_t _stride;
size_t _nelem;
};
template <typename T>
std::ostream& operator<<(std::ostream& out, const MatrixView<T>& view) {
for (size_t i = 0; i < view.size(); ++i) {
out << view(i) << ' ';
}
return out;
}
template <typename T>
class Row : public MatrixView<T> {
public:
Row(Matrix<T>& matrix, int index) :
MatrixView<T>(matrix, mod(index, matrix.nrow()),
matrix.nrow(), matrix.ncol()) { };
};
template <typename T>
class Col : public MatrixView<T> {
public:
Col(Matrix<T>& matrix, int index) :
MatrixView<T>(matrix, mod(index, matrix.ncol()) * matrix.nrow(),
1, matrix.nrow()) { };
};
template <typename T>
class Diag : public MatrixView<T> {
public:
Diag(Matrix<T>& matrix) :
MatrixView<T>(matrix, 0, matrix.nrow() + 1,
matrix.nrow() < matrix.ncol() ? matrix.nrow() : matrix.ncol()) { };
};