// Matrix_List.h Copyright (C) 2009 adolfo@di-mare.com #ifdef Spanish_dox /** \file Matrix_List.h \brief Declaraciones y definiciones para la clase \c Matrix_List<>. \author Adolfo Di Mare \date 2004 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 */ #endif #ifdef English_dox /** \file Matrix_List.h \brief Declarations and definitiones for class \c Matrix_List<>. \author Adolfo Di Mare \date 2004 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 */ #endif #include "Matrix_BASE.h" #include #include namespace Mx { template class Matrix_List_ColVal { template friend class Matrix_List; private: unsigned m_col; E m_val; private: /// Constructor. Matrix_List_ColVal( unsigned col , const E& val ) : m_col( col ) , m_val( val ) { } public: ~Matrix_List_ColVal() { } //< Destructor. }; #ifdef Spanish_dox /** \class Matrix_List_ColVal \brief Clase privada para implementar la lista de valores de cada fila. \var Matrix_List_ColVal::m_col; \brief Columna del valor almacenado en la matriz. \var Matrix_List_ColVal::m_val; \brief Valor almacenado en la matriz. */ #endif #ifdef English_dox /** \class Matrix_List_ColVal \brief Private class used to implement the list of values for each row. \var Matrix_List_ColVal::m_col \brief Column o the value stored in the matrix. \var Matrix_List_ColVal::m_val \brief Value stored in the matrix. */ #endif #ifdef Spanish_dox /// Matriz muy chirrisquitica almacenada como matriz rala implementada con listas. #endif #ifdef English_dox /// Chirrisquitica matrix stored as a sparse matrix implemented with lists. #endif /// \copydetails Matrix_BASE template class Matrix_List : public Matrix_BASE { public: Matrix_List(unsigned m = 1, unsigned n = 1); Matrix_List(const Matrix_List& o); /// \copydoc Matrix_BASE::Matrix_BASE(const E&) Matrix_List(const E& V) : Matrix_BASE() , m_same() { reSize(1,1); (*this)(0,0) = V; } ~Matrix_List(); ///< Destructor. public: unsigned rows() const { return static_cast(m_VL.size()); } unsigned cols() const { return m_cols; } unsigned size() const { return rows() * m_cols; } unsigned count() const { return size(); } unsigned capacity() const { return size(); } public: Matrix_List& operator= (const Matrix_List &o) { return copy(o); } Matrix_List& copy( const Matrix_List &o ); Matrix_List& move( Matrix_List &o ); Matrix_List& swap( Matrix_List &o ); void clear(); public: bool equals( const Matrix_List & o ) const; public: E& operator()(unsigned, unsigned); const E& operator()(unsigned, unsigned) const; E& at(unsigned i, unsigned j) { return at_Matrix(*this,i,j); } const E& at(unsigned i, unsigned j) const { return at_Matrix(*this,i,j); } void reShape(unsigned m, unsigned n); void reSize( unsigned m, unsigned n); void transpose(); void setDefault(const E& same); const E& getDefault(); public: template friend bool check_ok( const Matrix_List& M ); template friend class test_Matrix; ///< BUnit test. private: std::vector< std::list < Matrix_List_ColVal< E > > > m_VL; // filas unsigned m_cols; // # cols E m_same; // valor por defecto private: void reserve(unsigned n); void reSize(unsigned newsize); }; // Matrix_List #ifdef Spanish_dox /** \var Matrix_List::m_VL; \brief Vector en donde están almacenados los valores de la lista de columnas. \var Matrix_List::m_cols; \brief Cantidad de columnas de la matriz. \var Matrix_List::m_same; \brief Valor almacenado en la mayor parte de la \c Matrix_List. */ #endif #ifdef English_dox /** \var Matrix_List::m_VL; \brief Vector where the column list values are stored. \var Matrix_List::m_cols; \brief Number of columns in the matrix. \var Matrix_List::m_same; \brief Most common value stored in the \c Matrix_List. */ #endif #ifdef Spanish_dox /** \copydoc check_ok_Matrix(const MAT&) \brief Verifica la invariante de la clase. - El campo \c m_same indica cuál es el valor que se repite más en toda la matriz. - Usualmente \c m_same es el neutro aditivo \c value_type(). - No existe un constructor explícito para darle a \c m_same su valor inicial, que es siempre inicializado en \c value_type(). Para cambiarlo es necesario invocar el método \c setgetDefault(). - El vector \c m_VL[] contiene las listas de valores almacenados en la matriz. Cualquiera de estas listas puede estar vacía; aún todas pueden ser listas vacías. - La cantidad de columnas de la matriz es el valor fijo es \c m_cols. - El largo de las listas de valores cambia cuando se hace referencia a un valor M(i,j) mediante la versión que no es \c const del \c operator()(i,j). O sea, que es ese método el encargado de agregar valores en \c m_VL[], pues el operador homónimo \c const operator()(i,j) nunca agrega nada y, como es \c const, en general retorna una referencia constante a \c m_same. - Es posible que la matriz tenga dimensiones nulas, lo que implica que el vector \c m_VL[] está vacío. - No hace falta alacenar la cantidad de valores de la matriz porque se siempre es \c m_VL.size(). el valor \c 0 (cero) al campo \c m_capacity. \par Rep Modelo de la clase \code m_VL> _______ +---+ / \ 0 | *-|---| 1 , 'a' | +---+ \_______/ 0 1 2 3 4 5 6 1 | ? | M(0,1)=='a' 0 / - a - - - - - \ +---+ 1 | - - - - - - - | 2 | ? | _______ _______ 2 | - - - - - - - | +---+ / \ / \ 3 | - c - - - b - | 3 | *-|---| 5 , 'b' |---| 1 , 'c' | 4 \ - - - - - - - / +---+ \_______/ \_______/ 4 | ? | M(3,5)=='b' M(3,1)=='c' +---+ m_same == '-' rows() == m_VL.size() m_cols == 7 \endcode - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep \remark Libera al programador de implementar el método \c Ok() - http://www.di-mare.com/adolfo/binder/c04.htm#sc11 */ #endif #ifdef English_dox #endif template bool check_ok( const Matrix_List& M ) { if ( ! check_ok_Matrix( M ) ) { return false; } if ( M.m_cols == 0 ) { // m_cols es el "marcador" que determina el valor de los demás if ( ! M.m_VL.empty() ) { return false; /// - check_ok(): (m_cols == 0) <==> (m_VL.empty()) } } if ( ! check_ok( M.m_same ) ) { return false; /// - check_ok(): check_ok( M.m_same ) } return true; } #ifdef Spanish_dox /** \fn Matrix_List::reserve(unsigned); \brief Ajusta la matriz para que pueda almacenar al menos \n valores diferentes a \c getDefault(). */ #endif #ifdef English_dox /** \fn Matrix_List::reserve(unsigned); \brief Adjust the matrix so that it can store at least \n values different than \c getDefault(). */ #endif /// \copydoc Matrix_BASE::Matrix_BASE(unsigned,unsigned) template Matrix_List::Matrix_List(unsigned m, unsigned n) : m_VL(), m_cols(n), m_same() { if (m == 0 || n == 0) { m_cols = 0; } else { reSize(m,n); } // m_same = E(); // Usa el número "cero" como neutro tipo "E" } /// \copydoc Matrix_BASE::Matrix_BASE(const Matrix_BASE&) template inline Matrix_List::Matrix_List(const Matrix_List& o) { copy( o ); return; } template inline Matrix_List::~Matrix_List() { } /// \copydoc Matrix_BASE::equals() template inline bool Matrix_List::equals( const Matrix_List & o ) const { return equals_Matrix( *this, o ); } /// \copydoc Matrix_BASE::copy() template Matrix_List& Matrix_List::copy( const Matrix_List &o ) { if (this == &o) { // evita auto-borrado return *this; } this->m_VL = o.m_VL; this->m_cols = o.m_cols; this->m_same = o.m_same; return *this; } /// \copydoc Matrix_BASE::move() template Matrix_List& Matrix_List::move( Matrix_List &o ) { if (this == &o) { // evita auto-borrado return *this; } if ( m_VL.size() != o.m_VL.size() ) { m_VL.clear(); m_VL.resize( o.m_VL.size() ); } assert( m_VL.size() == o.m_VL.size() ); for ( int i=0; im_VL[i].clear(); this->m_VL[i].splice( m_VL[i].begin(), o.m_VL[i] ); } this->m_cols = o.m_cols; this->m_same = o.m_same; return *this; } /// \copydoc Matrix_BASE::swap() template Matrix_List& Matrix_List::swap( Matrix_List &o ) { std::swap( this->m_VL , o.m_VL ); std::swap( this->m_cols , o.m_cols ); std::swap( this->m_same , o.m_same ); return *this; } /// \copydoc Matrix_BASE::clear() template void Matrix_List::clear() { m_VL.clear(); m_VL.push_back( std::list < Matrix_List_ColVal< E > >() ); m_cols = 1; m_same = E(); // typename Matrix_BASE::value_type(); } /// \copydoc Matrix_BASE::reSize() template void Matrix_List::reSize(unsigned m, unsigned n) { unsigned rows = m_VL.size(); if ( m > rows ) { std::vector< std::list < Matrix_List_ColVal< E > > > TMP(m); // TMP.resize(m); for ( unsigned i=0 ; im_VL[i] ); } this->m_VL.resize(m); // reinstala las filas anteriores for ( unsigned i=0 ; im_VL[i].splice( this->m_VL[i].begin() , TMP[i] ); } } else if ( m < rows ) { // desecha las filas restantes this->m_VL.resize(m); } assert( m_VL.size() == m ); if ( n < m_cols ) { // desecha valores en columnas mayores a "n" rows = m_VL.size(); for ( unsigned i=0 ; i >::iterator ITR; ITR it = this->m_VL[i].begin(); while ( it != this->m_VL[i].end() ) { if ( it->m_col >= n ) { it = this->m_VL[i].erase( it ); } else { ++it; } } } } m_cols = n; return; } /// \copydoc Matrix_BASE::reShape() template void Matrix_List::reShape(unsigned m, unsigned n) { if ( m * n == m_VL.size() * m_cols ) { reSize(n,m); } } /// \copydoc Matrix_BASE::operator()(unsigned,unsigned) const template inline const E& Matrix_List::operator()(unsigned i, unsigned j) const { assert( "Matrix_List::operator()()" && (i < rows()) ); assert( "Matrix_List::operator()()" && (j < cols()) ); typedef typename std::list < Matrix_List_ColVal< E > >::const_iterator ITR; ITR it = this->m_VL[i].begin(); while ( it != this->m_VL[i].end() ) { if ( it->m_col == j ) { break; } ++it; } if ( it == this->m_VL[i].end() ) { return m_same; } else { return it->m_val; } /* NOTA Como este método es "const", de antemano se sabe que el programador no puede usarlo para modificar el valor de la matriz. Por eso, aunque el valor retornado sea una referencia a valor común por defecto m_same, de antemano el compilador asegura que ese valor no será modificado. Sin embargo, cuando el programador usa el método homónimo \c operator()(i,j) que no es \c "const", es posible que el valor retornado sí sea modificado. En ese caso, ya no es correcto retornar una referencia al valor común \c "m_same". - Por eso, cuando se usa el hace referencia en el otro \c operator()(i,j) es necesario agregar una entrada en los vectores paralelos en aquellos casos en que no existe un valor diferente a \c "m_same" para \c (i,j). - Esto quiere decir que sí es posible que al utilizar la versión modificable de \c operator()(i,j) quede en el vector "m_VL[]" un valor que es igual a "m_same". En el caso peor, podría ocurrir que todos los valores almacenados en el vector \c "m_VL[]" sean todos iguales a \c "m_same". - Una forma de corregir esta anomalía es "revisar después" si existe un valor en el vector \c "m_VL[]" que es igual a "m_same". Una forma eficiente de hacerlo es mantener el el Rep un puntero "m_last_change" que apunte al último valor \c "m_VL[]" que la versión modificable de \c operator()(i,j) retornó. En la siguiente invocación a \c operator()(i,j), se puede verificar si hubo un ese valor anterior fue cambiado de manera que tiene almacenado \c "m_same". */ } /// \copydoc Matrix_BASE::operator()(unsigned,unsigned) template inline E& Matrix_List::operator()(unsigned i, unsigned j) { assert( "Matrix_List::operator()()" && (i < rows()) ); assert( "Matrix_List::operator()()" && (j < cols()) ); // Busca al elemento M(i,j) typedef typename std::list < Matrix_List_ColVal< E > >::iterator ITR; ITR it = this->m_VL[i].begin(); while ( it != this->m_VL[i].end() ) { if ( it->m_col == j ) { break; } ++it; } if ( it == this->m_VL[i].end() ) { // lo agrega porque no estaba this->m_VL[i].push_front( Matrix_List_ColVal< E >(j,m_same) ); it = this->m_VL[i].begin(); } return it->m_val; } /// \copydoc Matrix_BASE::getDefault() template inline const E& Matrix_List::getDefault() { return m_same; } /// \copydoc Matrix_BASE::setDefault() template inline void Matrix_List::setDefault(const E& same) { if ( m_same != same ) { clear(); m_same = same; } } } // namespace Mx #ifndef Matrix_List_h #define Matrix_List_h // Evita la inclusión múltiple #ifdef Spanish_dox /// \c "Doxygen: Documentación en español" #define Spanish_dox "Doxygen: Documentación en español" /// \def Matrix_List_h /// \brief Evita la inclusión múltiple. #endif #ifdef English_dox /// "Doxygen: English documentation" #define English_dox "Doxygen: English documentation" /// \def Matrix_List_h /// \brief Avoids multiple inclusion. #endif #endif // Matrix_List_h // EOF: Matrix_List.h