/* 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 #include #include #include #include #include #include "mm/mmiterator.hpp" namespace mm { template class basic_matrix; /* specialisations */ template class matrix; // simple matrix format /* specialization of basic_matrx for Cols = 1 */ template class row_vec; /* specialization of basic_matrx for Rows = 1 */ template class col_vec; // transposed version of row_vec template class square_matrix; /* specialisation of a square_matrix for a sub-diagonal composed matrix */ template class diag_matrix; } /*namespace mm { template using diag_iterator = vector_iterator>; template using const_diag_iterator = vector_iterator::type, N, N, MM_DIAG_ITER, typename std::add_const>::type>; }*/ /* * Matrix class, no access methods */ template class mm::basic_matrix { public: using type = T; template friend class mm::basic_matrix; template friend class mm::matrix; template friend class mm::iter::basic_iterator; template friend class mm::iter::diag_iterator; //template //friend class mm::iter::basic_iterator>; //template //friend class mm::iter::basic_iterator::type, Rows, Cols, typename std::add_const>::type>; basic_matrix(); // from initializer_list basic_matrix(std::initializer_list> l); // copyable and movable basic_matrix(const basic_matrix& other) = default; basic_matrix(basic_matrix&& other) = default; // copy from another matrix template basic_matrix(const basic_matrix& other); void swap_rows(std::size_t x, std::size_t y); void swap_cols(std::size_t x, std::size_t y); // mathematical operations //virtual basic_matrix transposed() const; //inline basic_matrix td() const { return transposed(); } protected: template basic_matrix(ConstIterator begin, ConstIterator end); private: std::array data; }; template mm::basic_matrix::basic_matrix() { std::fill(data.begin(), data.end(), 0); } template mm::basic_matrix::basic_matrix( std::initializer_list> l ) { assert(l.size() == Rows); auto data_it = data.begin(); for (auto&& row : l) { data_it = std::copy(row.begin(), row.end(), data_it); } } 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" ); std::fill(data.begin(), data.end(), 0); for (unsigned row = 0; row < Rows; row++) for (unsigned col = 0; col < Cols; col++) this->at(row, col) = other.at(row, col); } /* protected construtor */ template template mm::basic_matrix::basic_matrix( ConstIterator begin, ConstIterator end ) { assert(static_cast(std::distance(begin, end)) >= ((Rows * Cols))); std::copy(begin, end, data.begin()); } template void mm::basic_matrix::swap_rows(std::size_t x, std::size_t y) { if (x == y) return; for (unsigned col = 0; col < Cols; col++) std::swap(this->at(x, col), this->at(y, col)); } template void mm::basic_matrix::swap_cols(std::size_t x, std::size_t y) { if (x == y) return; for (unsigned row = 0; row < Rows; row++) std::swap(this->at(row, x), this->at(row, y)); } /* * Matrix object */ template class mm::matrix { public: //template using vec_iterator = mm::iter::basic_iterator>; //template using const_vec_iterator = mm::iter::basic_iterator::type, Rows, Cols, typename std::add_const>::type>; // default zeros constructor matrix() : M(std::make_shared>()), transposed(false) {} // from initializer_list matrix(std::initializer_list> l) : M(std::make_shared>(l)), transposed(false) {} // copyable and movable matrix(const matrix& other) // deep copy : M(std::make_shared>(*other.M)), transposed(other.transposed) {} matrix(basic_matrix&& other) // move ptr : M(other.M), transposed(other.transposed) { other.M = nullptr; } // copy from another matrix /*template matrix(const matrix& other) : M(std::make_shared(*other.M)), transposed(other.transposed) {} */ matrix operator=(const basic_matrix& other) // deep copy { *M = *other.M; transposed = other.transposed; } /* * Transposition */ matrix& transpose_d() { transposed = !transposed; return *this; } matrix transpose() const { auto m = shallow_cpy(); m.transposed = !transposed; return m; } inline matrix& td() { return transpose(); } inline matrix t() const { return transpose(); } // strongly transpose matrix transpose_cpy() const { matrix out(); // copy // TODO } /* * Pointer status */ bool expired() const { return M == nullptr; } /* * Downcasting conditions */ /// downcast to square matrix static inline constexpr bool is_square() { return (Rows == Cols); } inline constexpr square_matrix to_square() const { static_assert(is_square()); return static_cast>(*this); } /// downcast to row_vector static inline constexpr bool is_row_vec() { return (Cols == 1); } inline constexpr row_vec to_row_vec() const { static_assert(is_row_vec()); return static_cast>(*this); } /// downcast to col_vector static inline constexpr bool is_col_vec() { return (Rows == 1); } inline constexpr col_vec to_col_vec() const { static_assert(is_col_vec()); return static_cast>(*this); } /* Accessors */ T& at(std::size_t row, std::size_t col) { return (transposed) ? M->data[col * Cols + row] : M->data[row * Cols + col]; } const T& at(std::size_t row, std::size_t col) const { return (transposed) ? M->data[col * Cols + row] : M->data[row * Cols + col]; } std::size_t rows() const { return (transposed) ? Cols : Rows; } std::size_t cols() const { return (transposed) ? Rows : Cols; } mm::matrix::vec_iterator operator[](std::size_t index) { return mm::matrix::vec_iterator(*M, index, 0, !transposed); } mm::matrix::const_vec_iterator operator[](std::size_t index) const { return mm::matrix::const_vec_iterator(*M, index, 0, !transposed); } /* * Basic matematical operations (dimension indipendent) */ mm::matrix& operator+=(const mm::matrix& m) { for (unsigned row = 0; row < std::min(rows(), m.rows()); ++row) for (unsigned col = 0; col < std::min(cols(), m.cols()); ++col) at(row, col) += m.at(row, col); return *this; } mm::matrix& operator-=(const mm::matrix& m) { for (unsigned row = 0; row < std::min(rows(), m.rows()); ++row) for (unsigned col = 0; col < std::min(cols(), m.cols()); ++col) at(row, col) -= m.at(row, col); return *this; } mm::matrix operator*=(const T& k) { for (unsigned row = 0; row < rows(); ++row) for (auto& x : (*this)[row]) x *= k; return *this; } protected: std::shared_ptr> M; // shallow construction matrix(std::shared_ptr> grid, bool tr = false) : M(grid), transposed(tr) {} matrix shallow_cpy() const { return matrix(M, transposed); } private: bool transposed; }; /* Basic operator overloading (dimension indipendent) */ template mm::matrix operator+( mm::matrix a, const mm::matrix& b ) { return a += b; } template mm::matrix operator-( mm::matrix a, const mm::matrix& b ) { return a -= b; } template mm::matrix operator*( mm::matrix a, const T& k ) { return a *= k; } template mm::matrix operator*( const T& k, mm::matrix a ) { return a *= k; } // simple multiplication template mm::matrix operator*( const mm::matrix& a, const mm::matrix& b ) { // TODO, adjust asserts for transposed cases static_assert(P1 == P2, "invalid matrix multiplication"); assert(a.cols() == b.rows()); mm::matrix result; mm::matrix bt = b.t(); // weak transposition for (unsigned row = 0; row < M; row++) for (unsigned col = 0; col < N; col++) result.at(row, col) = a[row] * bt[col]; // scalar product return result; } /* * Matrix operator << */ template std::ostream& operator<<(std::ostream& os, const mm::matrix& m) { for (unsigned index = 0; index < m.rows(); index++) { os << "[ "; for (unsigned col = 0; col < m.cols()-1; ++col) { os << std::setw(NumW) << m.at(index, col) << ", "; } os << std::setw(NumW) << m.at(index, m.cols()-1) << " ]\n"; } return os; } /* * Square matrix */ template class mm::square_matrix : public mm::matrix { public: using mm::matrix::matrix; using diag_iterator = mm::iter::diag_iterator>; using const_diag_iterator = mm::iter::diag_iterator::type, N, typename std::add_const>::type>; T trace(); inline T tr() { return trace(); } mm::square_matrix::diag_iterator diag_beg(int row = 0) { return diag_iterator(*(this->M), row, 0); } mm::square_matrix::const_diag_iterator diag_end(int row = 0) const { return const_diag_iterator(*(this->M), row, N); } // TODO, determinant /// in place inverse // TODO, det != 0 // TODO, use gauss jordan for invertible ones //void invert();, TODO, section algorithm /* * Generate the identity */ static inline constexpr mm::square_matrix identity() { mm::square_matrix i; for (unsigned row = 0; row < N; row++) for (unsigned col = 0; col < N; col++) i.at(row, col) = (row == col) ? 1 : 0; return i; } }; template T mm::square_matrix::trace() { T sum = 0; for (const auto& x : diag_beg()) sum += x; return sum; }