//////////////////////////////////////////////////////////////////////////////// /** \file \brief \ru Двумерный массив объектов. \en Two-dimensional array of objects. \~ */ //////////////////////////////////////////////////////////////////////////////// #ifndef __TEMPL_SPARSE_ARRAY2_H #define __TEMPL_SPARSE_ARRAY2_H #include #include #include #include #include //------------------------------------------------------------------------------ /** \brief \ru Разреженный вектор объектов. \en Sparse vector of objects. \~ \details \ru Разреженный вектор объектов. \n \en Sparse vector of objects. \n \~ \ingroup Base_Tools_Containers */ // --- template class SparseRow { public: typedef std::pair NumberRange; typedef std::pair NumberType; typedef std::vector RowVector; typedef std::map RowMap; typedef typename RowMap::const_iterator RowMapConstIt; typedef typename RowMap::iterator RowMapIt; typedef typename std::pair RowMapRet; protected: RowMap rowMap; ///< \ru Таблица ненулевых элементов. \en Map of nonzero elements. RowVector rowVector; ///< \ru Вектор ненулевых элементов. \en Vector of nonzero elements. size_t rowSize; ///< \ru Размер вектора. \en Vector size. NumberRange nzRange; ///< \ru Диапазон ненулевых элементов [nz1,nz2]. \en Range of nonzero elements [nz1,nz2]. public: static const Type defaultElem; ///< \ru Нулевой элемент (элемент по умолчанию). \en Zero element (default element). public: /// \ru Конструктор. \en Constructor. SparseRow() : rowMap(), rowVector(), rowSize(0), nzRange( SYS_MAX_ST, SYS_MIN_ST ) {} /// \ru Конструктор копирования. \en Copy-constructor. SparseRow( const SparseRow & ); /// \ru Деструктор. \en Destructor. virtual ~SparseRow() {} public: /// \ru Оператор присваивания. \en The assignment operator. const SparseRow & operator = ( const SparseRow & ); public: void Clear() { rowMap.clear(); rowVector.clear(); ResetNzRange(); } void SetSize( size_t rsz ) { Clear(); rowSize = rsz; } bool IsNzRange() const { return (nzRange.first <= nzRange.second); } const NumberRange & NzRange() const { return nzRange; } void ResetNzRange() { nzRange.first = SYS_MAX_ST; nzRange.second = SYS_MIN_ST; } void EnlargeNzRange( ptrdiff_t n ) { nzRange.first = std_min( nzRange.first, n ); nzRange.second = std_max( nzRange.second, n ); } const Type & GetElem( size_t k ) const; bool SetElem( size_t k, const Type & ); bool SetLine( std::vector & ); ptrdiff_t NzBegin() const { return nzRange.first; } ptrdiff_t NzEnd () const { return IsNzRange() ? nzRange.second + 1 : nzRange.first; } bool NzIndices( const NumberRange & searchRange, std::vector & ) const; void NzUpdate(); void Swap( SparseRow & ); protected: bool CheckNzRange() const; bool CheckBand( std::vector & ) const; }; //------------------------------------------------------------------------------ // // --- template const Type SparseRow::defaultElem = Type(); //------------------------------------------------------------------------------ // // --- template SparseRow::SparseRow( const SparseRow & src ) : rowMap ( src.rowMap ) , rowVector( src.rowVector ) , rowSize ( src.rowSize ) , nzRange ( src.nzRange ) { } //------------------------------------------------------------------------------ // // --- template const SparseRow & SparseRow::operator = ( const SparseRow & src ) { rowMap = src.rowMap; rowVector = src.rowVector; rowSize = src.rowSize; nzRange = src.nzRange; return *this; } //------------------------------------------------------------------------------ // // --- template inline bool SparseRow::CheckNzRange() const { bool res = false; if ( 0 <= nzRange.first && nzRange.second <= (ptrdiff_t)rowSize ) { res = true; if ( rowVector.empty() ) { RowMapConstIt it = rowMap.begin(); while ( res && it != rowMap.end() ) { res = false; if ( nzRange.first <= (ptrdiff_t)it->first && (ptrdiff_t)it->first <= nzRange.second ) res = true; ++it; } } else { size_t nzSize = nzRange.second - nzRange.first + 1; if ( nzSize <= rowSize && nzSize == rowVector.size() ) res = true; } } return res; } //------------------------------------------------------------------------------ // // --- template inline const Type & SparseRow::GetElem( size_t k ) const { //C3D_ASSERT( CheckNzRange() ); if ( nzRange.first <= (ptrdiff_t)k && (ptrdiff_t)k <= nzRange.second ) { if ( rowVector.empty() ) { RowMapConstIt it = rowMap.find( k ); if ( it != rowMap.end() ) return it->second; } else { ptrdiff_t index = (ptrdiff_t)k - nzRange.first; PRECONDITION( index >= 0 && index < (ptrdiff_t)rowVector.size() ); if ( index >= 0 && index < (ptrdiff_t)rowVector.size() ) return rowVector[index]; } } return defaultElem; } //------------------------------------------------------------------------------ // // --- template inline bool SparseRow::SetElem( size_t k, const Type & item ) { //C3D_ASSERT( CheckNzRange() ); PRECONDITION( k < rowSize ); if ( k < rowSize ) { if ( rowVector.empty() ) { RowMapRet ret = rowMap.insert( std::make_pair( k, item ) ); if ( !ret.second ) ret.first->second = item; EnlargeNzRange( k ); return true; } else { if ( nzRange.first <= (ptrdiff_t)k && (ptrdiff_t)k <= nzRange.second ) { ptrdiff_t index = k - nzRange.first; PRECONDITION( index >= 0 && index <= (ptrdiff_t)rowVector.size() ); if ( index >= 0 && index < (ptrdiff_t)rowVector.size() ) rowVector[index] = item; } else if ( (ptrdiff_t)k < nzRange.first ) { size_t addCnt = nzRange.first - k; std::reverse( rowVector.begin(), rowVector.end() ); rowVector.resize( rowVector.size() + addCnt ); rowVector.back() = item; std::reverse( rowVector.begin(), rowVector.end() ); EnlargeNzRange( k ); } else if ( (ptrdiff_t)k > nzRange.second ) { size_t addCnt = k - nzRange.second; rowVector.resize( rowVector.size() + addCnt ); rowVector.back() = item; EnlargeNzRange( k ); } return true; } } return false; } //------------------------------------------------------------------------------ // // --- template inline bool SparseRow::CheckBand( std::vector & row ) const { bool res = true; size_t nzSize = row.size(); if ( nzSize > 0 ) { if ( nzSize > 1 ) { std::sort( row.begin(), row.end() ); size_t k; for ( k = nzSize; k--; ) { PRECONDITION( row[k].first < rowSize ); if ( row[k].second == defaultElem ) { row.erase( row.begin() + k ); nzSize--; } else if ( row[k].first >= rowSize ) { row.erase( row.begin() + k ); nzSize--; } } if ( nzSize > 1 ) { size_t nzBegInd = row.front().first; size_t nzEndInd = row.back().first; size_t nzCnt = nzEndInd - nzBegInd + 1; if ( nzCnt > 2*nzSize ) { res = false; } } } } return res; } //------------------------------------------------------------------------------ // // --- template inline bool SparseRow::SetLine( std::vector & row ) { PRECONDITION( row.size() <= rowSize ); if ( row.size() > 0 && row.size() <= rowSize ) { Clear(); if ( !CheckBand( row ) ) { size_t nzSize = row.size(); for ( size_t k = 0; k < nzSize; ++k ) { size_t nzCurInd = row[k].first; rowMap.insert( std::make_pair( nzCurInd, row[k].second ) ); EnlargeNzRange( nzCurInd ); } } else { size_t nzSize = row.size(); size_t nzBegInd = row.front().first; size_t nzEndInd = row.back().first; size_t nzCnt = nzEndInd - nzBegInd + 1; rowVector.resize( nzCnt ); for ( size_t k = 0; k < nzSize; ++k ) { size_t nzCurInd = row[k].first; rowVector[nzCurInd - nzBegInd] = row[k].second; EnlargeNzRange( nzCurInd ); } } return true; } return false; } //------------------------------------------------------------------------------ // // --- template inline bool SparseRow::NzIndices( const NumberRange & searchRange, std::vector & indices ) const { PRECONDITION( indices.empty() && searchRange.first <= searchRange.second ); indices.clear(); if ( searchRange.first <= searchRange.second ) { if ( rowVector.empty() ) { RowMapConstIt it = rowMap.begin(); while ( it != rowMap.end() ) { if ( searchRange.first <= (ptrdiff_t)it->first && (ptrdiff_t)it->first <= searchRange.second ) indices.push_back( it->first ); ++it; } } else { size_t nzCnt = rowVector.size(); indices.reserve( nzCnt ); for ( size_t i = 0; i < nzCnt; ++i ) { size_t index = nzRange.first + i; if ( searchRange.first <= (ptrdiff_t)index && (ptrdiff_t)index <= searchRange.second ) { if ( rowVector[i] != defaultElem ) indices.push_back( index ); } } } if ( indices.size() > 1 ) std::sort( indices.begin(), indices.end() ); } return (indices.size() > 0); } //------------------------------------------------------------------------------ // // --- template inline void SparseRow::NzUpdate() { bool doUpdate = false; std::vector nzElems; if ( rowVector.empty() ) { if ( !rowMap.empty() ) { ResetNzRange(); nzElems.reserve( rowMap.size() ); RowMapIt it = rowMap.begin(); while ( it != rowMap.end() ) { if ( it->second == defaultElem ) { it = c3d::older_stl_support::erase(rowMap, it ); doUpdate = true; } else { EnlargeNzRange( it->first ); nzElems.push_back( std::make_pair( it->first, it->second ) ); ++it; } } } } else { PRECONDITION( rowMap.empty() ); size_t nzCnt = rowVector.size(); nzElems.reserve( nzCnt ); for ( size_t k = 0; k < nzCnt; k++ ) { if ( rowVector[k] != defaultElem ) nzElems.push_back( std::make_pair( nzRange.first + k, rowVector[k] ) ); else doUpdate = true; } } if ( doUpdate ) { SetLine( nzElems ); } PRECONDITION( CheckNzRange() ); } //------------------------------------------------------------------------------ // // --- template inline void SparseRow::Swap( SparseRow & r2 ) { SparseRow & r1 = *this; std::swap( r1.rowMap, r2.rowMap ); std::swap( r1.rowVector, r2.rowVector ); PRECONDITION( r1.rowSize == r2.rowSize ); std::swap( r1.rowSize, r2.rowSize ); std::swap( r1.nzRange, r2.nzRange ); } //------------------------------------------------------------------------------ /** \brief \ru Двумерный разреженный массив объектов. \en Two-dimensional sparse array of objects. \~ \details \ru Двумерный разреженный массив объектов. \n \en Two-dimensional sparse array of objects. \n \~ \ingroup Base_Tools_Containers */ // --- template class SparseArray2 { public: typedef std::pair NumberRange; typedef std::pair NumberType; typedef std::vector< SparseRow > SparseData; typedef SparseArray2 SparseMatrix; protected : SparseData data; ///< \ru Данные. \en Data. size_t nColumns; ///< \ru Количество столбцов массива. \en Count of columns of array. protected: /// \ru Конструктор по заданной размерности. \en The constructor by a given dimension. SparseArray2( size_t lsz, size_t csz ) : data(), nColumns(0) { SetSize( lsz, csz ); } public: /// \ru Конструктор. \en Constructor. SparseArray2() : data(), nColumns(0) {} /// \ru Конструктор ограниченной размерности. \en The constructor of restricted dimension. SparseArray2( const uint16 & lsz, const uint16 & csz ) : data(), nColumns(0) { SetSize( lsz, csz ); } /// \ru Конструктор копирования. \en Copy-constructor. explicit SparseArray2( const SparseArray2 & src ) : data(), nColumns(0) { Init( src ); } /// \ru Деструктор. \en Destructor. virtual ~SparseArray2() { SetSize( 0, 0 ); } public: /// \ru Конструктор по заданной размерности. \en The constructor by a given dimension. static SparseMatrix * Create( size_t lsz, size_t csz ); public: // Общие методы матриц (двумерных массивов) size_t Lines () const { return data.size(); } ///< \ru Количество строк. \en Count of rows. size_t Columns() const { return nColumns; } ///< \ru Количество столбцов. \en Count of columns. size_t Count () const { return (data.size()*nColumns); } ///< \ru Количество элементов. \en Count of elements. c3d::IndicesPair GetSize() const { return c3d::IndicesPair( data.size(), nColumns ); } ///< \ru Дать размер массива. \en Give the size of the array. bool SetSize( c3d::IndicesPair sz ) { return SetSize( sz.first, sz.second ); } ///< \ru Установить размер. \en Set size. bool SetSize( size_t lsz, size_t csz ); ///< \ru Установить размер. \en Set size. bool SetSize( size_t sz ) { return SetSize( sz, sz ); } ///< \ru Установить размер. \en Set size. /// \ru Получить элемент массива. \en Get an element of the array. const Type & GetElem( size_t ln, size_t cn ) const; /// \ru Установить элемент массива. \en Set an element of the array. bool SetElem( size_t ln, size_t cn, const Type & ); /// \ru Оператор доступа по индексам. \en Access by indices operator. const Type & operator () ( size_t ln, size_t cn ) const { C3D_ASSERT( ln < data.size() && cn < nColumns ); return GetElem( ln, cn ); } /// \ru Расписать массив нулями. \en Assign zeros to array. SparseMatrix & SetZero(); /// \ru Функция присваивания. \en An assignment function. bool Init( const SparseMatrix & ); /// \ru Оператор присваивания. \en The assignment operator. SparseMatrix & operator = ( const SparseMatrix & src ) { Init( src ); return *this; } /// \ru Поменять местами строки. \en Swap lines. bool SwapLines( size_t ln1, size_t ln2 ); public: /// \ru Установить ненулевые элементы строки. \en Set nonzero elements of the row. bool SetLine( size_t ln, std::vector & ); /// \ru Индекс первого ненулевого элемента. \en Index of the first nonzero element. size_t NzBegin( size_t ln ) const { C3D_ASSERT(ln < data.size()); if ( ln < data.size() ) return data[ln].NzBegin(); return SYS_MAX_ST; } /// \ru Завершающий индекс последовательности ненулевых элементов. \en The final index of the sequence of non-zero elements. size_t NzEnd ( size_t ln ) const { C3D_ASSERT(ln < data.size()); if ( ln < data.size() ) return data[ln].NzEnd(); return SYS_MIN_ST; } /// \ru Получить индексы ненулевых элементов строки. \en Get indices of nonzero elements of the row. bool NzIndices( size_t ln, const NumberRange & searchRange, std::vector & ) const; /// \ru Обновить строку (удалить нулевые элементы по возможности). \en Update the row (delete zero elements as far as possible). void NzUpdate(); }; //------------------------------------------------------------------------------ // // --- template inline bool SparseArray2::SetSize( size_t lsz, size_t csz ) { if ( lsz*csz <= c3d::MATRIX_MAX_COUNT ) { data.clear(); data.resize( lsz ); for ( size_t k = data.size(); k--; ) data[k].SetSize( csz ); nColumns = csz; return true; } return false; } //------------------------------------------------------------------------------ // // --- template inline SparseArray2 * SparseArray2::Create( size_t lsz, size_t csz ) { if ( lsz*csz <= c3d::MATRIX_MAX_COUNT ) return new SparseMatrix( lsz, csz ); return nullptr; } //------------------------------------------------------------------------------ // // --- template bool SparseArray2::Init( const SparseArray2 & src ) { data.assign( src.data.begin(), src.data.end() ); nColumns = src.nColumns; return true; } //------------------------------------------------------------------------------ // // --- template inline const Type & SparseArray2::GetElem( size_t ln, size_t cn ) const { PRECONDITION( ln < data.size() && cn < nColumns ); if ( ln < data.size() && cn < nColumns ) return data[ln].GetElem( cn ); return SparseRow::defaultElem; } //------------------------------------------------------------------------------ // // --- template inline bool SparseArray2::SetElem( size_t ln, size_t cn, const Type & item ) { PRECONDITION( ln < data.size() && cn < nColumns ); if ( ln < data.size() && cn < nColumns ) return data[ln].SetElem( cn, item ); return false; } //------------------------------------------------------------------------------ // // --- template inline SparseArray2 & SparseArray2::SetZero() { std::for_each( data.begin(), data.end(), std::mem_fn( &SparseRow::Clear ) ); return *this; } //------------------------------------------------------------------------------ // // --- template inline bool SparseArray2::SetLine( size_t ln, std::vector & newRow ) { PRECONDITION( ln < data.size() && newRow.size() <= nColumns ); if ( ln < data.size() && newRow.size() <= nColumns ) return data[ln].SetLine( newRow ); return false; } //------------------------------------------------------------------------------ // // --- template inline bool SparseArray2::SwapLines( size_t ln1, size_t ln2 ) { size_t nLines = data.size(); PRECONDITION( ln1 < nLines && ln2 < nLines ); if ( ln1 < nLines && ln2 < nLines ) { data[ln1].Swap( data[ln2] ); return true; } return false; } //------------------------------------------------------------------------------ // // --- template inline bool SparseArray2::NzIndices( size_t ln, const NumberRange & searchRange, std::vector & indices ) const { PRECONDITION( ln < data.size() ); if ( ln < data.size() ) return data[ln].NzIndices( searchRange, indices ); return false; } //------------------------------------------------------------------------------ // // --- template inline void SparseArray2::NzUpdate() { std::for_each( data.begin(), data.end(), std::mem_fn( &SparseRow::NzUpdate ) ); } #endif // __TEMPL_SPARSE_ARRAY2_H