/** * \file impl.hpp * \brief Implementation common header file * * \author * Christos Choutouridis AEM:8997 * */ #ifndef IMPL_HPP_ #define IMPL_HPP_ #include #include #include #include #include #include #include #include using std::size_t; /* * Small helper to strip types */ template struct remove_cvref { typedef std::remove_cv_t> type; }; template using remove_cvref_t = typename remove_cvref::type; /*! * Enumerator to denote the storage type of the array to use. */ enum class MatrixType { FULL, /*!< Matrix is asymmetric */ SYMMETRIC, /*!< Matrix is symmetric */ }; /* * Forward type declerations */ template struct Matrix; template struct SpMat; template struct SpMatCol; template struct SpMatRow; template struct SpMatVal; /*! * 2D-array wrapper for v1 and v2 part of the exercise t use as RAII * and copy-prevention. * * This is a very thin abstraction layer over a native array. * This is tested using compiler explorer and our template produce * almost identical assembly. * * The penalty hit we have is due to the fact that we use a one dimension array * and we have to calculate the actual position from an (i,j) pair. * The use of 1D array was our intention from the beginning, so the penalty * was pretty much unavoidable. * * \tparam DataType The underling data type of the array * \tparam Type The storage type of the array * \arg FULL For full matrix * \arg SYMMETRIC For symmetric matrix (we use only the lower part) */ template struct Matrix { using dataType = DataType; //!< meta:export of underling data type using indexType = IndexType; //!< meta:export of underling index type static constexpr MatrixType matrixType = Type; //!< export of array type /*! * \name Obj lifetime */ //! @{ //! Constructor using data type and size Matrix(DataType* t, IndexType s) noexcept : m_(t), size_(s) { } //! RAII deleter ~Matrix() { delete m_; } //! move ctor Matrix(Matrix&& a) noexcept { a.swap(*this); } //! move Matrix& operator=(Matrix&& a) noexcept { a.swap(*this); return *this; } Matrix(const Matrix& a) = delete; //!< No copy ctor Matrix& operator=(const Matrix& a) = delete; //!< No copy //! @} //! \name Data exposure //! @{ //! memory capacity of the matrix with diagonal data template std::enable_if_t static constexpr capacity(IndexType N) noexcept { return (N+1)*N/2; } //! memory capacity for full matrix template std::enable_if_t static constexpr capacity(IndexType N) noexcept { return N*N; } //! Get the size of each dimension IndexType size() noexcept { return size_; } /* * virtual 2D accessors */ template std::enable_if_t get (IndexType i, IndexType j) { if (i std::enable_if_t get(IndexType i, IndexType j) { return m_[i*size_ + j]; } template std::enable_if_t set(DataType v, IndexType i, IndexType j) { if (i std::enable_if_t set(DataType v, IndexType i, IndexType j) { return m_[i*size_ + j] =v; } DataType operator()(IndexType i, IndexType j) { return get(i, j); } // a basic serial iterator support DataType* begin() noexcept { return m_; } DataType* end() noexcept { return m_ + Matrix::capacity(size_); } //! @} /*! * \name Safe iteration API * * This api automates the iteration over the array based on * MatrixType */ //! @{ template void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) { for (IndexType it=begin ; it(lambda)(std::forward(args)..., it); } } //! @} // move helper void swap(Matrix& src) noexcept { std::swap(m_, src.m_); std::swap(size_, src.size_); } private: DataType* m_; //!< Pointer to actual data. IndexType size_; //!< the virtual size of each dimension. }; /*! * RAII allocation helper, the smart_ptr way. */ template Matrix make_Matrix(IndexType s) { return Matrix(new DataType[Matrix::capacity(s)], s); } template using CooVal = std::tuple; template struct SpMat { using dataType = DataType; //!< meta:export of underling data type using indexType = IndexType; //!< meta:export of underling index type static constexpr MatrixType matrixType = Type; //!< export of array type friend class SpMatCol; friend class SpMatRow; friend class SpMatVal; /*! * \name Obj lifetime */ //! @{ //! allocation with init value ctor SpMat(IndexType n=IndexType{}, IndexType nnz=IndexType{}) : values(nnz, DataType{}), rows(nnz, IndexType{}), col_ptr((n)? n+1:2, IndexType{}), N(n), NNZ(nnz) { } SpMat(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) : values(nnz, 1), rows(row, row+nnz), col_ptr(col, col+n+1), N(n), NNZ(nnz) { } SpMat(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) : values(v, v+nnz), rows(row, row+nnz), col_ptr(col, col+n+1), N(n), NNZ(nnz) { } SpMat(IndexType n, IndexType nnz, const DataType v, const std::vector& row, const std::vector& col) : values(nnz, v), rows (row), col_ptr(col), N(n), NNZ(nnz) { } //! move ctor SpMat(SpMat&& a) noexcept { moves(std::move(a)); } //! move SpMat& operator=(SpMat&& a) noexcept { moves(std::move(a)); return *this; } SpMat(const SpMat& a) = delete; //!< No copy ctor SpMat& operator=(const SpMat& a) = delete; //!< No copy assignment //! @} //! \name Data exposure //! @{ IndexType size() noexcept { return N; } IndexType size(IndexType n) { col_ptr.resize(n+1); return N = n; } IndexType capacity() noexcept { return NNZ; } IndexType capacity(IndexType nnz) { values.reserve(nnz); rows.reserve(nnz); return NNZ; } std::vector& getRows() noexcept { return rows; } std::vector& getCols() noexcept { return col_ptr; } SpMatVal operator()(IndexType i, IndexType j) { return SpMatVal(this, get(i, j), i, j); } DataType get(IndexType i, IndexType j) { IndexType idx; bool found; std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i); return (found) ? values[idx] : 0; } DataType set(DataType v, IndexType i, IndexType j) { IndexType idx; bool found; std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i); if (found) return values[idx] = v; // we don't change NNZ even if we write "0" else { values.insert(values.begin()+idx, v); rows.insert(rows.begin()+idx, i); std::transform(col_ptr.begin()+j+1, col_ptr.end(), col_ptr.begin()+j+1, [](IndexType it) { return ++it; }); ++NNZ; // we increase the NNZ even if we write "0" return v; } } SpMatCol getCol(IndexType j) { return SpMatCol(this, col_ptr[j], col_ptr[j+1]); } template std::enable_if_t> getRow(IndexType i) { return getCol(i); } template std::enable_if_t> getRow(IndexType i) { return SpMatRow(this, i); } // iterator support DataType* begin() noexcept { return values.begin(); } DataType* end() noexcept { return values.end(); } //! @} /*! * \name Safe iteration API * * This api automates the iteration over the array based on * MatrixType */ //! @{ template void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) { for (IndexType it=begin ; it(lambda)(std::forward(args)..., it); } } //! @} // operations template friend void print(SpMat& mat); template friend void print_dense(SpMat& mat); private: // index-find helper std::pair find_idx(const std::vector& v, IndexType begin, IndexType end, IndexType match) { for ( ; begin < end ; ++begin) { if (match == v[begin]) return std::make_pair(begin, true); else if (match < v[begin]) return std::make_pair(begin, false); } return std::make_pair(end, false); } // move helper void moves(SpMat&& src) noexcept { values = std::move(src.values); rows = std::move(src.rows); col_ptr = std::move(src.col_ptr); N = std::move(src.N); // redundant for primitives NNZ = std::move(src.NNZ); // } //! \name Data //! @{ std::vector values {}; std::vector rows{}; std::vector col_ptr{1,0}; IndexType N{0}; IndexType NNZ{0}; //! @} }; template struct SpMatCol { using owner_t = SpMat; SpMatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept : owner_(own), index_(begin), begin_(begin), end_(end) { vindex_ = vIndexCalc(index_); } SpMatCol() = default; SpMatCol(const SpMatCol&) = delete; SpMatCol& operator=(const SpMatCol&)= delete; SpMatCol(SpMatCol&&) = default; SpMatCol& operator=(SpMatCol&&) = default; DataType operator* () { return get(); } SpMatCol& operator++ () { advance(); return *this; } SpMatCol& operator++ (int) { SpMatCol& p = *this; advance(); return p; } DataType operator()(IndexType x) { return (x == index())? get() : DataType{}; } DataType operator= (DataType v) { return owner_->values[index_] = v; } IndexType index() noexcept { return vindex_; } const IndexType index() const noexcept { return vindex_; } IndexType begin() noexcept { return vIndexCalc(begin_); } const IndexType begin() const noexcept { return vIndexCalc(begin_); } IndexType end() noexcept { return owner_->N; } const IndexType end() const noexcept { return owner_->N; } template DataType operator* (C&& c) { static_assert(std::is_same, SpMatCol>(), ""); DataType v{}; while (index() != end() && c.index() != c.end()) { if (index() < c.index()) advance(); else if (index() > c.index()) ++c; else { //index() == c.index() v += get() * *c; ++c; advance(); } } return v; } private: void advance() noexcept { ++index_; vindex_ = vIndexCalc(index_); } IndexType vIndexCalc(IndexType idx) { return (idx < end_) ? owner_->rows[idx] : end(); } DataType get() { return owner_->values[index_]; } owner_t* owner_{nullptr}; IndexType vindex_ {IndexType{}}; IndexType index_{IndexType{}}; IndexType begin_{IndexType{}}; IndexType end_{IndexType{}}; }; template struct SpMatRow { using owner_t = SpMat; SpMatRow(owner_t* own, const IndexType row) noexcept : owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}), begin_(IndexType{}), end_(owner_->NNZ) { // place begin while(begin_ != end_ && owner_->rows[begin_] != row_) ++begin_; // place index_ and vindex_ if (owner_->rows[index_] != row_) advance(); } SpMatRow() = default; SpMatRow(const SpMatRow&) = delete; SpMatRow& operator=(const SpMatRow&)= delete; SpMatRow(SpMatRow&&) = default; SpMatRow& operator=(SpMatRow&&) = default; DataType operator* () { return get(); } SpMatRow& operator++ () { advance(); return *this; } SpMatRow& operator++ (int) { SpMatRow& p = *this; advance(); return p; } DataType operator()(IndexType x) { return (x == index())? get() : DataType{}; } DataType operator= (DataType v) { return owner_->values[index_] = v; } IndexType index() noexcept { return vindex_; } const IndexType index() const noexcept { return vindex_; } IndexType begin() noexcept { return vIndexCalc(begin_); } const IndexType begin() const noexcept { return vIndexCalc(begin_); } IndexType end() noexcept { return owner_->N; } const IndexType end() const noexcept { return owner_->N; } template DataType operator* (C&& c) { static_assert(std::is_same, SpMatCol>(), ""); DataType v{}; while (index() != end() && c.index() != c.end()) { if (index() < c.index()) advance(); else if (index() > c.index()) ++c; else { //index() == c.index() v += get()* *c; ++c; advance(); } } return v; } private: void advance() noexcept { do ++index_; while(index_ != end_ && owner_->rows[index_] != row_); vindex_ = vIndexCalc(index_); } IndexType vIndexCalc(IndexType idx) { for(IndexType i =0 ; i<(owner_->N+1) ; ++i) if (idx < owner_->col_ptr[i]) return i-1; return end(); } DataType get() { return owner_->values[index_]; } owner_t* owner_ {nullptr}; IndexType vindex_ {IndexType{}}; IndexType row_ {IndexType{}}; IndexType index_ {IndexType{}}; IndexType begin_ {IndexType{}}; IndexType end_ {IndexType{}}; }; template struct SpMatVal { using owner_t = SpMat; SpMatVal(owner_t* own, DataType v, IndexType i, IndexType j) : owner_(own), v_(v), i_(i), j_(j) { } SpMatVal() = default; SpMatVal(const SpMatVal&) = delete; SpMatVal& operator=(const SpMatVal&) = delete; SpMatVal(SpMatVal&&) = default; SpMatVal& operator=(SpMatVal&&) = default; operator DataType() { return v_; } SpMatVal& operator=(DataType v) { v_ = v; owner_->set(v_, i_, j_); return *this; } private: owner_t* owner_{nullptr};; DataType v_{DataType{}}; IndexType i_{IndexType{}}; IndexType j_{IndexType{}}; }; enum class InputMatrix{ GENERATE, MTX }; /*! * Session option for each invocation of the executable */ struct session_t { std::size_t size {0}; double probability {0}; InputMatrix inputMatrix {InputMatrix::GENERATE}; std::ifstream mtxFile {}; std::size_t print_size {80}; bool timing {false}; bool print {false}; bool makeSymmetric {false}; }; extern session_t session; #endif /* IMPL_HPP_ */