/** --------------------------------------------------------------------------- * -*- c++ -*- * @file: matrix.h * * Copyright (c) 2017 Yann Herklotz Grave * MIT License, see LICENSE file for more details. * ---------------------------------------------------------------------------- */ #pragma once #include #include #include #include #include namespace yage { template class Matrix; /** @internal Namespace for internal details. * * Details Namespace * ================ * * This is the namespace used for implementation details. */ namespace details { /** @internal Internal Row class used by the Matrix class to return the * internal data structure of the Matrix. * * Row * === * * Internal Row class to return a value in the row of the matrix. */ template class Row { private: Matrix *parent_; int index_; public: Row(Matrix *parent, int index) : parent_(parent), index_(index) { } Type &operator[](int col) { // The index is the y-position of the element in the matrix return parent_->data_[index_ * Cols + col]; } const Type &operator[](int col) const { return parent_->data_[index_ * Cols + col]; } }; } // namespace details /** Base Matrix class used by other similar classes. */ template class Matrix { // friended with the row class so that it can access protected member data. friend class details::Row; protected: /// Vector containing the data of the matrix. std::vector data_; public: /// Initializes the size of the data_ vector. Matrix() : data_(Rows * Cols) {} Matrix(const std::vector &data) : data_(data) {} /// Returns the row size of the Matrix. int rowSize() const { return Rows; } /// Returns the column size of the Matrix. int colSize() const { return Cols; } /** Return the row specified row as a Matrix with only one row. * * @param row Row number to be returned. * @return The row that is specified by the row variables. */ Matrix<1, Cols, Type> getRow(int row) const { Matrix<1, Cols, Type> rowMatrix; for (int i = 0; i < Cols; ++i) { rowMatrix[0][i] = data_[row][i]; } return rowMatrix; } /** Get a specific column in a column vector. * * @param col Column number to be returned. * @return Column Matrix of the selected column. */ Matrix getCol(int col) const { Matrix colMatrix; for (int i = 0; i < Rows; ++i) { colMatrix[i][0] = data_[i][col]; } return colMatrix; } /** Iterator support for the start. * * @return Iterator pointing to the start of the data. */ typename std::vector::iterator begin() { return data_.begin(); } /** Iterator support for the end. * * @return Iterator pointing to the end of the data. */ typename std::vector::iterator end() { return data_.end(); } /** Prints out the matrix, but can also be implemented by other classes to * print data differently. * * @bug When printing certain matrices, it omits a row or column. Still * need to determine under which conditions. */ virtual std::string toString() const { std::stringstream ss; ss << '['; for (int i = 0; i < Rows - 1; ++i) { ss << '['; for (int j = 0; j < Cols - 1; ++j) { ss << data_[i * Cols + j] << ' '; } ss << data_[(Rows - 1) * Cols + Cols - 1] << "],"; } ss << '['; for (int j = 0; j < Cols - 1; ++j) { ss << data_[(Rows - 1) * Cols + j] << ' '; } ss << data_[(Rows - 1) * Cols + Cols - 1] << "]]"; return ss.str(); } details::Row operator[](int row) { return details::Row(this, row); } details::Row operator[](int row) const { return details::Row((Matrix *)this, row); } Matrix &operator+=(const Matrix &rhs) { std::vector out; out.reserve(data_.size()); std::transform(data_.begin(), data_.end(), rhs.data_.begin(), std::back_inserter(out), [](Type a, Type b) { return a + b; }); data_ = std::move(out); return *this; } Matrix &operator-=(const Matrix &rhs) { std::vector out; out.reserve(data_.size()); std::transform(data_.begin(), data_.end(), rhs.begin(), std::back_inserter(out), [](Type a, Type b) { return a - b; }); data_ = std::move(out); return *this; } }; template Matrix operator+(Matrix lhs, const Matrix &rhs) { lhs += rhs; return lhs; } template Matrix operator-(Matrix lhs, const Matrix &rhs) { lhs -= rhs; return lhs; } template Matrix operator+(Matrix lhs, const T &rhs) { for (auto &data : lhs) { data += rhs; } return lhs; } template Matrix operator+(const T &lhs, Matrix rhs) { for (auto &data : rhs) { data += lhs; } return rhs; } template Matrix operator-(Matrix lhs, const T &rhs) { for (auto &data : lhs) { data -= rhs; } return lhs; } template Matrix operator-(const T &lhs, Matrix rhs) { for (auto &data : rhs) { data = lhs - data; } return rhs; } template Matrix operator*(Matrix lhs, const T &rhs) { for (auto &data : lhs) { data *= rhs; } return lhs; } template Matrix operator*(const T &lhs, Matrix rhs) { for (auto &data : rhs) { data *= lhs; } return rhs; } template Matrix operator/(Matrix lhs, const T &rhs) { for (auto &data : lhs) { data /= rhs; } return lhs; } template bool operator==(const Matrix &lhs, const Matrix &rhs) { for (int i = 0; i < M; ++i) { for (int j = 0; j < N; ++j) { if (lhs[i][j] != rhs[i][j]) { return false; } } } return true; } template std::ostream &operator<<(std::ostream &os, const Matrix &mat) { return os << mat.toString(); } template class Vector : public Matrix { public: Vector() : Matrix() {} Vector(const Matrix &other) : Matrix(other) { } Vector(const std::vector &data) : Matrix(data) { } Type &operator[](int col) { return this->data_[col]; } const Type &operator[](int col) const { return this->data_[col]; } std::string toString() const override { std::stringstream ss; ss << "["; for (std::size_t i = 0; i < this->data_.size() - 1; ++i) { ss << this->data_[i] << " "; } ss << this->data_[this->data_.size() - 1] << "]"; return ss.str(); } }; /** 2D Vector class. * * Two dimensional vector class. */ template class Vector2 : public Vector<2, Type> { public: Vector2() : Vector<2, Type>() {} Vector2(const std::vector &data) : Vector<2, Type>(data) {} Vector2(Type x, Type y) { this->data_[0] = x; this->data_[1] = y; } Vector2(const Matrix<2, 1, Type> &other) : Vector<2, Type>(other) {} Type &x() { return this->data_[0]; } const Type &x() const { return this->data_[0]; } Type &y() { return this->data_[1]; } const Type &y() const { return this->data_[1]; } }; /** 3D Vector class. * * Two dimensional vector class. */ template class Vector3 : public Vector<3, Type> { public: Type &x, &y, &z; Vector3() : Vector<4, Type>() {} Vector3(std::vector data) : Vector<3, Type>(data), x(this->data_[0]), y(this->data_[1]), z(this->data_[2]) { } Vector3(Type x_in, Type y_in, Type z_in) : Vector<3, Type>({x_in, y_in, z_in}), x(this->data_[0]), y(this->data_[1]), z(this->data_[2]) { } }; /** 4D Vector class */ template class Vector4 : public Vector<4, Type> { public: Type &x, &y, &z, &w; Vector4() : Vector<4, Type>() {} Vector4(std::vector data) : Vector<4, Type>(data), x(this->data_[0]), y(this->data_[1]), z(this->data_[2]), w(this->data_[3]) { } Vector4(Type x_in, Type y_in, Type z_in, Type w_in) : Vector<4, Type>({x_in, y_in, z_in, w_in}), x(this->data_[0]), y(this->data_[1]), z(this->data_[2]), w(this->data_[3]) { } }; /** Definition of a 2D vector. */ using Vector2d = Vector2; using Vector2f = Vector2; using Vector2i = Vector2; /** Definition of a 3D vector. */ using Vector3d = Vector3; using Vector3f = Vector3; using Vector3i = Vector3; /** Definition of a 4D vector */ using Vector4d = Vector4; using Vector4f = Vector4; using Vector4i = Vector4; /** Namespace containing functions that operate on matrices. * * Implementations defined here are meant to operate on anything that inherits * from the base Matrix class. */ namespace matrix { /** Transposes a matrix and returns the result * * @param m input matrix. */ template Matrix transpose(const Matrix &m) { Matrix trans; for (int i = 0; i < M; ++i) { for (int j = 0; j < N; ++j) { trans[j][i] = m[i][j]; } } return trans; } /** Returns the dot product between two vectors * * @param m1,m2 Input matrices. */ template T dot(const Matrix &m1, const Matrix &m2) { T sum = 0; for (int i = 0; i < R; ++i) { sum += m1[i][0] * m2[i][0]; } return sum; } /** Multiplies two matrices together. * * @param m1,m2 Matrix inputs * * Requires the two matrices to be compatible with multiplication. */ template Matrix multiply(const Matrix &m1, const Matrix &m2) { /// @todo Think if this should be a static_assert. if (N != P) { throw std::runtime_error( "Matrices don't have the right dimensions for multiplication"); } Matrix res; /// Performs multiplication by getting the rows and columns, transposing /// one of them and then doting the result. for (int i = 0; i < M; ++i) { for (int j = 0; j < Q; ++j) { res[i][j] = dot(transpose(m1.getRow(i)), m2.getCol(j)); } } return res; } } // namespace matrix } // namespace yage