/* mmmatrix.hpp * Part of Mathematical library built (ab)using Modern C++ 17 abstractions. * * This library is not intended to be _performant_, it does not contain * hand written SMID / SSE / AVX optimizations. It is instead an example * of highly inefficient (but abstract!) code, where matrices can contain any * data type. * * Naoki Pross * 2018 ~ 2019 */ #pragma once #include #include namespace mm { template class basic_matrix; template class matrix; template class square_matrix; // template // class diag_matrix; template class row_vec; template class col_vec; } template class mm::basic_matrix { public: using type = T; static constexpr std::size_t rows = Rows; static constexpr std::size_t cols = Cols; basic_matrix(const basic_matrix& other); basic_matrix(basic_matrix&& other); template basic_matrix(const basic_matrix& other); basic_matrix(const T (& values)[Rows][Cols]); basic_matrix(T (&& values)[Rows][Cols]); // access data T& at(std::size_t row, std::size_t col); auto&& operator[](std::size_t index); void swap_rows(std::size_t x, std::size_t y); void swap_cols(std::size_t x, std::size_t y); // mathematical operations basic_matrix transposed(); inline basic_matrix trd() { return transposed(); } // bool is_invertible(); // bool invert(); // basic_matrix inverse(); /// downcast to square matrix inline constexpr bool is_square() { return (Rows == Cols); } inline constexpr square_matrix to_square() { static_assert(is_square()); return static_cast>(*this); } /// downcast to row_vector inline constexpr bool is_row_vec() { return (Cols == 1); } inline constexpr row_vec to_row_vec() { static_assert(is_row_vec()); return static_cast>(*this); } /// downcast to col_vector inline constexpr bool is_col_vec() { return (Rows == 1); } inline constexpr col_vec to_col_vec() { static_assert(is_col_vec()); return static_cast>(*this); } private: T data[Rows][Cols] = {}; }; template mm::basic_matrix::basic_matrix(const mm::basic_matrix& other) { for (int row = 0; row < Rows; row++) for (int col = 0; col < Cols; col++) data[row][col] = other.data[row][col]; } template mm::basic_matrix::basic_matrix(mm::basic_matrix&& other) { data = other.data; } template template mm::basic_matrix::basic_matrix(const mm::basic_matrix& other) { static_assert((ORows <= Rows), "cannot copy a taller matrix into a smaller one" ); static_assert((OCols <= Cols), "cannot copy a larger matrix into a smaller one" ); for (int row = 0; row < Rows; row++) for (int col = 0; col < Cols; col++) data[row][col] = other.data[row][col]; } template mm::basic_matrix::basic_matrix(const T (& values)[Rows][Cols]) { std::memcpy(&data, &values, sizeof(data)); } template mm::basic_matrix::basic_matrix(T (&& values)[Rows][Cols]) { data = values; } /* member functions */ template T& mm::basic_matrix::at(std::size_t row, std::size_t col) { static_assert(row < Rows, "out of row bound"); static_assert(col < Cols, "out of column bound"); return data[row][col]; } template auto&& mm::basic_matrix::operator[](std::size_t index) { if constexpr (is_row_vec()) return data[0][index]; else if constexpr (is_col_vec()) return data[index][0]; return row_vec(std::move(data[index])); } template void mm::basic_matrix::swap_rows(std::size_t x, std::size_t y) { if (x == y) return; for (int col = 0; col < Cols; col++) std::swap(data[x][col], data[y][col]); } template void mm::basic_matrix::swap_cols(std::size_t x, std::size_t y) { if (x == y) return; for (int row = 0; row < rows; row++) std::swap(data[row][x], data[row][y]); } template mm::basic_matrix mm::basic_matrix::transposed() { mm::basic_matrix result; for (int row = 0; row < M; row++) for (int col = 0; col < N; col++) result[row][col] = this[col][row]; return result; } /* operator overloading */ template mm::basic_matrix operator+( const mm::basic_matrix& a, const mm::basic_matrix& b ) { mm::basic_matrix result; for (int row = 0; row < Rows; row++) for (int col = 0; col < Cols; col++) result.at(row, col) = a.at(row, col) + a.at(row, col); return result; } template mm::basic_matrix operator*( const mm::basic_matrix& m, const T& scalar ) { mm::basic_matrix result; for (int row = 0; row < Rows; row++) for (int col = 0; col < Cols; col++) result.at(row, col) = m.at(row, col) * scalar; return result; } template mm::basic_matrix operator*( const T& scalar, const mm::basic_matrix& m ) { return m * scalar; } template mm::basic_matrix operator*( const mm::basic_matrix& a, const mm::basic_matrix& b ) { mm::basic_matrix result; // TODO: use a more efficient algorithm for (int row = 0; row < M; row++) for (int col = 0; col < N; col++) for (int k = 0; k < P; k++) result.at(row, col) = a.at(row, k) * b.at(k, col); return result; } template mm::basic_matrix operator-( const mm::basic_matrix& a, const mm::basic_matrix& b ) { return a + static_cast(-1) * b; } template std::ostream& operator<<(std::ostream& os, const mm::basic_matrix& m) { for (int row = 0; row < Rows; row++) { os << "[ "; for (int col = 0; col < (Cols -1); col++) { os << m.at(row, col); } os << m.at(Rows -1, Cols -1) << " ]\n"; } return os; } /* square matrix specializaiton */ template class mm::square_matrix : public mm::basic_matrix { public: /// in place transpose void transpose(); inline void tr() { transpose(); } /// in place inverse void invert(); }; template void mm::square_matrix::transpose() { for (int row = 0; row < N; row++) for (int col = 0; col < row; col++) std::swap(this->at(row, col), this->at(col, row)); } /* row vector specialization */ template class mm::row_vec : public mm::basic_matrix { public: }; /* column vector specialization */ template class mm::col_vec : public mm::basic_matrix { public: };