225 lines
6.6 KiB
C++
225 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include <stddef.h>
|
|
#include <ostream>
|
|
#include <utility>
|
|
#include <vector>
|
|
#define _USE_MATH_DEFINES /* enables math constants from cmath */
|
|
#include <cmath>
|
|
|
|
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) { };
|
|
// // Move constructor // TODO:
|
|
// Matrix(Matrix<T>&& A) :
|
|
// _nrow{A._nrow},
|
|
// _ncol{A._ncol},
|
|
// _data(std::move(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 missmatch, only the number of elements
|
|
* of the smaller matrix are considured.
|
|
*/
|
|
template <enum Norm N = Norm::Frob>
|
|
double dist(const Matrix<T>& A) const {
|
|
T accum = static_cast<T>(0); /*< result accomulator */
|
|
size_t nelem = size() < A.size() ? size() : A.size();
|
|
|
|
// Enforce valid norm types (at compile time)
|
|
static_assert(N == Norm::Frob || N == Norm::Max);
|
|
|
|
if constexpr (N == Norm::Frob) {
|
|
// Sum of Squared differences
|
|
for (size_t i = 0; i < nelem; ++i) {
|
|
T diff = (*this)(i) - A(i);
|
|
accum += diff * diff;
|
|
}
|
|
return std::sqrt(static_cast<double>(accum));
|
|
} else { // Maximum Norm
|
|
for (size_t i = 0; i < nelem; ++i) {
|
|
T diff = std::abs((*this)(i) - A(i));
|
|
if (accum < diff) {
|
|
accum = diff;
|
|
}
|
|
}
|
|
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()) { };
|
|
};
|