mirror of https://github.com/bsnes-emu/bsnes.git
214 lines
5.9 KiB
C++
214 lines
5.9 KiB
C++
#pragma once
|
|
|
|
namespace nall {
|
|
|
|
template<typename T, uint Rows, uint Cols>
|
|
struct Matrix {
|
|
static_assert(Rows > 0 && Cols > 0);
|
|
|
|
Matrix() = default;
|
|
Matrix(const Matrix&) = default;
|
|
Matrix(const initializer_list<T>& source) {
|
|
uint index = 0;
|
|
for(auto& value : source) {
|
|
if(index >= Rows * Cols) break;
|
|
values[index / Cols][index % Cols] = value;
|
|
}
|
|
}
|
|
|
|
operator array_span<T>() { return {values, Rows * Cols}; }
|
|
operator array_view<T>() const { return {values, Rows * Cols}; }
|
|
|
|
//1D matrices (for polynomials, etc)
|
|
auto operator[](uint row) -> T& { return values[row][0]; }
|
|
auto operator[](uint row) const -> T { return values[row][0]; }
|
|
|
|
//2D matrices
|
|
auto operator()(uint row, uint col) -> T& { return values[row][col]; }
|
|
auto operator()(uint row, uint col) const -> T { return values[row][col]; }
|
|
|
|
//operators
|
|
auto operator+() const -> Matrix {
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = +target(row, col);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto operator-() const -> Matrix {
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = -target(row, col);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto operator+(const Matrix& source) const -> Matrix {
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = target(row, col) + source(row, col);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto operator-(const Matrix& source) const -> Matrix {
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = target(row, col) - source(row, col);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto operator*(T source) const -> Matrix {
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = target(row, col) * source;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto operator/(T source) const -> Matrix {
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = target(row, col) / source;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//warning: matrix multiplication is not commutative!
|
|
template<uint SourceRows, uint SourceCols>
|
|
auto operator*(const Matrix<T, SourceRows, SourceCols>& source) const -> Matrix<T, Rows, SourceCols> {
|
|
static_assert(Cols == SourceRows);
|
|
Matrix<T, Rows, SourceCols> result;
|
|
for(uint y : range(Rows)) {
|
|
for(uint x : range(SourceCols)) {
|
|
T sum{};
|
|
for(uint z : range(Cols)) {
|
|
sum += target(y, z) * source(z, x);
|
|
}
|
|
result(y, x) = sum;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<uint SourceRows, uint SourceCols>
|
|
auto operator/(const Matrix<T, SourceRows, SourceCols>& source) const -> maybe<Matrix<T, Rows, SourceCols>> {
|
|
static_assert(Cols == SourceRows && SourceRows == SourceCols);
|
|
if(auto inverted = source.invert()) return operator*(inverted());
|
|
return {};
|
|
}
|
|
|
|
auto& operator+=(const Matrix& source) { return *this = operator+(source); }
|
|
auto& operator-=(const Matrix& source) { return *this = operator-(source); }
|
|
auto& operator*=(T source) { return *this = operator*(source); }
|
|
auto& operator/=(T source) { return *this = operator/(source); }
|
|
template<uint SourceRows, uint SourceCols>
|
|
auto& operator*=(const Matrix<T, SourceRows, SourceCols>& source) { return *this = operator*(source); }
|
|
//matrix division is not always possible (when matrix cannot be inverted), so operator/= is not provided
|
|
|
|
//algorithm: Gauss-Jordan
|
|
auto invert() const -> maybe<Matrix> {
|
|
static_assert(Rows == Cols);
|
|
Matrix source = *this;
|
|
Matrix result = identity();
|
|
|
|
const auto add = [&](uint targetRow, uint sourceRow, T factor = 1) {
|
|
for(uint col : range(Cols)) {
|
|
result(targetRow, col) += result(sourceRow, col) * factor;
|
|
source(targetRow, col) += source(sourceRow, col) * factor;
|
|
}
|
|
};
|
|
|
|
const auto sub = [&](uint targetRow, uint sourceRow, T factor = 1) {
|
|
for(uint col : range(Cols)) {
|
|
result(targetRow, col) -= result(sourceRow, col) * factor;
|
|
source(targetRow, col) -= source(sourceRow, col) * factor;
|
|
}
|
|
};
|
|
|
|
const auto mul = [&](uint row, T factor) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) *= factor;
|
|
source(row, col) *= factor;
|
|
}
|
|
};
|
|
|
|
for(uint i : range(Cols)) {
|
|
if(source(i, i) == 0) {
|
|
for(uint row : range(Rows)) {
|
|
if(source(row, i) != 0) {
|
|
add(i, row);
|
|
break;
|
|
}
|
|
}
|
|
//matrix is not invertible:
|
|
if(source(i, i) == 0) return {};
|
|
}
|
|
|
|
mul(i, T{1} / source(i, i));
|
|
for(uint row : range(Rows)) {
|
|
if(row == i) continue;
|
|
sub(row, i, source(row, i));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
auto transpose() const -> Matrix<T, Cols, Rows> {
|
|
Matrix<T, Cols, Rows> result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(col, row) = target(row, col);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static auto identity() -> Matrix {
|
|
static_assert(Rows == Cols);
|
|
Matrix result;
|
|
for(uint row : range(Rows)) {
|
|
for(uint col : range(Cols)) {
|
|
result(row, col) = row == col;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//debugging function: do not use in production code
|
|
template<uint Pad = 0>
|
|
auto _print() const -> void {
|
|
for(uint row : range(Rows)) {
|
|
nall::print("[ ");
|
|
for(uint col : range(Cols)) {
|
|
nall::print(pad(target(row, col), Pad, ' '), " ");
|
|
}
|
|
nall::print("]\n");
|
|
}
|
|
}
|
|
|
|
protected:
|
|
//same as operator(), but with easier to read syntax inside Matrix class
|
|
auto target(uint row, uint col) -> T& { return values[row][col]; }
|
|
auto target(uint row, uint col) const -> T { return values[row][col]; }
|
|
|
|
T values[Rows][Cols]{};
|
|
};
|
|
|
|
}
|